Skip to content

Commit 7004fb2

Browse files
feat(dev): don't use Redis for dev purposes
1 parent 852db31 commit 7004fb2

File tree

6 files changed

+107
-22
lines changed

6 files changed

+107
-22
lines changed

src/lib/server/cache-handler.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import type { Redis } from "@upstash/redis";
2+
3+
export type RedisJson = Parameters<InstanceType<typeof Redis>["json"]["set"]>[2];
4+
5+
export class CacheHandler {
6+
readonly #redis: Redis;
7+
readonly #memoryCache: Map<string, { value: unknown; expiresAt: number | null }>;
8+
readonly #isDev: boolean;
9+
10+
/**
11+
* Initialize the cache handler
12+
*
13+
* @param redis the Redis instance
14+
* @param isDev whether we're in dev or prod
15+
*/
16+
constructor(redis: Redis, isDev: boolean) {
17+
this.#redis = redis;
18+
this.#memoryCache = new Map();
19+
this.#isDev = isDev;
20+
}
21+
22+
/**
23+
* Get a value from the cache
24+
*
25+
* @param key the key to get the result for
26+
* @returns the cached value, or `null` if not present
27+
*/
28+
async get<T extends RedisJson>(key: string) {
29+
if (this.#isDev) {
30+
console.log(`Retrieving ${key} from in-memory cache`);
31+
// In dev mode, use memory cache only
32+
const entry = this.#memoryCache.get(key);
33+
34+
if (!entry) {
35+
console.log("Nothing to retrieve");
36+
return null;
37+
}
38+
39+
// Check if entry is expired
40+
if (entry.expiresAt && entry.expiresAt < Date.now()) {
41+
console.log("Value expired, purging and returning null");
42+
this.#memoryCache.delete(key);
43+
return null;
44+
}
45+
46+
console.log("Returning found value from in-memory cache");
47+
return entry.value as T;
48+
}
49+
50+
// In production, use Redis
51+
try {
52+
return await this.#redis.json.get<T>(key);
53+
} catch (error) {
54+
console.error("Redis get error:", error);
55+
return null;
56+
}
57+
}
58+
59+
/**
60+
* Set a value in the cache
61+
*
62+
* @param key the key to store the value for
63+
* @param value the value to store
64+
* @param ttlSeconds the optional TTL to set for expiration
65+
*/
66+
async set<T extends RedisJson>(key: string, value: T, ttlSeconds?: number) {
67+
if (this.#isDev) {
68+
console.log(`Setting value for ${key} in memory cache`);
69+
// In dev mode, use memory cache only
70+
const expiresAt = ttlSeconds ? Date.now() + ttlSeconds * 1000 : null;
71+
if (expiresAt) {
72+
console.log(`Defining cache for ${key}, expires at ${new Date(expiresAt)}`);
73+
} else console.log(`No cache set for ${key}`);
74+
this.#memoryCache.set(key, { value, expiresAt });
75+
} else {
76+
// In production, use Redis
77+
try {
78+
await this.#redis.json.set(key, "$", value);
79+
if (ttlSeconds) await this.#redis.expire(key, ttlSeconds);
80+
} catch (error) {
81+
console.error("Redis set error:", error);
82+
}
83+
}
84+
}
85+
}

src/lib/server/github-cache.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { dev } from "$app/environment";
12
import { GITHUB_TOKEN, KV_REST_API_TOKEN, KV_REST_API_URL } from "$env/static/private";
23
import type {
34
CommentAuthorAssociation,
@@ -8,6 +9,7 @@ import { Octokit } from "octokit";
89
import parseChangelog from "$lib/changelog-parser";
910
import type { Repository } from "$lib/repositories";
1011
import type { Issues, Pulls } from "$lib/types";
12+
import { CacheHandler, type RedisJson } from "./cache-handler";
1113

1214
/**
1315
* A strict version of Extract.
@@ -159,7 +161,7 @@ const DEPRECATIONS_TTL = 60 * 60 * 24 * 2; // 2 days
159161
* with an additional caching mechanism.
160162
*/
161163
export class GitHubCache {
162-
readonly #redis: Redis;
164+
readonly #cache: CacheHandler;
163165
readonly #octokit: Octokit;
164166

165167
/**
@@ -171,10 +173,13 @@ export class GitHubCache {
171173
* @constructor
172174
*/
173175
constructor(redisUrl: string, redisToken: string, githubToken: string) {
174-
this.#redis = new Redis({
175-
url: redisUrl,
176-
token: redisToken
177-
});
176+
this.#cache = new CacheHandler(
177+
new Redis({
178+
url: redisUrl,
179+
token: redisToken
180+
}),
181+
dev
182+
);
178183

