Skip to content

Commit

Permalink
Cacheable - adding in get raw, get many raw, set many, get many, and …
Browse files Browse the repository at this point in the history
…delete many to cacheable memory (#814)

* export {CacheableStoreItem} from './cacheable-item-types.js';

* adding in setMany

* adding in getManyRaw

* node-cache - ttl handling for NodeCacheStore

* adding in getMany

* deleteMany added

* takeMany added

* updating KeyvCacheableMemory

* version bump to 1.7.1
  • Loading branch information
jaredwray authored Sep 28, 2024
1 parent c74e0e6 commit d3b5a43
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 62 deletions.
9 changes: 8 additions & 1 deletion packages/cacheable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,20 @@ By default we use lazy expiration deletion which means on `get` and `getMany` ty
### CacheableMemory API

* `set(key, value, ttl?)`: Sets a value in the cache.
* `setMany([{key, value, ttl?}])`: Sets multiple values in the cache from `CachableItem`.
* `get(key)`: Gets a value from the cache.
* `getMany([keys])`: Gets multiple values from the cache.
* `getRaw(key)`: Gets a value from the cache as `CacheableStoreItem`.
* `getManyRaw([keys])`: Gets multiple values from the cache as `CacheableStoreItem`.
* `has(key)`: Checks if a value exists in the cache.
* `delete(key)`: Deletes a value from the cache.
* `deleteMany([keys])`: Deletes multiple values from the cache.
* `take(key)`: Takes a value from the cache and deletes it.
* `takeMany([keys])`: Takes multiple values from the cache and deletes them.
* `clear()`: Clears the cache.
* `size()`: The number of keys in the cache.
* `keys()`: The keys in the cache.
* `items()`: The items in the cache as `{ key, value, expires? }`.
* `items()`: The items in the cache as `CacheableStoreItem` example `{ key, value, expires? }`.
* `checkExpired()`: Checks for expired keys in the cache. This is used by the `checkInterval` property.
* `startIntervalCheck()`: Starts the interval check for expired keys if `checkInterval` is above 0 ms.
* `stopIntervalCheck()`: Stops the interval check for expired keys.
Expand Down
2 changes: 1 addition & 1 deletion packages/cacheable/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cacheable",
"version": "1.7.0",
"version": "1.7.1",
"description": "Simple Caching Engine using Keyv",
"type": "module",
"main": "./dist/index.cjs",
Expand Down
12 changes: 12 additions & 0 deletions packages/cacheable/src/cacheable-item-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

export type CacheableItem = {
key: string;
value: any;
ttl?: number | string;
};

export type CacheableStoreItem = {
key: string;
value: any;
expires?: number;
};
8 changes: 2 additions & 6 deletions packages/cacheable/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Hookified} from 'hookified';
import {shorthandToMilliseconds} from './shorthand-time.js';
import {KeyvCacheableMemory} from './keyv-memory.js';
import {CacheableStats} from './stats.js';
import {type CacheableItem} from './cacheable-item-types.js';

