Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/benchmark/cache-get-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
generateAlphaNumeric,
getModuleName,
printToConsole,
} from "index.js";
} from "./src/index.js";

const bench = createBenchmark("Memory Cache Benchmark", 100000);

Expand Down
6 changes: 3 additions & 3 deletions packages/benchmark/memory-lru.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { CacheableMemory } from "@cacheable/memory";
import { createLRU } from "lru.min";
import QuickLRU from "quick-lru";
import {
createBenchmark,
generateAlphaNumeric,
getModuleName,
printToConsole,
} from "index.js";
import { createLRU } from "lru.min";
import QuickLRU from "quick-lru";
} from "./src/index.js";

const bench = createBenchmark("Memory LRU Benchmark", 100000);

Expand Down
4 changes: 2 additions & 2 deletions packages/benchmark/memory.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { CacheableMemory } from "@cacheable/memory";
import { BentoCache, bentostore } from "bentocache";
import { memoryDriver } from "bentocache/drivers/memory";
import NodeCache from "node-cache";
import {
createBenchmark,
generateAlphaNumeric,
getModuleName,
printToConsole,
} from "index.js";
import NodeCache from "node-cache";
} from "./src/index.js";

const bench = createBenchmark("Memory Benchmark", 100000);

Expand Down
4 changes: 2 additions & 2 deletions packages/benchmark/node-cache.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { NodeCache as CacheableNodeCache } from "@cacheable/node-cache";
import NodeCache from "node-cache";
import {
createBenchmark,
generateAlphaNumeric,
getModuleName,
printToConsole,
} from "index.js";
import NodeCache from "node-cache";
} from "./src/index.js";

const bench = createBenchmark("NodeCache Benchmark", 100000);

Expand Down
2 changes: 1 addition & 1 deletion packages/benchmark/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// biome-ignore-all lint/suspicious/noExplicitAny: benchmarks
import { faker } from "@faker-js/faker";
import { tinybenchPrinter } from "@monstermann/tinybench-pretty-printer";
import { ObjectGenerator } from "object-generator.js";
import { Bench } from "tinybench";
import pkg from "../package.json" with { type: "json" };
import { ObjectGenerator } from "./object-generator.js";

