@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
96 lines (84 loc) • 2.65 kB
text/typescript
import {Metrics} from "../metrics/metrics.js";
/**
* If consumer wants more memory than available, we grow the buffer by this ratio.
*/
const GROW_RATIO = 1.1;
export enum AllocSource {
PERSISTENT_CHECKPOINTS_CACHE_VALIDATORS = "persistent_checkpoints_cache_validators",
PERSISTENT_CHECKPOINTS_CACHE_STATE = "persistent_checkpoints_cache_state",
ARCHIVE_STATE = "archive_state",
}
/**
* A simple implementation to manage a single buffer.
* This is initially used for state serialization at every epoch and for state reload.
* We can enhance and use this for other purposes in the future.
*/
export class BufferPool {
private buffer: Uint8Array;
private inUse = false;
private currentKey: number;
private readonly metrics: Metrics["bufferPool"] | null = null;
constructor(size: number, metrics: Metrics | null = null) {
this.buffer = new Uint8Array(Math.floor(size * GROW_RATIO));
this.currentKey = 0;
if (metrics) {
this.metrics = metrics.bufferPool;
metrics.bufferPool.length.addCollect(() => {
metrics.bufferPool.length.set(this.buffer.length);
});
}
}
get length(): number {
return this.buffer.length;
}
/**
* Returns a buffer of the given size with all 0.
* If the buffer is already in use, return null.
* Grow the buffer if the requested size is larger than the current buffer.
*/
alloc(size: number, source: AllocSource): BufferWithKey | null {
return this.doAlloc(size, source, false);
}
/**
* Same to alloc() but the buffer is not zeroed.
*/
allocUnsafe(size: number, source: AllocSource): BufferWithKey | null {
return this.doAlloc(size, source, true);
}
private doAlloc(size: number, source: AllocSource, isUnsafe = false): BufferWithKey | null {
if (this.inUse) {
this.metrics?.misses.inc({source});
return null;
}
this.inUse = true;
this.metrics?.hits.inc({source});
this.currentKey += 1;
if (size > this.buffer.length) {
this.metrics?.grows.inc();
this.buffer = new Uint8Array(Math.floor(size * GROW_RATIO));
}
const bytes = this.buffer.subarray(0, size);
if (!isUnsafe) {
bytes.fill(0);
}
return new BufferWithKey(bytes, this.currentKey, this);
}
/**
* Marks the buffer as free.
*/
free(key: number): void {
if (key === this.currentKey) {
this.inUse = false;
}
}
}
export class BufferWithKey implements Disposable {
constructor(
readonly buffer: Uint8Array,
private readonly key: number,
private readonly pool: BufferPool
) {}
[Symbol.dispose](): void {
this.pool.free(this.key);
}
}