cachette 6.0.3 → 6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { CachableValue, CacheInstance } from './CacheInstance';
1
+ import { CachableValue, CacheInstance, Lock } from './CacheInstance';
2
2
  /**
3
3
  * Write-through cache, using Redis and a local LRU cache with aligned TTLs.
4
4
  *
@@ -71,9 +71,17 @@ export declare class WriteThroughCache extends CacheInstance {
71
71
  clearMemory(): Promise<void>;
72
72
  /**
73
73
  * @inheritdoc
74
- * Locking is *not* supported by the Write-Through cache. You want either:
75
- * - The full-fledged RedisCache for prod workloads
76
- * - A dumb LocalCache for dev/local workloads
74
+ * Locking delegates to the Redis primary: it is the only shared backing store,
75
+ * so it is the only one that can provide cross-instance mutual exclusion. The
76
+ * per-process localCache could not. Lock keys therefore live only in Redis.
77
77
  */
78
78
  isLockingSupported(): boolean;
79
+ /**
80
+ * @inheritdoc
81
+ */
82
+ lock(resource: string, ttlMs: number, retry?: boolean): Promise<Lock>;
83
+ /**
84
+ * @inheritdoc
85
+ */
86
+ hasLock(prefix: string): Promise<boolean>;
79
87
  }
@@ -159,12 +159,24 @@ class WriteThroughCache extends CacheInstance_1.CacheInstance {
159
159
  }
160
160
  /**
161
161
  * @inheritdoc
162
- * Locking is *not* supported by the Write-Through cache. You want either:
163
- * - The full-fledged RedisCache for prod workloads
164
- * - A dumb LocalCache for dev/local workloads
162
+ * Locking delegates to the Redis primary: it is the only shared backing store,
163
+ * so it is the only one that can provide cross-instance mutual exclusion. The
164
+ * per-process localCache could not. Lock keys therefore live only in Redis.
165
165
  */
166
166
  isLockingSupported() {
167
- return false;
167
+ return true;
168
+ }
169
+ /**
170
+ * @inheritdoc
171
+ */
172
+ lock(resource, ttlMs, retry) {
173
+ return this.redisCacheForWriting.lock(resource, ttlMs, retry);
174
+ }
175
+ /**
176
+ * @inheritdoc
177
+ */
178
+ hasLock(prefix) {
179
+ return this.redisCacheForWriting.hasLock(prefix);
168
180
  }
169
181
  }
170
182
  exports.WriteThroughCache = WriteThroughCache;
