diff --git a/httpd-client/lib/project.ts b/httpd-client/lib/project.ts index 9a8c2bfe5..e4054a55b 100644 --- a/httpd-client/lib/project.ts +++ b/httpd-client/lib/project.ts @@ -1,3 +1,4 @@ +import { LRUCache } from "lru-cache"; import type { Commit, Commits } from "./project/commit.js"; import type { Embed } from "./project/comment.js"; import type { Fetcher, RequestOptions } from "./fetcher.js"; @@ -142,6 +143,13 @@ const diffResponseSchema = object({ export class Client { #fetcher: Fetcher; + // We make the cache a static variable because clients are usually not + // persisted and new instances are frequently created. + static #cache = { + tree: new LRUCache({ max: 300 }), + blob: new LRUCache({ max: 500 }), + }; + public constructor(fetcher: Fetcher) { this.#fetcher = fetcher; } @@ -221,7 +229,12 @@ export class Client { path: string, options?: RequestOptions, ): Promise { - return this.#fetcher.fetchOk( + const cacheKey = `blob:${sha}:${path}`; + const cachedBlob = Client.#cache.blob.get(cacheKey); + if (cachedBlob) { + return cachedBlob; + } + const blob = await this.#fetcher.fetchOk( { method: "GET", path: `projects/${id}/blob/${sha}/${path}`, @@ -229,6 +242,8 @@ export class Client { }, blobSchema, ); + Client.#cache.blob.set(cacheKey, blob); + return blob; } public async getTree( @@ -237,7 +252,12 @@ export class Client { path?: string, options?: RequestOptions, ): Promise { - return this.#fetcher.fetchOk( + const cacheKey = `tree:${sha}:${path}`; + const cachedTree = Client.#cache.tree.get(cacheKey); + if (cachedTree) { + return cachedTree; + } + const tree = await this.#fetcher.fetchOk( { method: "GET", path: `projects/${id}/tree/${sha}/${path ?? ""}`, @@ -245,6 +265,8 @@ export class Client { }, treeSchema, ); + Client.#cache.tree.set(cacheKey, tree); + return tree; } public async getAllRemotes( diff --git a/package-lock.json b/package-lock.json index cf64384a7..e97eee139 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "hast-util-to-dom": "^4.0.0", "hast-util-to-html": "^9.0.0", "lodash": "^4.17.21", + "lru-cache": "^10.2.0", "marked": "^11.1.1", "marked-katex-extension": "^5.0.0", "marked-linkify-it": "^3.1.8", @@ -3303,6 +3304,14 @@ "get-func-name": "^2.0.1" } }, + "node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/magic-string": { "version": "0.30.5", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", diff --git a/package.json b/package.json index eb2bc3643..7d58d72cf 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "hast-util-to-dom": "^4.0.0", "hast-util-to-html": "^9.0.0", "lodash": "^4.17.21", + "lru-cache": "^10.2.0", "marked": "^11.1.1", "marked-katex-extension": "^5.0.0", "marked-linkify-it": "^3.1.8",