* Try to acquire a distributed lock (atomic SET NX operation) * @param key - Lock key * @param value - Lock value (typically "locked" or instance identifier) * @param ttlMs - Time to live in milliseconds (lock expiration) * @returns Result containing boolean indicating if lock was acquir
(key: CacheKey, value: string, ttlMs: number)
| 212 | * @returns Result containing boolean indicating if lock was acquired, or an error |
| 213 | */ |
| 214 | async tryLock(key: CacheKey, value: string, ttlMs: number): Promise<Result<boolean, CacheError>> { |
| 215 | // Check Redis availability first |
| 216 | if (!this.isRedisClientReady()) { |
| 217 | return err({ |
| 218 | code: ErrorCode.RedisConnectionError, |
| 219 | }); |
| 220 | } |
| 221 | |
| 222 | const validation = validateInputs([key, ZCacheKey], [ttlMs, ZTtlMs]); |
| 223 | if (!validation.ok) { |
| 224 | return validation; |
| 225 | } |
| 226 | |
| 227 | try { |
| 228 | // Use SET with NX (only set if not exists) and PX (expiration in milliseconds) for atomic lock acquisition |
| 229 | const result = await this.withTimeout( |
| 230 | this.redis.set(key, value, { |
| 231 | NX: true, |
| 232 | PX: ttlMs, |
| 233 | }) |
| 234 | ); |
| 235 | // SET returns "OK" if lock was acquired, null if key already exists |
| 236 | return ok(result === "OK"); |
| 237 | } catch (error) { |
| 238 | logger.error({ error, key, ttlMs }, "Cache lock operation failed"); |
| 239 | return err({ |
| 240 | code: ErrorCode.RedisOperationError, |
| 241 | }); |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | /** |
| 246 | * Cache wrapper for functions (cache-aside). |
no test coverage detected