export enum CacheableHooks {
BEFORE_SET = 'BEFORE_SET',
Expand All @@ -19,12 +20,6 @@ export enum CacheableEvents {
ERROR = 'error',
}

export type CacheableItem = {
key: string;
value: unknown;
ttl?: number | string;
};

export type CacheableOptions = {
primary?: Keyv | KeyvStoreAdapter;
secondary?: Keyv | KeyvStoreAdapter;
Expand Down Expand Up @@ -424,3 +419,4 @@ export {CacheableStats} from './stats.js';
export {CacheableMemory} from './memory.js';
export {KeyvCacheableMemory} from './keyv-memory.js';
export {shorthandToMilliseconds, shorthandToTime} from './shorthand-time.js';
export type {CacheableItem} from './cacheable-item-types.js';
35 changes: 16 additions & 19 deletions packages/cacheable/src/keyv-memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,30 @@ export class KeyvCacheableMemory implements KeyvStoreAdapter {
return undefined;
}

set(key: string, value: any, ttl?: number) {
async getMany<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>> {
const result = this._cache.getMany(keys);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return result;
}

async set(key: string, value: any, ttl?: number): Promise<void> {
this._cache.set(key, value, ttl);
}

async setMany(values: Array<{key: string; value: any; ttl?: number}>): Promise<void> {
this._cache.setMany(values);
}

async delete(key: string): Promise<boolean> {
this._cache.delete(key);
return true;
}

async deleteMany?(key: string[]): Promise<boolean> {
this._cache.deleteMany(key);
return true;
}

async clear(): Promise<void> {
this._cache.clear();
}
Expand All @@ -46,24 +61,6 @@ export class KeyvCacheableMemory implements KeyvStoreAdapter {
return this._cache.has(key);
}

async getMany?<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>> {
const result = [];
for (const key of keys) {
result.push(this._cache.get(key));
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return result;
}

async deleteMany?(key: string[]): Promise<boolean> {
for (const k of key) {
this._cache.delete(k);
}

return true;
}

on(event: string, listener: (...arguments_: any[]) => void): this {
return this;
}
Expand Down
78 changes: 57 additions & 21 deletions packages/cacheable/src/memory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {DoublyLinkedList} from './memory-lru.js';
import {shorthandToTime} from './shorthand-time.js';
import {type CacheableStoreItem, type CacheableItem} from './cacheable-item-types.js';

export type CacheableMemoryOptions = {
ttl?: number | string;
Expand All @@ -8,24 +9,18 @@ export type CacheableMemoryOptions = {
checkInterval?: number;
};

export type CacheableItem = {
key: string;
value: any;
expires?: number;
};

export class CacheableMemory {
private readonly _hashCache = new Map<string, number>();
private readonly _hash0 = new Map<string, CacheableItem>();
private readonly _hash1 = new Map<string, CacheableItem>();
private readonly _hash2 = new Map<string, CacheableItem>();
private readonly _hash3 = new Map<string, CacheableItem>();
private readonly _hash4 = new Map<string, CacheableItem>();
private readonly _hash5 = new Map<string, CacheableItem>();
private readonly _hash6 = new Map<string, CacheableItem>();
private readonly _hash7 = new Map<string, CacheableItem>();
private readonly _hash8 = new Map<string, CacheableItem>();
private readonly _hash9 = new Map<string, CacheableItem>();
private readonly _hash0 = new Map<string, CacheableStoreItem>();
private readonly _hash1 = new Map<string, CacheableStoreItem>();
private readonly _hash2 = new Map<string, CacheableStoreItem>();
private readonly _hash3 = new Map<string, CacheableStoreItem>();
private readonly _hash4 = new Map<string, CacheableStoreItem>();
private readonly _hash5 = new Map<string, CacheableStoreItem>();
private readonly _hash6 = new Map<string, CacheableStoreItem>();
private readonly _hash7 = new Map<string, CacheableStoreItem>();
private readonly _hash8 = new Map<string, CacheableStoreItem>();
private readonly _hash9 = new Map<string, CacheableStoreItem>();
private readonly _lru = new DoublyLinkedList<string>();

private _ttl: number | string | undefined; // Turned off by default
Expand Down Expand Up @@ -95,13 +90,13 @@ export class CacheableMemory {
return this.concatStores().keys();
}

public get items(): IterableIterator<CacheableItem> {
public get items(): IterableIterator<CacheableStoreItem> {
return this.concatStores().values();
}

public get<T>(key: string): any {
const store = this.getStore(key);
const item = store.get(key) as CacheableItem;
const item = store.get(key) as CacheableStoreItem;
if (!item) {
return undefined;
}
Expand All @@ -120,9 +115,18 @@ export class CacheableMemory {
return this.clone(item.value) as T;
}

public getRaw(key: string): CacheableItem | undefined {
public getMany<T>(keys: string[]): any[] {
const result = new Array<any>();
for (const key of keys) {
result.push(this.get(key) as T);
}

return result;
}

public getRaw(key: string): CacheableStoreItem | undefined {
const store = this.getStore(key);
const item = store.get(key) as CacheableItem;
const item = store.get(key) as CacheableStoreItem;
if (!item) {
return undefined;
}
Expand All @@ -136,6 +140,15 @@ export class CacheableMemory {
return item;
}

public getManyRaw(keys: string[]): Array<CacheableStoreItem | undefined> {
const result = new Array<CacheableStoreItem | undefined>();
for (const key of keys) {
result.push(this.getRaw(key));
}

return result;
}

public set(key: string, value: any, ttl?: number | string): void {
const store = this.getStore(key);
let expires;
Expand Down Expand Up @@ -170,6 +183,12 @@ export class CacheableMemory {
});
}

public setMany(items: CacheableItem[]): void {
for (const item of items) {
this.set(item.key, item.value, item.ttl);
}
}

public has(key: string): boolean {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const item = this.get(key);
Expand All @@ -187,11 +206,26 @@ export class CacheableMemory {
return item as T;
}

public takeMany<T>(keys: string[]): any[] {
const result = new Array<any>();
for (const key of keys) {
result.push(this.take(key) as T);
}

return result;
}

public delete(key: string): void {
const store = this.getStore(key);
store.delete(key);
}

public deleteMany(keys: string[]): void {
for (const key of keys) {
this.delete(key);
}
}

public clear(): void {
this._hash0.clear();
this._hash1.clear();
Expand Down Expand Up @@ -349,7 +383,7 @@ export class CacheableMemory {
return result;
}

private concatStores(): Map<string, CacheableItem> {
private concatStores(): Map<string, CacheableStoreItem> {
const result = new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]);
return result;
}
Expand All @@ -364,3 +398,5 @@ export class CacheableMemory {
}
}
}

export type {CacheableItem, CacheableStoreItem} from './cacheable-item-types.js';
2 changes: 1 addition & 1 deletion packages/cacheable/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ describe('cacheable ttl parsing', async () => {
await cacheable.setMany(list);
const firstResult = await cacheable.getMany(['key1', 'key2']);
expect(firstResult).toEqual(['value1', 'value2']);
await sleep(3);
await sleep(5);
const result = await cacheable.getMany(['key1', 'key2']);
expect(result).toEqual(['value1', undefined]);
});
Expand Down
6 changes: 6 additions & 0 deletions packages/cacheable/test/keyv-memory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,10 @@ describe('Keyv Cacheable Memory', () => {
const value = await keyv.get('key');
expect(value).toBe(undefined);
});
test('should set many values in keyv cacheable memory', async () => {
const keyvCacheableMemory = new KeyvCacheableMemory();
await keyvCacheableMemory.setMany([{key: 'key', value: 'value'}, {key: 'key1', value: 'value1'}]);
const value = await keyvCacheableMemory.get('key1');
expect(value).toBe('value1');
});
});
Loading

0 comments on commit d3b5a43

Please sign in to comment.