export function createBenchmark(name: string, iterations: number) {
const bench = new Bench({
Expand Down
1 change: 0 additions & 1 deletion packages/benchmark/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"target": "ESNext",
"module": "nodenext",
"moduleResolution": "nodenext" /* Specify how TypeScript looks up a file from a given module specifier. */,
"baseUrl": "./src" /* Specify the base directory to resolve non-relative module names. */,

/* Emit */
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
Expand Down
2 changes: 1 addition & 1 deletion packages/cache-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ See unit tests in [`test/del.test.ts`](./test/del.test.ts) for more information.

`mdel(keys: [key]): Promise<true>`

Delete multiple keys, an error will be throw on any failed.
Delete multiple keys, an error will be throw on any failed. Cache manager makes one bulk deletion call per store, allowing adapters with native multi-delete support (such as `@keyv/redis`) to batch the work into fewer network round trips.

```ts
await cache.mset([
Expand Down
9 changes: 6 additions & 3 deletions packages/cache-manager/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export type Events = {
error?: unknown;
}) => void;
del: (data: { key: string; error?: unknown }) => void;
mdel: (data: { keys: string[]; error?: unknown }) => void;
clear: (error?: unknown) => void;
refresh: <T>(data: { key: string; value: T; error?: unknown }) => void;
};
Expand Down Expand Up @@ -289,11 +290,13 @@ export const createCache = (options?: CreateCacheOptions): Cache => {

const mdel = async (keys: string[]) => {
try {
const promises: Array<Promise<boolean>> = [];
for (const key of keys) {
promises.push(...stores.map(async (store) => store.delete(key)));
if (keys.length === 0) {
eventEmitter.emit("mdel", { keys });
return true;
}

const promises = stores.map(async (store) => store.deleteMany(keys));
Comment thread
jaredwray marked this conversation as resolved.

if (nonBlocking) {
Promise.all(promises);
eventEmitter.emit("mdel", { keys });
Expand Down
85 changes: 81 additions & 4 deletions packages/cache-manager/test/mdel.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { faker } from "@faker-js/faker";
import { createKeyv } from "@keyv/redis";
import { Keyv } from "keyv";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createCache } from "../src/index.js";
Expand Down Expand Up @@ -33,6 +34,78 @@ describe("mdel", () => {
await expect(cache.get(list[2].key)).resolves.toEqual(list[2].value);
});

it("should delete every key with a single store call", async () => {
const cache = createCache({ stores: [keyv], nonBlocking: false });
await cache.mset(list);
const keys = list.map(({ key }) => key);
const deleteManyHandler = vi.spyOn(keyv, "deleteMany");

await expect(cache.mdel(keys)).resolves.toBe(true);

expect(deleteManyHandler).toHaveBeenCalledOnce();
expect(deleteManyHandler).toHaveBeenCalledWith(keys);
});

it("should call deleteMany once per store", async () => {
const secondKeyv = new Keyv();
const cache = createCache({
stores: [keyv, secondKeyv],
nonBlocking: false,
});
await cache.mset(list);
const keys = list.map(({ key }) => key);
const firstHandler = vi.spyOn(keyv, "deleteMany");
const secondHandler = vi.spyOn(secondKeyv, "deleteMany");

await cache.mdel(keys);

expect(firstHandler).toHaveBeenCalledOnce();
expect(firstHandler).toHaveBeenCalledWith(keys);
expect(secondHandler).toHaveBeenCalledOnce();
expect(secondHandler).toHaveBeenCalledWith(keys);
});

it("should not dispatch an empty key list to Redis", async () => {
const redis = createKeyv();
const cache = createCache({ stores: [redis], nonBlocking: false });
const deleteManyHandler = vi.spyOn(redis, "deleteMany");
const listener = vi.fn();
cache.on("mdel", listener);

try {
await expect(cache.mdel([])).resolves.toBe(true);
expect(deleteManyHandler).not.toHaveBeenCalled();
expect(listener).toHaveBeenCalledOnce();
expect(listener).toHaveBeenCalledWith({ keys: [] });
} finally {
await redis.disconnect();
}
});

it("should delete keys through the native Redis deleteMany", async () => {
const redis = createKeyv();
redis.throwOnErrors = true;
const cache = createCache({ stores: [redis], nonBlocking: false });

try {
await cache.mset(list);
const nativeDeleteManyHandler = vi.spyOn(redis.store, "deleteMany");
const keys = [list[0].key, list[1].key];

await expect(cache.mdel(keys)).resolves.toBe(true);

expect(nativeDeleteManyHandler).toHaveBeenCalledOnce();
await expect(redis.get(list.map(({ key }) => key))).resolves.toEqual([
undefined,
undefined,
list[2].value,
]);
} finally {
await Promise.allSettled(list.map(async ({ key }) => redis.delete(key)));
await redis.disconnect();
}
});

it("should work blocking", async () => {
let resolveDeleted: (value: boolean) => void = () => undefined;
const deletePromise = new Promise<boolean>((_resolve) => {
Expand All @@ -41,15 +114,17 @@ describe("mdel", () => {
const cache = createCache({ stores: [keyv], nonBlocking: false });
await cache.mset(list);

const delHandler = vi.spyOn(keyv, "delete").mockReturnValue(deletePromise);
const delHandler = vi
.spyOn(keyv, "deleteMany")
.mockReturnValue(deletePromise);
const deleteResolved = vi.fn();
const deleteRejected = vi.fn();
cache
.mdel(list.map(({ key }) => key))
.catch(deleteRejected)
.then(deleteResolved);

expect(delHandler).toBeCalledTimes(list.length);
expect(delHandler).toHaveBeenCalledOnce();

await sleep(200);

Expand All @@ -70,15 +145,17 @@ describe("mdel", () => {
const cache = createCache({ stores: [keyv], nonBlocking: true });
await cache.mset(list);

const delHandler = vi.spyOn(keyv, "delete").mockReturnValue(deletePromise);
const delHandler = vi
.spyOn(keyv, "deleteMany")
.mockReturnValue(deletePromise);
const deleteResolved = vi.fn();
const deleteRejected = vi.fn();
cache
.mdel(list.map(({ key }) => key))
.catch(deleteRejected)
.then(deleteResolved);

expect(delHandler).toBeCalledTimes(list.length);
expect(delHandler).toHaveBeenCalledOnce();

await sleep(1);

Expand Down
3 changes: 1 addition & 2 deletions packages/cache-manager/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */
"moduleResolution": "bundler", /* Specify how TypeScript looks up a file from a given module specifier. */

/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
Expand Down
5 changes: 2 additions & 3 deletions packages/flat-cache/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */
"moduleResolution": "bundler", /* Specify how TypeScript looks up a file from a given module specifier. */

/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
Expand All @@ -23,4 +22,4 @@
"ESNext", "DOM"
]
}
}
}
5 changes: 2 additions & 3 deletions packages/memory/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */
"moduleResolution": "bundler", /* Specify how TypeScript looks up a file from a given module specifier. */

/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
Expand All @@ -23,4 +22,4 @@
"ESNext", "DOM"
]
}
}
}
5 changes: 2 additions & 3 deletions packages/net/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */
"moduleResolution": "bundler", /* Specify how TypeScript looks up a file from a given module specifier. */

/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
Expand All @@ -23,4 +22,4 @@
"ESNext", "DOM"
]
}
}
}
2 changes: 1 addition & 1 deletion packages/node-cache/src/store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Stats, shorthandToMilliseconds } from "@cacheable/utils";
import { Hookified } from "hookified";
import type { NodeCacheStats, PartialNodeCacheItem } from "index.js";
import Keyv from "keyv";
import type { NodeCacheStats, PartialNodeCacheItem } from "./index.js";

export type NodeCacheStoreOptions<T> = {
/**
Expand Down
5 changes: 2 additions & 3 deletions packages/node-cache/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */
"moduleResolution": "bundler", /* Specify how TypeScript looks up a file from a given module specifier. */

/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
Expand All @@ -23,4 +22,4 @@
"ESNext", "DOM"
]
}
}
}