Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
87 changes: 87 additions & 0 deletions src/__tests__/services/auditCacheWarm.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { warmAuditCache } from "../../services/auditCacheWarm";
import { logger } from "../../config/logger";
import { env } from "../../config/env";

jest.mock("../../config/logger", () => ({
logger: {
info: jest.fn(),
warn: jest.fn(),
},
}));

describe("auditCacheWarm", () => {
let originalFetch: typeof global.fetch;

beforeAll(() => {
originalFetch = global.fetch;
});

afterAll(() => {
global.fetch = originalFetch;
});

beforeEach(() => {
jest.clearAllMocks();
});

it("should warm the audit cache successfully", async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: true,
status: 200,
});

await warmAuditCache();

expect(global.fetch).toHaveBeenCalledWith(
`http://localhost:${env.PORT}/api/audit?limit=10`,
expect.objectContaining({
method: "GET",
headers: expect.objectContaining({
"x-audit-cache-warm": "true",
"x-correlation-id": expect.any(String),
}),
})
);

expect(logger.info).toHaveBeenCalledWith(
expect.objectContaining({ url: expect.any(String) }),
"Starting audit cache warm"
);

expect(logger.info).toHaveBeenCalledWith(
expect.objectContaining({ status: 200 }),
"Audit cache warm completed successfully"
);
});

it("should handle failed cache warm response", async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: false,
status: 500,
});

await warmAuditCache();

expect(logger.warn).toHaveBeenCalledWith(
expect.objectContaining({
err: expect.any(Error),
url: expect.any(String),
}),
"Audit cache warm failed"
);
});

it("should handle fetch error", async () => {
global.fetch = jest.fn().mockRejectedValue(new Error("Network Error"));

await warmAuditCache();

expect(logger.warn).toHaveBeenCalledWith(
expect.objectContaining({
err: expect.any(Error),
url: expect.any(String),
}),
"Audit cache warm failed"
);
});
});
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import { gracefulShutdown } from "./lifecycle/shutdown";
import { DrizzleWebhookStore } from "./services/drizzleWebhookStore";
import type { IWebhookDispatcher } from "./services/webhookDispatcher";
import type { WebhookStore } from "./services/webhookStore";
import { warmAuditCache } from "./services/auditCacheWarm";


const docsEnabled =
Expand Down Expand Up @@ -276,6 +277,10 @@ if (require.main === module) {
if (env.ENABLE_DOCS) {
logger.info(`Swagger UI available at http://localhost:${env.PORT}/docs`);
}

warmAuditCache().catch((err) => {
logger.error({ err }, "Unhandled error in warmAuditCache");
});
});

const handleShutdown = async (signal: string) => {
Expand Down
39 changes: 39 additions & 0 deletions src/services/auditCacheWarm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { env } from "../config/env";
import { logger } from "../config/logger";
import { v4 as uuidv4 } from "uuid";

/**
* Warms the /api/audit cache at startup.
* Makes an HTTP request to the local audit endpoint to ensure
* caching layers are primed, mitigating cold-cache latency spikes.
*/
export async function warmAuditCache(): Promise<void> {
const correlationId = uuidv4();
const url = `http://localhost:${env.PORT}/api/audit?limit=10`;

try {
logger.info({ correlationId, url }, "Starting audit cache warm");

// Make an internal request to our own server, using native fetch.
const response = await fetch(url, {
method: "GET",
headers: {
"x-correlation-id": correlationId,
"x-audit-cache-warm": "true",
},
// Timeout relatively short to prevent hanging indefinitely
signal: AbortSignal.timeout(5000),
});

if (!response.ok) {
throw new Error(`Failed to warm audit cache, status: ${response.status}`);
}

logger.info(
{ correlationId, status: response.status },
"Audit cache warm completed successfully",
);
} catch (err) {
logger.warn({ err, correlationId, url }, "Audit cache warm failed");
}
}