fix(caching): properly respect ttl for all cache levels
This commit is contained in:
		
							
								
								
									
										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(), | ||||||
|     ]); |     ]); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user