@chainsafe/libp2p-gossipsub
Version:
A typescript implementation of gossipsub
72 lines (59 loc) • 2.21 kB
text/typescript
interface SimpleTimeCacheOpts {
validityMs: number
}
interface CacheValue<T> {
value: T
validUntilMs: number
}
/**
* This is similar to https://github.com/daviddias/time-cache/blob/master/src/index.js
* for our own need, we don't use lodash throttle to improve performance.
* This gives 4x - 5x performance gain compared to npm TimeCache
*/
export class SimpleTimeCache<T> {
private readonly entries = new Map<string | number, CacheValue<T>>()
private readonly validityMs: number
constructor (opts: SimpleTimeCacheOpts) {
this.validityMs = opts.validityMs
// allow negative validityMs so that this does not cache anything, spec test compliance.spec.js
// sends duplicate messages and expect peer to receive all. Application likely uses positive validityMs
}
get size (): number {
return this.entries.size
}
/** Returns true if there was a key collision and the entry is dropped */
put (key: string | number, value: T): boolean {
if (this.entries.has(key)) {
// Key collisions break insertion order in the entries cache, which break prune logic.
// prune relies on each iterated entry to have strictly ascending validUntilMs, else it
// won't prune expired entries and SimpleTimeCache will grow unexpectedly.
// As of Oct 2022 NodeJS v16, inserting the same key twice with different value does not
// change the key position in the iterator stream. A unit test asserts this behaviour.
return true
}
this.entries.set(key, { value, validUntilMs: Date.now() + this.validityMs })
return false
}
prune (): void {
const now = Date.now()
for (const [k, v] of this.entries.entries()) {
if (v.validUntilMs < now) {
this.entries.delete(k)
} else {
// Entries are inserted with strictly ascending validUntilMs.
// Stop early to save iterations
break
}
}
}
has (key: string): boolean {
return this.entries.has(key)
}
get (key: string | number): T | undefined {
const value = this.entries.get(key)
return (value != null) && value.validUntilMs >= Date.now() ? value.value : undefined
}
clear (): void {
this.entries.clear()
}
}