@@ -1 +1 @@
1
- {"version":3,"file":"WriteThroughCache.js","sourceRoot":"","sources":["../../../src/lib/WriteThroughCache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WriteThroughCache.js","sourceRoot":"","sources":["../../../src/lib/WriteThroughCache.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cachette",
3
- "version": "6.0.3",
3
+ "version": "6.1.1",
4
4
  "engines": {
5
5
  "node": ">=20",
6
6
  "npm": ">=10"
@@ -1,4 +1,4 @@
1
- import { CachableValue, CacheInstance } from './CacheInstance';
1
+ import { CachableValue, CacheInstance, Lock } from './CacheInstance';
2
2
  import { RedisCache } from './RedisCache';
3
3
  import { LocalCache } from './LocalCache';
4
4
 
@@ -190,11 +190,25 @@ export class WriteThroughCache extends CacheInstance {
190
190
 
191
191
  /**
192
192
  * @inheritdoc
193
- * Locking is *not* supported by the Write-Through cache. You want either:
194
- * - The full-fledged RedisCache for prod workloads
195
- * - A dumb LocalCache for dev/local workloads
193
+ * Locking delegates to the Redis primary: it is the only shared backing store,
194
+ * so it is the only one that can provide cross-instance mutual exclusion. The
195
+ * per-process localCache could not. Lock keys therefore live only in Redis.
196
196
  */
197
197
  public isLockingSupported(): boolean {
198
- return false;
198
+ return true;
199
+ }
200
+
201
+ /**
202
+ * @inheritdoc
203
+ */
204
+ public lock(resource: string, ttlMs: number, retry?: boolean): Promise<Lock> {
205
+ return this.redisCacheForWriting.lock(resource, ttlMs, retry);
206
+ }
207
+
208
+ /**
209
+ * @inheritdoc
210
+ */
211
+ public hasLock(prefix: string): Promise<boolean> {
212
+ return this.redisCacheForWriting.hasLock(prefix);
199
213
  }
200
214
  }
@@ -155,6 +155,99 @@ describe('WriteThroughCache', () => {
155
155
  });
156
156
  });
157
157
 
158
+ describe('locking', () => {
159
+ it('reports locking as supported', () => {
160
+ const cache = makeFakeWriteThroughCache();
161
+ expect(cache.isLockingSupported()).to.be.true;
162
+ });
163
+
164
+ it('delegates lock() to the writing Redis instance', async () => {
165
+ const cache = makeFakeWriteThroughCache();
166
+ const spy = sinon.spy(cache['redisCacheForWriting'], 'lock');
167
+
168
+ const lock = await cache.lock('test-resource', 10000, false);
169
+
170
+ sinon.assert.calledOnceWithExactly(spy, 'test-resource', 10000, false);
171
+ expect(lock).to.have.property('value');
172
+ expect(lock.unlock).to.be.a('function');
173
+
174
+ await cache.unlock(lock);
175
+ });
176
+
177
+ it('delegates hasLock() to the writing Redis instance', async () => {
178
+ const cache = makeFakeWriteThroughCache();
179
+ const spy = sinon.spy(cache['redisCacheForWriting'], 'hasLock');
180
+
181
+ const resource = 'test-resource-haslock';
182
+ const lock = await cache.lock(resource, 10000, false);
183
+ expect(await cache.hasLock(resource)).to.be.true;
184
+ sinon.assert.calledWith(spy, resource);
185
+
186
+ await cache.unlock(lock);
187
+ expect(await cache.hasLock(resource)).to.be.false;
188
+ });
189
+
190
+ it('does not write lock keys to the local cache', async () => {
191
+ const cache = makeFakeWriteThroughCache();
192
+ const resource = 'test-resource-local';
193
+
194
+ const lock = await cache.lock(resource, 10000);
195
+ expect(await cache['localCache'].getValue(resource)).to.be.undefined;
196
+
197
+ await cache.unlock(lock);
198
+ });
199
+
200
+ it('acquires, blocks a contender, releases, and re-acquires against real Redis', async function (): Promise<void> {
201
+ if (!process.env.TEST_REDIS_URL) {
202
+ this.skip();
203
+ }
204
+ const cache = new WriteThroughCache(process.env.TEST_REDIS_URL as string);
205
+ await cache.isReady();
206
+ const resource = `wtc-lock-${Math.random()}`;
207
+
208
+ const lock = await cache.lock(resource, 10000);
209
+ expect(await cache.hasLock(resource)).to.be.true;
210
+
211
+ // A non-retrying contender must fail while the lock is held.
212
+ let contended = false;
213
+ try {
214
+ await cache.lock(resource, 10000, false);
215
+ } catch {
216
+ contended = true;
217
+ }
218
+ expect(contended).to.be.true;
219
+
220
+ await cache.unlock(lock);
221
+ expect(await cache.hasLock(resource)).to.be.false;
222
+
223
+ // Re-acquire succeeds once released.
224
+ const lock2 = await cache.lock(resource, 10000, false);
225
+ expect(lock2).to.have.property('value');
226
+ await cache.unlock(lock2);
227
+
228
+ await cache.quit();
229
+ });
230
+
231
+ it('honors lockTtl in getOrFetchValue against real Redis', async function (): Promise<void> {
232
+ if (!process.env.TEST_REDIS_URL) {
233
+ this.skip();
234
+ }
235
+ const cache = new WriteThroughCache(process.env.TEST_REDIS_URL as string);
236
+ await cache.isReady();
237
+ const lockSpy = sinon.spy(cache['redisCacheForWriting'], 'lock');
238
+ const key = `wtc-fetch-${Math.random()}`;
239
+
240
+ const value = await cache.getOrFetchValue(key, 60, async () => 'fetched', 5);
241
+
242
+ expect(value).to.equal('fetched');
243
+ sinon.assert.calledOnce(lockSpy);
244
+ sinon.assert.calledWith(lockSpy, `lock__${key}`, 5000);
245
+
246
+ await cache.delValue(key);
247
+ await cache.quit();
248
+ });
249
+ });
250
+
158
251
  describe('metrics', () => {
159
252
  let sinonClock: sinon.SinonFakeTimers;
160
253