Skip to content

Commit

Permalink
Fall back to FS for large blobs in LMDBCache
Browse files Browse the repository at this point in the history
  • Loading branch information
lettertwo committed Nov 3, 2021
1 parent 6c88b5d commit 74b4dd4
Showing 1 changed file with 35 additions and 20 deletions.
55 changes: 35 additions & 20 deletions packages/core/cache/src/LMDBCache.js
Expand Up @@ -44,44 +44,46 @@ export class LMDBCache implements Cache {
}

has(key: string): Promise<boolean> {
return Promise.resolve(this.store.get(key) != null);
if (this.store.get(key) != null) return Promise.resolve(true);
return this.hasLargeBlob(key);
}

get<T>(key: string): Promise<?T> {
let data = this.store.get(key);
if (data == null) {
return Promise.resolve(null);
}

return Promise.resolve(deserialize(data));
async get<T>(key: string): Promise<?T> {
let data = await this.getBuffer(key);
return data == null ? null : deserialize(data);
}

async set(key: string, value: mixed): Promise<void> {
await this.store.put(key, serialize(value));
await this.setBlob(key, serialize(value));
}

getStream(key: string): Readable {
return blobToStream(this.store.get(key));
return blobToStream(
this.store.get(key) ?? this.fs.readFileSync(path.join(this.dir, key)),
);
}

async setStream(key: string, stream: Readable): Promise<void> {
let buf = await bufferStream(stream);
await this.store.put(key, buf);
await this.setBlob(key, await bufferStream(stream));
}

getBlob(key: string): Promise<Buffer> {
let buffer = this.store.get(key);
return buffer != null
? Promise.resolve(buffer)
: Promise.reject(new Error(`Key ${key} not found in cache`));
async getBlob(key: string): Promise<Buffer> {
let buffer = await this.getBuffer(key);
if (buffer == null) throw new Error(`Key ${key} not found in cache`);
return buffer;
}

async setBlob(key: string, contents: Buffer | string): Promise<void> {
await this.store.put(key, contents);
if (isLargeBlob(contents)) await this.setLargeBlob(key, contents);
else await this.store.put(key, contents);
}

getBuffer(key: string): Promise<?Buffer> {
return Promise.resolve(this.store.get(key));
async getBuffer(key: string): Promise<?Buffer> {
let buffer = this.store.get(key);
if (buffer == null && (await this.hasLargeBlob(key))) {
buffer = await this.getLargeBlob(key);
}
return buffer;
}

hasLargeBlob(key: string): Promise<boolean> {
Expand All @@ -97,4 +99,17 @@ export class LMDBCache implements Cache {
}
}

// lmbd-store decodes cached binary data into a Node Buffer
// via `Nan::NewBuffer`, which enforces a max size of ~1GB.
// We subtract 9 bytes to account for any compression heaader
// added by lmbd-store when encoding the data.
// See: https://github.com/nodejs/nan/issues/883
const MAX_BUFFER_SIZE = 0x3fffffff - 9;

function isLargeBlob(contents: Buffer | string): boolean {
return typeof contents === 'string'
? Buffer.byteLength(contents) > MAX_BUFFER_SIZE
: contents.length > MAX_BUFFER_SIZE;
}

registerSerializableClass(`${packageJson.version}:LMDBCache`, LMDBCache);

0 comments on commit 74b4dd4

Please sign in to comment.