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

View File

@ -4,22 +4,35 @@ export abstract class AbstractCache {
public abstract ready: Promise<void>;
public abstract status: 'active' | 'inactive';
// Blobs
// Cache Entries
/**
* store a Blob
*/
public abstract storeCacheEntryByKey(keyArg: string, valueArg: CacheEntry): Promise<void>;
// Cache Entries
/**
* retrieve cache entry
*/
public abstract retrieveCacheEntryByKey(keyArg: string): Promise<CacheEntry>;
/**
* checks for the presence of a key
* @param keyArg
*/
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)));
}
/**
* cleans the DiskCache directory
*/
public async clean() {
public async deleteCacheEntryByKey(keyArg: string) {
await plugins.smartfile.fs.remove(plugins.path.join(this.fsPath, encodeURIComponent(keyArg)));
}
public async cleanOutdated() {}
public async cleanAll() {
if (this.status === 'active') {
if (plugins.smartfile.fs.isDirectory(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();
}
}

View File

@ -64,7 +64,18 @@ export class CacheS3Manager extends AbstractCache {
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();
}
}

View File

@ -29,6 +29,9 @@ export class CacheEntry
@plugins.smartjson.foldDec()
contents: Buffer;
@plugins.smartjson.foldDec()
public createdAt: number;
public toStorageJsonString(): string {
return this.foldToJson();
}

View File

@ -62,6 +62,7 @@ export class LevelCache extends AbstractCache {
public async storeCacheEntryByKey(keyArg: string, cacheEntryArg: CacheEntry): Promise<void> {
cacheEntryArg.key = keyArg;
const targetCache = await this.cacheRouter.getCacheForStoreAction(keyArg, cacheEntryArg);
cacheEntryArg.createdAt = Date.now();
await targetCache.storeCacheEntryByKey(keyArg, cacheEntryArg);
}
@ -73,6 +74,10 @@ export class LevelCache extends AbstractCache {
const targetCache = await this.cacheRouter.getCacheForRetrieveAction(keyArg);
if (targetCache) {
const cacheEntry = await targetCache.retrieveCacheEntryByKey(keyArg);
if (cacheEntry.createdAt + cacheEntry.ttl < Date.now()) {
await this.deleteCacheEntryByKey(keyArg).catch();
return null;
}
return cacheEntry;
} else {
return null;
@ -87,15 +92,25 @@ export class LevelCache extends AbstractCache {
]);
}
// cache maintenance
/**
* cleans the cache
*/
public async clean(): Promise<void> {
public async deleteCacheEntryByKey(keyArg) {
await Promise.all([
this.cacheDiskManager.clean(),
this.cacheDiskManager.clean(),
this.cacheS3Manager.clean(),
this.cacheMemoryManager.deleteCacheEntryByKey(keyArg),
this.cacheDiskManager.deleteCacheEntryByKey(keyArg),
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(),
]);
}
}