Skip to content

Commit

Permalink
feat: Static tags can be lazy calculated
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkvak committed Jan 23, 2020
1 parent 57bdff3 commit 6965862
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 15 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ The library is designed to cache query results. Features:
To initialize Cache instance, you need:
* StorageAdapter (in the example below, an adapter for connecting to redis). RedisStorageAdapter takes as an argument the instance of ioredis client.
* Settings object. The options are the following options:
  

- prefix - prefix used by CacheManager for storing keys. In essence, this is the namespace for a specific CacheManager.
  

- logger - instance of logger. Must implement following interface:

```typescript
Expand Down Expand Up @@ -73,7 +73,7 @@ function getSomething() {
`get` will check the tags and compare their versions with the current date, runs an executor if necessary and returns result.
Options for `get`:
- expiresIn?: number; - The number of milliseconds after which key values are considered expired
- tags?: string[] - Tags - keys for which checks the validity of a particular record. If the tag value in the cache + invalidation time is <the current time, then the tag will be considered invalid and the record will need to be obtained using the executor
- tags?: string[] | (() => string[]) - Tags - keys for which checks the validity of a particular record. If the tag value in the cache + invalidation time is <the current time, then the tag will be considered invalid and the record will need to be obtained using the executor. Can be calculated lazy.
- getTags?: (executorResult) => string[] function which extracts tags from executor result. These tags will be merged with tags given in option below.

The next method, "touch", serves to invalidate tags. Calling this method with one of the tags will make all records in the cache with this tag invalid.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"watch": "tsc -w",
"lint": "tslint --project . \"src/**/*.ts\"",
"check": "npm run lint && npm run test:unit",
"test:unit": "jest --forceExit --coverage --verbose --detectOpenHandles --passWithNoTests",
"test": "npm run test:unit",
"test:unit": "jest --coverage --verbose --passWithNoTests",
"test:ci": "npm run test:unit -- --coverageReporters=text-lcov | coveralls",
"test:unit:watch": "jest --watch",
"prepublishOnly": "npm run check && npm run build",
Expand Down
13 changes: 6 additions & 7 deletions src/adapters/redis/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { KeyType, Redis } from 'ioredis';
import { Redis } from 'ioredis';
import { ConnectionStatus } from '../../connection-status';
import { StorageAdapter } from '../../storage-adapter';
import { withTimeout } from '../../with-timeout';
Expand Down Expand Up @@ -70,14 +70,13 @@ export class RedisStorageAdapter implements StorageAdapter {
* Use set command to set hash value in radish
*/
public async set(key: string, value: string, expiresIn?: number): Promise<boolean> {
const commands: [KeyType, CommandArgument] = [`${CACHE_PREFIX}:${key}`, value];
const cacheKey = `${CACHE_PREFIX}:${key}`;

if (expiresIn) {
commands.push('PX', expiresIn);
}
const setPromise = expiresIn ?
this.redisInstance.set(cacheKey, value, 'PX', expiresIn) :
this.redisInstance.set(cacheKey, value);

return Boolean(await withTimeout(
this.redisInstance.set(...commands), this.options.operationTimeout));
return Boolean(await withTimeout(setPromise, this.options.operationTimeout));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export interface WriteOptions extends ExpireOptions {
* If the tag value is in the cache and invalidation time < current time, the tag will be considered invalid and
* the record will need to be obtained using the executor
*/
tags?: string[];
tags?: string[] | (() => string[]);
/**
* getTags allows to detect tags for record depending on executor result
*/
Expand Down
19 changes: 17 additions & 2 deletions src/storages/base.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import timeout from '../timeout';
import { ConnectionStatus } from '../connection-status';
import TestStorageAdapter from '../adapters/test';
import { ConnectionStatus } from '../connection-status';
import timeout from '../timeout';
import { BaseStorage, TAGS_VERSIONS_ALIAS } from './base';

const testInterface = {
Expand Down Expand Up @@ -303,4 +303,19 @@ describe('BaseStorage', () => {
}
]);
});

it('set creates record with static tags calculated by function', async () => {
await storage.set('test', 'test', { tags: () => ['tag'] });

const value = JSON.parse(testInterface.internalStorage['cache-test']);

expect(value).toMatchObject({
key: 'test',
permanent: true,
value: '"test"'
});

expect(value.tags).toMatchObject([{ name: 'tag' }]);
expect(value.expiresIn).toEqual(expect.any(Number));
});
});
7 changes: 6 additions & 1 deletion src/storages/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,12 @@ export class BaseStorage implements Storage {
* set creates new record with provided options and sets it to storage using the adapter.
*/
public async set(key: string, value: StorageRecordValue, options: WriteOptions = {}): Promise<any> {
const tags: string[] = options.tags || [];
let tags: string[] = [];
if (isFunction(options.tags)) {
tags = options.tags();
} else if (options.tags !== undefined) {
tags = options.tags;
}
const dynamicTags = isFunction(options.getTags) ? options.getTags(value) : [];

if (!Array.isArray(dynamicTags)) {
Expand Down

0 comments on commit 6965862

Please sign in to comment.