fix(caching): properly respect ttl for all cache levels

This commit is contained in:
Philipp Kunz 2021-05-10 14:26:32 +00:00
parent f7b6df5ff7
commit 6b57e8b1f3
7 changed files with 88 additions and 20 deletions

View File

@ -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();

View File

@ -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>;
} }

View File

@ -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);

View File

@ -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();
} }
} }

View File

@ -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();
} }
} }

View File

@ -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();
} }

View File

@ -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(),
]); ]);
} }
} }