1
0
Fork 0
mirror of synced 2024-06-26 18:10:51 +12:00
budibase/packages/backend-core/src/cache/writethrough.ts
2022-10-25 09:02:55 +01:00

121 lines
2.9 KiB
TypeScript

import BaseCache from "./base"
import { getWritethroughClient } from "../redis/init"
import { logWarn } from "../logging"
import PouchDB from "pouchdb"
const DEFAULT_WRITE_RATE_MS = 10000
let CACHE: BaseCache | null = null
interface CacheItem {
doc: any
lastWrite: number
}
async function getCache() {
if (!CACHE) {
const client = await getWritethroughClient()
CACHE = new BaseCache(client)
}
return CACHE
}
function makeCacheKey(db: PouchDB.Database, key: string) {
return db.name + key
}
function makeCacheItem(doc: any, lastWrite: number | null = null): CacheItem {
return { doc, lastWrite: lastWrite || Date.now() }
}
export async function put(
db: PouchDB.Database,
doc: any,
writeRateMs: number = DEFAULT_WRITE_RATE_MS
) {
const cache = await getCache()
const key = doc._id
let cacheItem: CacheItem | undefined = await cache.get(makeCacheKey(db, key))
const updateDb = !cacheItem || cacheItem.lastWrite < Date.now() - writeRateMs
let output = doc
if (updateDb) {
const writeDb = async (toWrite: any) => {
// doc should contain the _id and _rev
const response = await db.put(toWrite)
output = {
...doc,
_id: response.id,
_rev: response.rev,
}
}
try {
await writeDb(doc)
} catch (err: any) {
if (err.status !== 409) {
throw err
} else {
// Swallow 409s but log them
logWarn(`Ignoring conflict in write-through cache`)
}
}
}
// if we are updating the DB then need to set the lastWrite to now
cacheItem = makeCacheItem(output, updateDb ? null : cacheItem?.lastWrite)
await cache.store(makeCacheKey(db, key), cacheItem)
return { ok: true, id: output._id, rev: output._rev }
}
export async function get(db: PouchDB.Database, id: string): Promise<any> {
const cache = await getCache()
const cacheKey = makeCacheKey(db, id)
let cacheItem: CacheItem = await cache.get(cacheKey)
if (!cacheItem) {
const doc = await db.get(id)
cacheItem = makeCacheItem(doc)
await cache.store(cacheKey, cacheItem)
}
return cacheItem.doc
}
export async function remove(
db: PouchDB.Database,
docOrId: any,
rev?: any
): Promise<void> {
const cache = await getCache()
if (!docOrId) {
throw new Error("No ID/Rev provided.")
}
const id = typeof docOrId === "string" ? docOrId : docOrId._id
rev = typeof docOrId === "string" ? rev : docOrId._rev
try {
await cache.delete(makeCacheKey(db, id))
} finally {
await db.remove(id, rev)
}
}
export class Writethrough {
db: PouchDB.Database
writeRateMs: number
constructor(
db: PouchDB.Database,
writeRateMs: number = DEFAULT_WRITE_RATE_MS
) {
this.db = db
this.writeRateMs = writeRateMs
}
async put(doc: any) {
return put(this.db, doc, this.writeRateMs)
}
async get(id: string) {
return get(this.db, id)
}
async remove(docOrId: any, rev?: any) {
return remove(this.db, docOrId, rev)
}
}