Skip to content

Commit

Permalink
feat: cache content
Browse files Browse the repository at this point in the history
  • Loading branch information
sammarxz committed Oct 30, 2024
1 parent da60400 commit c97db37
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 2 deletions.
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
"manifest": "deno task cli manifest $(pwd)",
"start": "deno run --unstable-kv -A --watch=static/,routes/ dev.ts",
"build": "deno run --unstable-kv -A dev.ts build",
"build": "deno run -A scripts/warm-cache.ts && deno run -A --unstable-kv dev.ts build",
"preview": "deno run -A main.ts",
"update": "deno run -A -r https://fresh.deno.dev/update ."
},
Expand Down
3 changes: 2 additions & 1 deletion routes/[module]/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { TableOfContents } from "@/islands/TableOfContents.tsx";
import { PostNavigation } from "@/components/PostNavigation.tsx";
import Footer from "@/components/Footer.tsx";
import { MentorshipCard } from "@/components/MentorshipCard.tsx";
import { getCachedContent } from "@/utils/cache.ts";

export default defineRoute<State>(
async (_req, ctx) => {
Expand All @@ -36,7 +37,7 @@ export default defineRoute<State>(
)
: false;

const { html, headings } = await renderMarkdown(post.content);
const { html, headings } = await getCachedContent(post);

return (
<>
Expand Down
16 changes: 16 additions & 0 deletions scripts/warm-cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getPosts } from "@/utils/content/posts.ts";
import { warmContentCache } from "@/utils/cache.ts";

async function main() {
const posts = await getPosts();
await warmContentCache(posts.flatMap(module => module.posts));
}

if (import.meta.main) {
main()
.then(() => Deno.exit(0))
.catch((error) => {
console.error("Failed to warm cache:", error);
Deno.exit(1);
});
}
79 changes: 79 additions & 0 deletions utils/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// cache.ts
import { kv } from "@/utils/db.ts";
import { Post } from "@/utils/content/posts.ts";
import { renderMarkdown } from "@/utils/content/markdow.ts";

interface CachedContent {
html: string;
headings: Array<{level: number; text: string}>;
lastModified: number;
}

interface CachedPost extends Post {
cachedContent?: CachedContent;
}

const CACHE_PREFIX = "content_cache";
const CACHE_VERSION = "v1";
const CACHE_TTL = 1000 * 60 * 60 * 24; // 24 hours

export async function getCachedContent(post: Post): Promise<CachedContent> {
const cacheKey = [CACHE_PREFIX, CACHE_VERSION, post.moduleSlug, post.slug];

// Try to get from cache first
const cached = await kv.get<CachedContent>(cacheKey);

if (cached.value) {
// Check if cache is still valid
const now = Date.now();
if (now - cached.value.lastModified < CACHE_TTL) {
return cached.value;
}
}

// Cache miss or expired, generate new content
const { html, headings } = await renderMarkdown(post.content);
const newCache: CachedContent = {
html,
headings,
lastModified: Date.now(),
};

// Store in cache
await kv.set(cacheKey, newCache);

return newCache;
}

// Function to preload all content into cache
export async function warmContentCache(posts: Post[]) {
console.log("Warming content cache...");

const operations = posts.map(async (post) => {
try {
await getCachedContent(post);
console.log(`Cached ${post.moduleSlug}/${post.slug}`);
} catch (error) {
console.error(`Failed to cache ${post.moduleSlug}/${post.slug}:`, error);
}
});

await Promise.all(operations);
console.log("Content cache warming complete");
}

// Function to invalidate cache for a specific post
export async function invalidateCache(moduleSlug: string, postSlug: string) {
const cacheKey = [CACHE_PREFIX, CACHE_VERSION, moduleSlug, postSlug];
await kv.delete(cacheKey);
}

// Function to invalidate entire cache
export async function invalidateAllCache() {
const prefix = [CACHE_PREFIX, CACHE_VERSION];
const entries = kv.list({ prefix });

for await (const entry of entries) {
await kv.delete(entry.key);
}
}

0 comments on commit c97db37

Please sign in to comment.