Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/.env.development
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ DATACITE_PASSWORD=""
DATACITE_API_URL="https://api.test.datacite.org"

GCLOUD_KEY_FILE='xxx'
VALKEY_URL='redis://cache:6379'
VALKEY_URL='redis://localhost:6379'
103 changes: 94 additions & 9 deletions core/cache-handler.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,96 @@
/**
* Solution taken from here: https://github.com/vercel/next.js/discussions/48324#discussioncomment-10542097
*
* We are reexporting `next.js`'s default cache handler to get around
* the fixed 2mb limit for cached fetches.
* We run into this limit somewhat quickly if we fetch more than 100 pubs.
*/
// Based on https://github.com/fortedigital/nextjs-cache-handler#full-example

import FileSystemCache from "next/dist/server/lib/incremental-cache/file-system-cache.js";
import { PHASE_PRODUCTION_BUILD } from "next/constants.js";
import createBufferStringHandler from "@fortedigital/nextjs-cache-handler/buffer-string-decorator";
import { Next15CacheHandler } from "@fortedigital/nextjs-cache-handler/next-15-cache-handler";
import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings";
import { CacheHandler } from "@neshca/cache-handler";
import { createClient } from "redis";

export default FileSystemCache;
// A cache that always misses - intended to let us disable caching when redis is unavailable. Should
// be replaced if there's a better way to do that.
const dummyHandler = {
name: "no-cache",
get: () => undefined,
set: () => undefined,
revalidateTag: () => undefined,
};

// Usual onCreation from @neshca/cache-handler
CacheHandler.onCreation(() => {
// Important - It's recommended to use global scope to ensure only one Redis connection is made
// This ensures only one instance get created
if (global.cacheHandlerConfig) {
return global.cacheHandlerConfig;
}

// Important - It's recommended to use global scope to ensure only one Redis connection is made
// This ensures new instances are not created in a race condition
if (global.cacheHandlerConfigPromise) {
return global.cacheHandlerConfigPromise;
}

// Main promise initializing the handler
global.cacheHandlerConfigPromise = (async () => {
console.info("Getting cache handler");
/** @type {import("redis").RedisClientType | null} */
let redisClient = null;
if (PHASE_PRODUCTION_BUILD !== process.env.NEXT_PHASE) {
try {
redisClient = createClient({
url: process.env.VALKEY_URL,
pingInterval: 10000,
});
redisClient.on("error", (e) => {
console.warn("Redis error", e);
console.warn("Disabling caching");
global.cacheHandlerConfig = { handlers: [dummyHandler] };
global.cacheHandlerConfigPromise = null;
throw e;
Comment thread
kalilsn marked this conversation as resolved.
Outdated
});
} catch (error) {
console.error("Failed to create Redis client:", error);
}
}

if (redisClient) {
try {
console.info("Connecting Redis client...");
await redisClient.connect();
console.info("Redis client connected.");
} catch (error) {
console.warn("Failed to connect Redis client:", error);
await redisClient
.disconnect()
.catch(() =>
console.warn("Failed to quit the Redis client after failing to connect.")
);
}
}

if (!redisClient?.isReady) {
console.error("Failed to initialize caching layer.");
global.cacheHandlerConfigPromise = null;
global.cacheHandlerConfig = { handlers: [dummyHandler] };
return global.cacheHandlerConfig;
}

const redisCacheHandler = createRedisHandler({
client: redisClient,
keyPrefix: "nextjs:",
keyExpirationStrategy: "EXAT",
});

global.cacheHandlerConfigPromise = null;

global.cacheHandlerConfig = {
handlers: [createBufferStringHandler(redisCacheHandler)],
};

return global.cacheHandlerConfig;
})();

return global.cacheHandlerConfigPromise;
});

export default new Next15CacheHandler();
5 changes: 5 additions & 0 deletions core/instrumentation.node.mts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ if (env.NODE_ENV === "production") {

// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
integrations: [
Sentry.redisIntegration({
cachePrefixes: ["nextjs:"],
}),
],
});
logger.info("✅ Successfully instrumented Sentry");
}
Expand Down
3 changes: 3 additions & 0 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,13 @@
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@faker-js/faker": "^9.0.0",
"@fortedigital/nextjs-cache-handler": "^1.2.0",
"@googleapis/drive": "^8.16.0",
"@handlewithcare/react-prosemirror": "catalog:",
"@honeycombio/opentelemetry-node": "catalog:",
"@hookform/resolvers": "catalog:",
"@icons-pack/react-simple-icons": "^10.2.0",
"@neshca/cache-handler": "^1.9.0",
"@nimpl/getters": "^2.0.0",
"@node-rs/argon2": "^1.8.3",
"@opentelemetry/auto-instrumentations-node": "catalog:",
Expand Down Expand Up @@ -125,6 +127,7 @@
"react-hook-form": "catalog:",
"react-markdown": "^9.0.1",
"reactflow": "^11.10.4",
"redis": "^4.7.0",
"rehype": "^13.0.2",
"rehype-format": "^5.0.0",
"rehype-parse": "^9.0.1",
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ services:
service: cache
networks:
- app-network
ports:
- 6379:6379

minio-init:
env_file:
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
"dev:inbucket:stop": "docker compose -f docker-compose.dev.yml down inbucket",
"dev:minio:start": "docker compose -f docker-compose.dev.yml up minio -d && docker compose -f docker-compose.dev.yml run minio-init",
"dev:minio:stop": "docker compose -f docker-compose.dev.yml down minio",
"dev:cache:start": "docker compose -f docker-compose.dev.yml up cache -d",
"dev:cache:stop": "docker compose -f docker-compose.dev.yml down cache",
"dev:setup": "pnpm install && docker compose -f docker-compose.dev.yml up -d && pnpm p:dev && pnpm --filter core migrate-dev && pnpm --filter core reset",
"dev:teardown": "docker compose -f docker-compose.dev.yml down -v",
"integration:setup": "docker compose -f docker-compose.test.yml --profile integration up -d",
Expand Down
Loading
Loading