From 445da7fcd6829cde9f18e1130a31a97a32097466 Mon Sep 17 00:00:00 2001 From: Blayne Chard Date: Fri, 23 Aug 2024 14:35:22 +1200 Subject: [PATCH] fix(lambda-tiler): prevent unhandled promise rejections when the rejection is handled BM-1067 (#3329) ### Motivation Fetching data from remote services can and will error on occasion, the promises to fetch data from tiffs has two `.then()` one of the `.then()` has a associated `.catch()` the other does not, this leads to a `UnhandledPromiseRejection` ### Modifications Do not use a `.then()` without `.catch()` ### Verification Unit test addded. also checked the code base for more usages of `void ....then()` without .catch and they only occur in the landing page. --- .../src/routes/tile.xyz.raster.ts | 1 - .../src/util/__test__/cache.test.ts | 20 +++++++++++++ .../lambda-tiler/src/util/source.cache.ts | 28 ++++++------------- 3 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 packages/lambda-tiler/src/util/__test__/cache.test.ts diff --git a/packages/lambda-tiler/src/routes/tile.xyz.raster.ts b/packages/lambda-tiler/src/routes/tile.xyz.raster.ts index e1b27e83a..c3792e095 100644 --- a/packages/lambda-tiler/src/routes/tile.xyz.raster.ts +++ b/packages/lambda-tiler/src/routes/tile.xyz.raster.ts @@ -109,7 +109,6 @@ export const TileXyzRaster = { } // Remove with typescript >=5.5.0 - return (await Promise.all(toLoad)).filter((f) => f != null); }, diff --git a/packages/lambda-tiler/src/util/__test__/cache.test.ts b/packages/lambda-tiler/src/util/__test__/cache.test.ts new file mode 100644 index 000000000..fcd91fb89 --- /dev/null +++ b/packages/lambda-tiler/src/util/__test__/cache.test.ts @@ -0,0 +1,20 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; + +import { fsa, FsMemory } from '@chunkd/fs'; + +import { SourceCache } from '../source.cache.js'; + +describe('CoSourceCache', () => { + it('should not exit if a promise rejection happens', async () => { + const cache = new SourceCache(5); + + const mem = new FsMemory(); + const tiffLoc = new URL('memory://foo/bar.tiff'); + await mem.write(tiffLoc, Buffer.from('ABC123')); + fsa.register('memory://', mem); + + await cache.getCog(tiffLoc).catch(() => null); + assert.equal(cache.cache.currentSize, 1); + }); +}); diff --git a/packages/lambda-tiler/src/util/source.cache.ts b/packages/lambda-tiler/src/util/source.cache.ts index 056e1b4ee..97b2ed7d3 100644 --- a/packages/lambda-tiler/src/util/source.cache.ts +++ b/packages/lambda-tiler/src/util/source.cache.ts @@ -7,54 +7,42 @@ export type LruStrut = LruStrutCotar | LruStrutCog; export interface LruStrutCotar { type: 'cotar'; value: Promise; - _value?: Cotar; + size: number; } export interface LruStrutCog { type: 'cog'; value: Promise; - _value?: Tiff; -} - -class LruStrutObj { - ob: T; - constructor(ob: T) { - this.ob = ob; - if (this.ob._value == null) { - void this.ob.value.then((c) => (this.ob._value = c)); - } - } - - size = 1; + size: number; } export class SourceCache { - cache: SwappingLru>; + cache: SwappingLru; constructor(maxSize: number) { - this.cache = new SwappingLru>(maxSize); + this.cache = new SwappingLru(maxSize); } getCog(location: URL): Promise { - const existing = this.cache.get(location.href)?.ob; + const existing = this.cache.get(location.href); if (existing != null) { if (existing.type === 'cog') return existing.value; throw new Error(`Existing object of type: ${existing.type} made for location: ${location.href}`); } const value = Tiff.create(fsa.source(location)); - this.cache.set(location.href, new LruStrutObj({ type: 'cog', value })); + this.cache.set(location.href, { type: 'cog', value, size: 1 }); return value; } getCotar(location: URL): Promise { - const existing = this.cache.get(location.href)?.ob; + const existing = this.cache.get(location.href); if (existing != null) { if (existing.type === 'cotar') return existing.value; throw new Error(`Existing object of type: ${existing.type} made for location: ${location.href}`); } const value = Cotar.fromTar(fsa.source(location)); - this.cache.set(location.href, new LruStrutObj({ type: 'cotar', value })); + this.cache.set(location.href, { type: 'cotar', value, size: 1 }); return value; } }