Skip to content

Commit

Permalink
setTTL and getTTL to manage ttl dynamically (#3)
Browse files Browse the repository at this point in the history
* setTTL and getTTL to manage ttl dynamically

* Update src/fineGrained.ts
  • Loading branch information
PabloSzx authored Jun 12, 2022
1 parent 13d7918 commit 44af92c
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/red-insects-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@soundxyz/fine-grained-cache": minor
---

setTTL and getTTL to manage ttl dynamically
57 changes: 45 additions & 12 deletions src/fineGrained.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ function ConcurrentCachedCall<T>(key: string, cb: () => Promise<T>) {
}) as Promise<Awaited<T>>;
}

export type CachedCallback<T> = (options: {
setTTL(options: {
/**
* Set TTL to `null` to disable caching
*/
ttl?: StringValue | "Infinity" | null;
timedInvalidation?: null | Date | (() => Date | Promise<Date>);
}): void;
getTTL(): {
ttl: StringValue | "Infinity" | null;
timedInvalidation: undefined | null | Date | (() => Date | Promise<Date>);
};
}) => T;

export function FineGrainedCache({
redis,
redLock: redLockConfig,
Expand All @@ -54,17 +68,16 @@ export function FineGrainedCache({
/**
* @default false
*/
useByDefault?: boolean
useByDefault?: boolean;
};
keyPrefix?: string;
memoryCache?: MemoryCache<unknown>;
onError?: (err: unknown) => void;

}) {
const redLock = redLockConfig?.client;
const defaultMaxExpectedTime = redLockConfig?.maxExpectedTime || "5 seconds";
const defaultRetryLockTime = redLockConfig?.retryLockTime || "250 ms";
const useRedlockByDefault = redLockConfig?.useByDefault ?? false
const useRedlockByDefault = redLockConfig?.useByDefault ?? false;

function generateCacheKey(keys: string | [string, ...(string | number)[]]) {
return (
Expand Down Expand Up @@ -100,7 +113,7 @@ export function FineGrainedCache({
}

function getCached<T>(
cb: () => T,
cb: CachedCallback<T>,
{
timedInvalidation,
ttl,
Expand Down Expand Up @@ -204,18 +217,38 @@ export function FineGrainedCache({
}

async function getNewValue() {
const newValue = await cb();
let currentTTL: typeof ttl | null = ttl;
let currentTimedInvalidation: typeof timedInvalidation | null = timedInvalidation;

let expirySeconds: number = 1;

const newValue = await cb({
setTTL(options) {
currentTTL = options.ttl !== undefined ? options.ttl : currentTTL;
currentTimedInvalidation =
options.timedInvalidation !== undefined
? options.timedInvalidation
: currentTimedInvalidation;
},
getTTL() {
return {
ttl: currentTTL,
timedInvalidation: currentTimedInvalidation,
};
},
});

try {
const timedInvalidationDate = timedInvalidation
? typeof timedInvalidation === "function"
? await timedInvalidation()
: timedInvalidation
const timedInvalidationDate = currentTimedInvalidation
? typeof currentTimedInvalidation === "function"
? await currentTimedInvalidation()
: currentTimedInvalidation
: null;

const ttlSeconds = ttl === "Infinity" ? -1 : getExpirySeconds(ttl);
const ttlSeconds =
currentTTL == null ? 0 : currentTTL === "Infinity" ? -1 : getExpirySeconds(currentTTL);

const expirySeconds =
expirySeconds =
timedInvalidationDate && timedInvalidationDate.getTime() > Date.now()
? getRemainingSeconds(timedInvalidationDate)
: ttlSeconds;
Expand All @@ -234,7 +267,7 @@ export function FineGrainedCache({
onError(err);
}

if (checkShortMemoryCache) memoryCache.set(key, newValue);
if (expirySeconds > 0 && checkShortMemoryCache) memoryCache.set(key, newValue);

return newValue;
}
Expand Down
54 changes: 54 additions & 0 deletions test/fineGrained.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import test from "ava";
import { join } from "path";
import type { CachedCallback } from "../src";

import { getCached, invalidateCache, memoryCache, redis } from "./utils";

Expand Down Expand Up @@ -320,3 +321,56 @@ test("fine grained - forceUpdate", async (t) => {
t.is(data5, "hello world3");
t.is(calls, 3);
});

test("fine grained - dynamic ttl", async (t) => {
let calls = 0;

const cb: CachedCallback<unknown> = async function cb({ setTTL, getTTL }) {
++calls;
await new Promise((resolve) => setTimeout(resolve, 50));

t.deepEqual(getTTL(), {
ttl: "10 seconds",
timedInvalidation: undefined,
});

const tempTimedInvalidation = new Date();
setTTL({
ttl: "1 hour",
timedInvalidation: tempTimedInvalidation,
});

t.deepEqual(getTTL(), {
ttl: "1 hour",
timedInvalidation: tempTimedInvalidation,
});

setTTL({
ttl: null,
timedInvalidation: null,
});

t.deepEqual(getTTL(), {
ttl: null,
timedInvalidation: null,
});

return "hello world" + calls;
};

const data = await getCached(cb, {
keys: "test",
ttl: "10 seconds",
});

t.is(data, "hello world1");
t.is(calls, 1);

const data2 = await getCached(cb, {
keys: "test",
ttl: "10 seconds",
});

t.is(data2, "hello world2");
t.is(calls, 2);
});

0 comments on commit 44af92c

Please sign in to comment.