mirror of
https://github.com/Drop-OSS/drop.git
synced 2025-11-10 04:22:09 +10:00
feat: unified cache handler
This commit is contained in:
@ -48,6 +48,20 @@ export default defineNuxtConfig({
|
||||
},
|
||||
|
||||
compressPublicAssets: true,
|
||||
|
||||
storage: {
|
||||
appCache: {
|
||||
driver: "lru-cache",
|
||||
},
|
||||
},
|
||||
|
||||
devStorage: {
|
||||
appCache: {
|
||||
// store cache on fs to handle dev server restarts
|
||||
driver: "fs",
|
||||
base: "./.data/appCache",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
typescript: {
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
"fast-fuzzy": "^1.12.0",
|
||||
"file-type-mime": "^0.4.3",
|
||||
"jdenticon": "^3.3.0",
|
||||
"lru-cache": "^11.1.0",
|
||||
"luxon": "^3.6.1",
|
||||
"micromark": "^4.0.1",
|
||||
"nuxt": "^3.16.2",
|
||||
@ -40,6 +39,7 @@
|
||||
"sharp": "^0.33.5",
|
||||
"stream-mime-type": "^2.0.0",
|
||||
"turndown": "^7.2.0",
|
||||
"unstorage": "^1.15.0",
|
||||
"vue": "latest",
|
||||
"vue-router": "latest",
|
||||
"vue3-carousel": "^0.15.0",
|
||||
@ -75,4 +75,4 @@
|
||||
"prisma": {
|
||||
"schema": "./prisma"
|
||||
}
|
||||
}
|
||||
}
|
||||
33
server/internal/cache/cacheHandler.ts
vendored
Normal file
33
server/internal/cache/cacheHandler.ts
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
import { prefixStorage, type StorageValue, type Storage } from "unstorage";
|
||||
|
||||
export interface CacheProviderOptions {
|
||||
/**
|
||||
* Max number of items in the cache
|
||||
*/
|
||||
max?: number;
|
||||
|
||||
/**
|
||||
* Time to live (in ms)
|
||||
*/
|
||||
ttl?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and manages the lifecycles of various caches
|
||||
*/
|
||||
export class CacheHandler {
|
||||
private caches = new Map<string, Storage<StorageValue>>();
|
||||
|
||||
/**
|
||||
* Create a new cache
|
||||
* @param name
|
||||
* @returns
|
||||
*/
|
||||
createCache<V extends StorageValue>(name: string) {
|
||||
// will allow us to dynamicing use redis in the future just by changing the storage used
|
||||
const provider = prefixStorage<V>(useStorage<V>("appCache"), name);
|
||||
// hack to let ts have us store cache
|
||||
this.caches.set(name, provider as unknown as Storage<StorageValue>);
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
4
server/internal/cache/index.ts
vendored
Normal file
4
server/internal/cache/index.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import { CacheHandler } from "./cacheHandler";
|
||||
|
||||
export const cacheHandler = new CacheHandler();
|
||||
export default cacheHandler;
|
||||
@ -1,12 +1,12 @@
|
||||
import type { ObjectMetadata, ObjectReference, Source } from "./objectHandler";
|
||||
import { ObjectBackend } from "./objectHandler";
|
||||
|
||||
import { LRUCache } from "lru-cache";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { Readable } from "stream";
|
||||
import { createHash } from "crypto";
|
||||
import prisma from "../db/database";
|
||||
import cacheHandler from "../cache";
|
||||
|
||||
export class FsObjectBackend extends ObjectBackend {
|
||||
private baseObjectPath: string;
|
||||
@ -34,7 +34,7 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
if (!fs.existsSync(objectPath)) return false;
|
||||
|
||||
// remove item from cache
|
||||
this.hashStore.delete(id);
|
||||
await this.hashStore.delete(id);
|
||||
|
||||
if (source instanceof Readable) {
|
||||
const outputStream = fs.createWriteStream(objectPath);
|
||||
@ -54,7 +54,7 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
const objectPath = path.join(this.baseObjectPath, id);
|
||||
if (!fs.existsSync(objectPath)) return undefined;
|
||||
// remove item from cache
|
||||
this.hashStore.delete(id);
|
||||
await this.hashStore.delete(id);
|
||||
return fs.createWriteStream(objectPath);
|
||||
}
|
||||
async create(
|
||||
@ -99,7 +99,7 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
if (!fs.existsSync(objectPath)) return true;
|
||||
fs.rmSync(objectPath);
|
||||
// remove item from cache
|
||||
this.hashStore.delete(id);
|
||||
await this.hashStore.delete(id);
|
||||
return true;
|
||||
}
|
||||
async fetchMetadata(
|
||||
@ -121,36 +121,35 @@ export class FsObjectBackend extends ObjectBackend {
|
||||
}
|
||||
async fetchHash(id: ObjectReference): Promise<string | undefined> {
|
||||
const cacheResult = await this.hashStore.get(id);
|
||||
if (cacheResult !== undefined) return cacheResult;
|
||||
if (cacheResult !== null) return cacheResult;
|
||||
|
||||
const obj = await this.fetch(id);
|
||||
if (obj === undefined) return;
|
||||
|
||||
// local variable to point to object
|
||||
const cache = this.hashStore;
|
||||
|
||||
// hash object
|
||||
const hash = createHash("md5");
|
||||
hash.setEncoding("hex");
|
||||
|
||||
// local variable to point to object
|
||||
const store = this.hashStore;
|
||||
|
||||
// read obj into hash
|
||||
obj.pipe(hash);
|
||||
await new Promise<void>((r) => {
|
||||
obj.on("end", function () {
|
||||
obj.on("end", async function () {
|
||||
hash.end();
|
||||
cache.save(id, hash.read());
|
||||
await store.save(id, hash.read());
|
||||
r();
|
||||
});
|
||||
});
|
||||
|
||||
return await this.hashStore.get(id);
|
||||
const result = await this.hashStore.get(id);
|
||||
return result === null ? undefined : result;
|
||||
}
|
||||
}
|
||||
|
||||
class FsHashStore {
|
||||
private cache = new LRUCache<string, string>({
|
||||
max: 1000, // number of items
|
||||
});
|
||||
private cache = cacheHandler.createCache<string>("ObjectHashStore");
|
||||
|
||||
/**
|
||||
* Gets hash of object
|
||||
@ -158,8 +157,11 @@ class FsHashStore {
|
||||
* @returns
|
||||
*/
|
||||
async get(id: ObjectReference) {
|
||||
const cacheRes = this.cache.get(id);
|
||||
if (cacheRes !== undefined) return cacheRes;
|
||||
const cacheRes = await this.cache.get(id);
|
||||
if (cacheRes !== null) {
|
||||
console.log("object cache hit");
|
||||
return cacheRes;
|
||||
}
|
||||
|
||||
const objectHash = await prisma.objectHash.findUnique({
|
||||
where: {
|
||||
@ -170,7 +172,7 @@ class FsHashStore {
|
||||
},
|
||||
});
|
||||
if (objectHash === null) return undefined;
|
||||
this.cache.set(id, objectHash.hash);
|
||||
await this.cache.set(id, objectHash.hash);
|
||||
return objectHash.hash;
|
||||
}
|
||||
|
||||
@ -191,7 +193,7 @@ class FsHashStore {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
this.cache.set(id, hash);
|
||||
await this.cache.set(id, hash);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,7 +201,7 @@ class FsHashStore {
|
||||
* @param id
|
||||
*/
|
||||
async delete(id: ObjectReference) {
|
||||
this.cache.delete(id);
|
||||
await this.cache.remove(id);
|
||||
|
||||
try {
|
||||
// need to catch in case the object doesn't exist
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
import { LRUCache } from "lru-cache";
|
||||
import prisma from "../db/database";
|
||||
import type { Session, SessionProvider } from "./types";
|
||||
import cacheHandler from "../cache";
|
||||
|
||||
export default function createDBSessionHandler(): SessionProvider {
|
||||
const cache = new LRUCache<string, Session>({
|
||||
max: 50, // number of items
|
||||
ttl: 30 * 100, // 30s (in ms)
|
||||
});
|
||||
const cache = cacheHandler.createCache<Session>("DBSession");
|
||||
|
||||
return {
|
||||
async setSession(token, session) {
|
||||
cache.set(token, session);
|
||||
await cache.set(token, session);
|
||||
|
||||
// const strData = JSON.stringify(data);
|
||||
await prisma.session.upsert({
|
||||
@ -29,8 +26,8 @@ export default function createDBSessionHandler(): SessionProvider {
|
||||
return await this.setSession(token, data);
|
||||
},
|
||||
async getSession<T extends Session>(token: string) {
|
||||
const cached = cache.get(token);
|
||||
if (cached !== undefined) return cached as T;
|
||||
const cached = await cache.get(token);
|
||||
if (cached !== null) return cached as T;
|
||||
|
||||
const result = await prisma.session.findUnique({
|
||||
where: {
|
||||
@ -45,7 +42,7 @@ export default function createDBSessionHandler(): SessionProvider {
|
||||
return result as unknown as T;
|
||||
},
|
||||
async removeSession(token) {
|
||||
cache.delete(token);
|
||||
await cache.remove(token);
|
||||
await prisma.session.delete({
|
||||
where: {
|
||||
token,
|
||||
|
||||
@ -2,15 +2,17 @@
|
||||
Handles managing collections
|
||||
*/
|
||||
|
||||
import cacheHandler from "../cache";
|
||||
import prisma from "../db/database";
|
||||
|
||||
class UserLibraryManager {
|
||||
// Caches the user's core library
|
||||
private userCoreLibraryCache: { [key: string]: string } = {};
|
||||
private coreLibraryCache =
|
||||
cacheHandler.createCache<string>("UserCoreLibrary");
|
||||
|
||||
private async fetchUserLibrary(userId: string) {
|
||||
if (this.userCoreLibraryCache[userId])
|
||||
return this.userCoreLibraryCache[userId];
|
||||
const cached = await this.coreLibraryCache.get(userId);
|
||||
if (cached !== null) return cached;
|
||||
|
||||
let collection = await prisma.collection.findFirst({
|
||||
where: {
|
||||
@ -28,7 +30,7 @@ class UserLibraryManager {
|
||||
},
|
||||
});
|
||||
|
||||
this.userCoreLibraryCache[userId] = collection.id;
|
||||
await this.coreLibraryCache.set(userId, collection.id);
|
||||
|
||||
return collection.id;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user