@lsdsoftware/s3-log-store
Version:
Efficiently store and retrieve log-structured data (append only) in S3
60 lines (51 loc) • 1.61 kB
text/typescript
import fs from "fs"
import fsp from "fs/promises"
import path from "path"
import * as rxjs from "rxjs"
export interface AccessTracker {
notifyAccess(): void
isPurgeable(): boolean
}
export function makeRetrievalCache({ cacheFolder, cleanupInterval, makeAccessTracker }: {
cacheFolder: string
cleanupInterval: number
makeAccessTracker(): AccessTracker
}) {
fs.statSync(cacheFolder)
const accessTrackers = new Map<string, AccessTracker>()
return {
cleanupJob$: rxjs.timer(0, cleanupInterval).pipe(
rxjs.exhaustMap(cleanup)
),
async get(hashKey: string) {
try {
const value = await fsp.readFile(path.join(cacheFolder, hashKey), 'utf8')
accessTrackers.get(hashKey)?.notifyAccess()
return value
} catch (err: any) {
if (err.code != 'ENOENT') throw err
}
},
async set(hashKey: string, value: string) {
await fsp.writeFile(path.join(cacheFolder, hashKey), value)
accessTrackers.set(hashKey, makeAccessTracker())
}
}
async function cleanup() {
const filesDeleted: string[] = []
for (const hashKey of await fsp.readdir(cacheFolder)) {
const tracker = accessTrackers.get(hashKey)
if (tracker) {
if (tracker.isPurgeable()) {
await fsp.rm(path.join(cacheFolder, hashKey))
accessTrackers.delete(hashKey)
filesDeleted.push(hashKey)
}
} else {
//if entry isn't currently tracked, e.g. process restart, start tracking
accessTrackers.set(hashKey, makeAccessTracker())
}
}
return { filesDeleted }
}
}