fix(caching): properly respect ttl for all cache levels
This commit is contained in:
parent
f7b6df5ff7
commit
6b57e8b1f3
18
test/test.ts
18
test/test.ts
@ -21,7 +21,23 @@ tap.test('should cache a value', async () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
const result = await testLevelCache.retrieveCacheEntryByKey('mykey');
|
const result = await testLevelCache.retrieveCacheEntryByKey('mykey');
|
||||||
console.log(result);
|
expect(result.contents.toString()).to.equal('heythere');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should respect ttl', async (tools) => {
|
||||||
|
await testLevelCache.storeCacheEntryByKey(
|
||||||
|
'mykey',
|
||||||
|
new CacheEntry({
|
||||||
|
contents: Buffer.from('heythere'),
|
||||||
|
ttl: 1000,
|
||||||
|
typeInfo: 'string',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const result = await testLevelCache.retrieveCacheEntryByKey('mykey');
|
||||||
|
expect(result.contents.toString()).to.equal('heythere');
|
||||||
|
await tools.delayFor(1100);
|
||||||
|
const result2 = await testLevelCache.retrieveCacheEntryByKey('mykey');
|
||||||
|
expect(result2).to.be.null;
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.start();
|
tap.start();
|
||||||
|
@ -4,22 +4,35 @@ export abstract class AbstractCache {
|
|||||||
public abstract ready: Promise<void>;
|
public abstract ready: Promise<void>;
|
||||||
public abstract status: 'active' | 'inactive';
|
public abstract status: 'active' | 'inactive';
|
||||||
|
|
||||||
// Blobs
|
// Cache Entries
|
||||||
/**
|
/**
|
||||||
* store a Blob
|
* store a Blob
|
||||||
*/
|
*/
|
||||||
public abstract storeCacheEntryByKey(keyArg: string, valueArg: CacheEntry): Promise<void>;
|
public abstract storeCacheEntryByKey(keyArg: string, valueArg: CacheEntry): Promise<void>;
|
||||||
|
|
||||||
// Cache Entries
|
|
||||||
/**
|
/**
|
||||||
* retrieve cache entry
|
* retrieve cache entry
|
||||||
*/
|
*/
|
||||||
public abstract retrieveCacheEntryByKey(keyArg: string): Promise<CacheEntry>;
|
public abstract retrieveCacheEntryByKey(keyArg: string): Promise<CacheEntry>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks for the presence of a key
|
||||||
|
* @param keyArg
|
||||||
|
*/
|
||||||
public abstract checkKeyPresence(keyArg: string): Promise<boolean>;
|
public abstract checkKeyPresence(keyArg: string): Promise<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cleans the cache
|
* delete a key
|
||||||
*/
|
*/
|
||||||
public abstract clean(): Promise<void>;
|
public abstract deleteCacheEntryByKey(keyArg: string): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clean the cache
|
||||||
|
*/
|
||||||
|
public abstract cleanOutdated(): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cleans the complete cache
|
||||||
|
*/
|
||||||
|
public abstract cleanAll(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
@ -51,14 +51,18 @@ export class CacheDiskManager extends AbstractCache {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkKeyPresence(keyArg): Promise<boolean> {
|
public async checkKeyPresence(keyArg: string): Promise<boolean> {
|
||||||
return plugins.smartfile.fs.isFile(plugins.path.join(this.fsPath, encodeURIComponent(keyArg)));
|
return plugins.smartfile.fs.isFile(plugins.path.join(this.fsPath, encodeURIComponent(keyArg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public async deleteCacheEntryByKey(keyArg: string) {
|
||||||
* cleans the DiskCache directory
|
await plugins.smartfile.fs.remove(plugins.path.join(this.fsPath, encodeURIComponent(keyArg)));
|
||||||
*/
|
}
|
||||||
public async clean() {
|
|
||||||
|
|
||||||
|
public async cleanOutdated() {}
|
||||||
|
|
||||||
|
public async cleanAll() {
|
||||||
if (this.status === 'active') {
|
if (this.status === 'active') {
|
||||||
if (plugins.smartfile.fs.isDirectory(this.fsPath)) {
|
if (plugins.smartfile.fs.isDirectory(this.fsPath)) {
|
||||||
await plugins.smartfile.fs.ensureEmptyDir(this.fsPath);
|
await plugins.smartfile.fs.ensureEmptyDir(this.fsPath);
|
||||||
|
@ -38,7 +38,13 @@ export class CacheMemoryManager extends AbstractCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async clean() {
|
public async deleteCacheEntryByKey(keyArg: string) {
|
||||||
|
this.fastMap.removeFromMap(keyArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async cleanOutdated() {}
|
||||||
|
|
||||||
|
public async cleanAll() {
|
||||||
this.fastMap.clean();
|
this.fastMap.clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,18 @@ export class CacheS3Manager extends AbstractCache {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async clean() {
|
public async deleteCacheEntryByKey(keyArg: string) {
|
||||||
|
if(this.status === 'active') {
|
||||||
|
await this.s3CacheDir.fastRemove(encodeURIComponent(keyArg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clean outdated
|
||||||
|
*/
|
||||||
|
public async cleanOutdated() {}
|
||||||
|
|
||||||
|
public async cleanAll() {
|
||||||
await this.s3CacheDir.deleteWithAllContents();
|
await this.s3CacheDir.deleteWithAllContents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ export class CacheEntry
|
|||||||
@plugins.smartjson.foldDec()
|
@plugins.smartjson.foldDec()
|
||||||
contents: Buffer;
|
contents: Buffer;
|
||||||
|
|
||||||
|
@plugins.smartjson.foldDec()
|
||||||
|
public createdAt: number;
|
||||||
|
|
||||||
public toStorageJsonString(): string {
|
public toStorageJsonString(): string {
|
||||||
return this.foldToJson();
|
return this.foldToJson();
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ export class LevelCache extends AbstractCache {
|
|||||||
public async storeCacheEntryByKey(keyArg: string, cacheEntryArg: CacheEntry): Promise<void> {
|
public async storeCacheEntryByKey(keyArg: string, cacheEntryArg: CacheEntry): Promise<void> {
|
||||||
cacheEntryArg.key = keyArg;
|
cacheEntryArg.key = keyArg;
|
||||||
const targetCache = await this.cacheRouter.getCacheForStoreAction(keyArg, cacheEntryArg);
|
const targetCache = await this.cacheRouter.getCacheForStoreAction(keyArg, cacheEntryArg);
|
||||||
|
cacheEntryArg.createdAt = Date.now();
|
||||||
await targetCache.storeCacheEntryByKey(keyArg, cacheEntryArg);
|
await targetCache.storeCacheEntryByKey(keyArg, cacheEntryArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +74,10 @@ export class LevelCache extends AbstractCache {
|
|||||||
const targetCache = await this.cacheRouter.getCacheForRetrieveAction(keyArg);
|
const targetCache = await this.cacheRouter.getCacheForRetrieveAction(keyArg);
|
||||||
if (targetCache) {
|
if (targetCache) {
|
||||||
const cacheEntry = await targetCache.retrieveCacheEntryByKey(keyArg);
|
const cacheEntry = await targetCache.retrieveCacheEntryByKey(keyArg);
|
||||||
|
if (cacheEntry.createdAt + cacheEntry.ttl < Date.now()) {
|
||||||
|
await this.deleteCacheEntryByKey(keyArg).catch();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return cacheEntry;
|
return cacheEntry;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@ -87,15 +92,25 @@ export class LevelCache extends AbstractCache {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache maintenance
|
public async deleteCacheEntryByKey(keyArg) {
|
||||||
/**
|
|
||||||
* cleans the cache
|
|
||||||
*/
|
|
||||||
public async clean(): Promise<void> {
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.cacheDiskManager.clean(),
|
this.cacheMemoryManager.deleteCacheEntryByKey(keyArg),
|
||||||
this.cacheDiskManager.clean(),
|
this.cacheDiskManager.deleteCacheEntryByKey(keyArg),
|
||||||
this.cacheS3Manager.clean(),
|
this.cacheS3Manager.deleteCacheEntryByKey(keyArg),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache maintenance
|
||||||
|
public async cleanOutdated() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cleans the complete cache
|
||||||
|
*/
|
||||||
|
public async cleanAll(): Promise<void> {
|
||||||
|
await Promise.all([
|
||||||
|
this.cacheDiskManager.cleanAll(),
|
||||||
|
this.cacheDiskManager.cleanAll(),
|
||||||
|
this.cacheS3Manager.cleanAll(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user