179184
this.#octokit = new Octokit({
180185
auth: githubToken
@@ -238,7 +243,7 @@ export class GitHubCache {
238243
* @returns a currying promise than handles everything needed for requests
239244
* @private
240245
*/
241-
#processCached<RType extends Parameters<InstanceType<typeof Redis>["json"]["set"]>[2]>() {
246+
#processCached<RType extends RedisJson>() {
242247
/**
243248
* Inner currying function to circumvent unsupported partial inference
244249
*
@@ -255,7 +260,7 @@ export class GitHubCache {
255260
transformer: (from: Awaited<PromiseType>) => RType | Promise<RType>,
256261
ttl: number | ((value: RType) => number | undefined) | undefined = undefined
257262
): Promise<RType> => {
258-
const cachedValue = await this.#redis.json.get<RType>(cacheKey);
263+
const cachedValue = await this.#cache.get<RType>(cacheKey);
259264
if (cachedValue) {
260265
console.log(`Cache hit for ${cacheKey}`);
261266
return cachedValue;
@@ -265,17 +270,15 @@ export class GitHubCache {
265270

266271
const newValue = await transformer(await promise());
267272

268-
await this.#redis.json.set(cacheKey, "$", newValue);
273+
let ttlResult: number | undefined = undefined;
269274
if (ttl !== undefined) {
270275
if (typeof ttl === "function") {
271-
const ttlResult = ttl(newValue);
272-
if (ttlResult !== undefined) {
273-
await this.#redis.expire(cacheKey, ttlResult);
274-
}
276+
ttlResult = ttl(newValue);
275277
} else {
276-
await this.#redis.expire(cacheKey, ttl);
278+
ttlResult = ttl;
277279
}
278280
}
281+
await this.#cache.set(cacheKey, newValue, ttlResult);
279282

280283
return newValue;
281284
};

src/routes/package/SidePanel.svelte

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@
9696
if (!lastVisitedItem) {
9797
return releases.filter(
9898
({ created_at, published_at }) =>
99-
new Date(published_at ?? created_at).getTime() >
100-
new Date().getTime() - 1000 * 60 * 60 * 24 * 7
99+
new Date(published_at ?? created_at).getTime() > Date.now() - 1000 * 60 * 60 * 24 * 7
101100
);
102101
}
103102
const lastVisitedDate = new Date(lastVisitedItem);

src/routes/package/[...package]/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
// Only expand releases that are less than a week old
126126
.filter(
127127
({ created_at }) =>
128-
new Date(created_at).getTime() > new Date().getTime() - 1000 * 60 * 60 * 24 * 7
128+
new Date(created_at).getTime() > Date.now() - 1000 * 60 * 60 * 24 * 7
129129
)
130130
.map(({ id }) => id.toString())}
131131
class="w-full space-y-2"

src/routes/package/[...package]/ReleaseCard.svelte

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,7 @@
5656
semVersion?.patch === 0 &&
5757
!semVersion?.prerelease.length
5858
);
59-
let isOlderThanAWeek = $derived(
60-
releaseDate.getTime() < new Date().getTime() - 1000 * 60 * 60 * 24 * 7
61-
);
59+
let isOlderThanAWeek = $derived(releaseDate.getTime() < Date.now() - 1000 * 60 * 60 * 24 * 7);
6260
6361
/**
6462
* Converts a date to a relative date string.
@@ -69,7 +67,7 @@
6967
* @returns the relative date
7068
*/
7169
function timeAgo(date: Date, locale = "en") {
72-
const diff = (new Date().getTime() - date.getTime()) / 1000;
70+
const diff = (Date.now() - date.getTime()) / 1000;
7371
const minutes = Math.floor(diff / 60);
7472
const hours = Math.floor(minutes / 60);
7573
const days = Math.floor(hours / 24);

src/routes/tracker/[org]/[repo]/+page.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* @returns if it is more recent than a month
2323
*/
2424
function isNew(date: Date) {
25-
return date.getTime() >= new Date().getTime() - 1000 * 60 * 60 * 24 * 30;
25+
return date.getTime() >= Date.now() - 1000 * 60 * 60 * 24 * 30;
2626
}
2727
2828
/**
@@ -32,7 +32,7 @@
3232
* @returns the formatted string output
3333
*/
3434
function daysAgo(date: Date) {
35-
const days = Math.floor((new Date().getTime() - date.getTime()) / 1000 / 60 / 60 / 24);
35+
const days = Math.floor((Date.now() - date.getTime()) / 1000 / 60 / 60 / 24);
3636
return new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-days, "day");
3737
}
3838

0 commit comments

Comments
 (0)