Skip to content

Commit

Permalink
Reduce requests in patch view
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastinez committed Aug 9, 2024
1 parent f914d94 commit de1a02a
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 26 deletions.
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"hast-util-to-dom": "^4.0.0",
"hast-util-to-html": "^9.0.1",
"lodash": "^4.17.21",
"lru-cache": "^11.0.0",
"marked": "^14.0.0",
"marked-emoji": "^1.4.2",
"marked-footnote": "^1.2.2",
Expand Down
31 changes: 31 additions & 0 deletions src/lib/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { HttpdClient, type BaseUrl } from "@http-client";
import { LRUCache } from "lru-cache";

function cached<Args extends unknown[], V>(
f: (...args: Args) => Promise<V>,
makeKey: (...args: Args) => string,
options?: LRUCache.Options<string, { value: V }, unknown>,
): (...args: Args) => Promise<V> {
const cache = new LRUCache(options || { max: 500 });
return async function (...args: Args): Promise<V> {
const key = makeKey(...args);
const cached = cache.get(key);

if (cached === undefined) {
const value = await f(...args);
cache.set(key, { value });
return value;
} else {
return cached.value;
}
};
}

export const cacheQueryDiff = cached(
async (baseUrl: BaseUrl, rid: string, base: string, oid: string) => {
const api = new HttpdClient(baseUrl);
return await api.project.getDiff(rid, base, oid);
},
(...args) => JSON.stringify(args),
{ max: 200 },
);
8 changes: 7 additions & 1 deletion src/views/projects/Cob/Revision.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import * as utils from "@app/lib/utils";
import { HttpdClient } from "@http-client";
import { cacheQueryDiff } from "@app/lib/cache";
import { onMount } from "svelte";
import CobCommitTeaser from "@app/views/projects/Cob/CobCommitTeaser.svelte";
Expand Down Expand Up @@ -104,7 +105,12 @@
onMount(async () => {
try {
loading = true;
response = await api.project.getDiff(projectId, fromCommit, revisionOid);
response = await cacheQueryDiff(
api.baseUrl,
projectId,
fromCommit,
revisionOid,
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
error = err;
Expand Down
12 changes: 2 additions & 10 deletions src/views/projects/DiffStatBadgeLoader.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import type { BaseUrl, Patch, Revision } from "@http-client";
import { HttpdClient } from "@http-client";
import { cacheQueryDiff } from "@app/lib/cache";
import { formatCommit } from "@app/lib/utils";
import DiffStatBadge from "@app/components/DiffStatBadge.svelte";
Expand All @@ -12,17 +12,9 @@
export let baseUrl: BaseUrl;
export let patch: Patch;
export let latestRevision: Revision;
$: diffPromise = api.project.getDiff(
projectId,
latestRevision.base,
latestRevision.oid,
);
const api = new HttpdClient(baseUrl);
</script>

{#await diffPromise}
{#await cacheQueryDiff(baseUrl, projectId, latestRevision.base, latestRevision.oid)}
<Loading small />
{:then { diff }}
<Link
Expand Down
63 changes: 48 additions & 15 deletions src/views/projects/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,24 @@ import type {
DiffBlob,
Issue,
IssueState,
Node,
Patch,
PatchState,
Project,
Remote,
Revision,
SeedingPolicy,
Tree,
} from "@http-client";

import * as Syntax from "@app/lib/syntax";
import config from "virtual:config";
import { isLocal, unreachable } from "@app/lib/utils";
import { nodePath } from "@app/views/nodes/router";
import { handleError, unreachableError } from "@app/views/projects/error";
import { HttpdClient } from "@http-client";
import { ResponseError, ResponseParseError } from "@http-client/lib/fetcher";
import { cacheQueryDiff } from "@app/lib/cache";
import { handleError, unreachableError } from "@app/views/projects/error";
import { isLocal, unreachable } from "@app/lib/utils";
import { nodePath } from "@app/views/nodes/router";

export const PATCHES_PER_PAGE = 10;
export const ISSUES_PER_PAGE = 10;
Expand Down Expand Up @@ -291,7 +294,7 @@ export async function loadProjectRoute(
} else if (route.resource === "project.issue") {
return await loadIssueView(route);
} else if (route.resource === "project.patch") {
return await loadPatchView(route);
return await loadPatchView(route, previousLoaded);
} else if (route.resource === "project.issues") {
return await loadIssuesView(route);
} else if (route.resource === "project.patches") {
Expand Down Expand Up @@ -633,21 +636,49 @@ async function loadIssueView(

async function loadPatchView(
route: ProjectPatchRoute,
previousLoaded: LoadedRoute,
): Promise<ProjectLoadedRoute> {
const api = new HttpdClient(route.node);
const rawPath = (commit?: string) =>
`${route.node.scheme}://${route.node.hostname}:${route.node.port}/raw/${
route.project
}${commit ? `/${commit}` : ""}`;

const [project, patch, seedingPolicy, node] = await Promise.all([
api.project.getById(route.project),
api.project.getPatchById(route.project, route.patch),
api.getPolicyById(route.project),
api.getNode(),
let projectPromise: Promise<Project>;
let patchPromise: Promise<Patch>;
let nodePromise: Promise<Partial<Node>>;
let seedingPolicyPromise: Promise<SeedingPolicy>;

if (
previousLoaded.resource === "project.patch" &&
previousLoaded.params.project.id === route.project &&
previousLoaded.params.patch.id === route.patch
) {
projectPromise = Promise.resolve(previousLoaded.params.project);
patchPromise = Promise.resolve(previousLoaded.params.patch);
seedingPolicyPromise = Promise.resolve(previousLoaded.params.seedingPolicy);
nodePromise = Promise.resolve({
avatarUrl: previousLoaded.params.nodeAvatarUrl,
});
} else {
projectPromise = api.project.getById(route.project);
patchPromise = api.project.getPatchById(route.project, route.patch);
seedingPolicyPromise = api.getPolicyById(route.project);
nodePromise = api.getNode();
}
const [project, patch, seedingPolicy, { avatarUrl }] = await Promise.all([
projectPromise,
patchPromise,
seedingPolicyPromise,
nodePromise,
]);
const latestRevision = patch.revisions[patch.revisions.length - 1];
const { diff } = await api.project.getDiff(

// SAFETY: Patches always have at least one revision
const latestRevision = patch.revisions.at(-1) as Revision;
const {
diff: { stats },
} = await cacheQueryDiff(
api.baseUrl,
route.project,
latestRevision.base,
latestRevision.oid,
Expand All @@ -669,7 +700,8 @@ async function loadPatchView(
`revision ${revisionId} of patch ${route.patch} not found`,
);
}
const { diff, commits, files } = await api.project.getDiff(
const { diff, commits, files } = await cacheQueryDiff(
api.baseUrl,
route.project,
revision.base,
revision.oid,
Expand All @@ -686,7 +718,8 @@ async function loadPatchView(
}
case "diff": {
const { fromCommit, toCommit } = route.view;
const { diff, files } = await api.project.getDiff(
const { diff, files } = await cacheQueryDiff(
api.baseUrl,
route.project,
fromCommit,
toCommit,
Expand All @@ -704,9 +737,9 @@ async function loadPatchView(
project,
rawPath,
patch,
stats: diff.stats,
stats,
view,
nodeAvatarUrl: node.avatarUrl,
nodeAvatarUrl: avatarUrl,
},
};
}
Expand Down

0 comments on commit de1a02a

Please sign in to comment.