Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support Redis 4 client #22

Merged
merged 7 commits into from
Dec 13, 2024
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
12 changes: 10 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
{
"extends": ["@acuris"]
}
"extends": [
"@acuris"
],
"parserOptions": {
"project": [
"./tsconfig.json",
"./tsconfig-test.json"
]
}
}
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
node-version: [20.x]
services:
redis:
image: redis
ports:
ports:
- 6379:6379
steps:
- uses: actions/checkout@v1
Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@
"**/.fuse_hidden*": true
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
}
}
11,073 changes: 7,227 additions & 3,846 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@acuris/leprechaun-cache",
"version": "0.0.12",
"version": "0.1.0",
"private": false,
"description": "Caching library that supports double checked caching and stale returns to avoid stampede and slow responses",
"keywords": [
Expand All @@ -17,7 +17,7 @@
"url": "https://github.com/mergermarket/leprechaun-cache.git"
},
"engines": {
"node": ">=10.15.0"
"node": ">=20.15.0"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -40,7 +40,7 @@
]
},
"dependencies": {
"@types/redis": "^2.8.14",
"redis": "^4.7.0",
"uuid": "^3.3.3"
},
"devDependencies": {
Expand Down Expand Up @@ -68,7 +68,6 @@
"lint-staged": "^9.4.2",
"mocha": "^6.2.2",
"prettier": "^1.19.1",
"redis": "^2.8.0",
"sinon": "^7.5.0",
"sinon-chai": "^3.3.0",
"ts-mocha": "^6.0.0",
Expand Down
86 changes: 24 additions & 62 deletions src/storage/redis-cache-store.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,44 @@
import { CacheStore, CacheItem, Cacheable } from '../types'
import { RedisClient } from 'redis'
import { RedisClientType } from 'redis'
import { v4 as uuidV4 } from 'uuid'

function lockKey(key: string): string {
return `LOCK-${key}`
}

export class RedisCacheStore<T extends Cacheable = Cacheable> implements CacheStore<T> {
public constructor(private redisClient: RedisClient) {}
public constructor(private redisClient: RedisClientType<any>) {}

public get(key: string): Promise<CacheItem<T> | null> {
return new Promise<CacheItem<T> | null>((resolve, reject) => {
this.redisClient.get(key, (error, result) => {
if (error) {
reject(error)
}
if (!result) {
resolve(null)
}
resolve(JSON.parse(result))
})
})
public async get(key: string): Promise<CacheItem<T> | null> {
const result = await this.redisClient.get(key)
return JSON.parse(result)
}

public set(key: string, data: CacheItem<T>, ttl: number): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
this.redisClient.set(key, JSON.stringify(data), 'PX', ttl, (error, result) => {
if (error) {
reject(error)
}
if (result === 'OK') {
resolve(true)
}
resolve(false)
})
})
public async set(key: string, data: CacheItem<T>, ttl: number): Promise<boolean> {
const result = await this.redisClient.set(key, JSON.stringify(data), { PX: ttl })
return result === 'OK'
}

public del(key: string): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
this.redisClient.del(key, (error, res) => {
if (error) {
reject(error)
}
resolve(res > 0)
})
})
public async del(key: string): Promise<boolean> {
const result = await this.redisClient.del(key)
return result > 0
}

public lock(key: string, ttl: number): Promise<string | false> {
public async lock(key: string, ttl: number): Promise<string | false> {
const lockId = uuidV4()
return new Promise<string | false>((resolve, reject) => {
this.redisClient.set(lockKey(key), lockId, 'PX', ttl, 'NX', (error, result) => {
if (error) {
reject(error)
}
if (result === 'OK') {
resolve(lockId)
}
resolve(false)
})
})
const result = await this.redisClient.set(lockKey(key), lockId, { PX: ttl, NX: true })
if (result === 'OK') {
return lockId
}
return false
}

public unlock(key: string, lockId: string): Promise<boolean> {
return new Promise((resolve, reject) => {
this.redisClient.get(lockKey(key), (error, result) => {
if (!error && result && result === lockId) {
this.redisClient.del(lockKey(key), err => {
if (err) {
reject(err)
}
resolve(true)
})
} else {
resolve(false)
}
})
})
public async unlock(key: string, lockId: string): Promise<boolean> {
const result = await this.redisClient.get(lockKey(key))
if (result && result === lockId) {
await this.redisClient.del(lockKey(key))
return true
}
return false
}
}
14 changes: 7 additions & 7 deletions test/integration/leprechaun-cache-redis.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { RedisCacheStore, LeprechaunCache } from '../../src'
import * as chai from 'chai'
import * as sinon from 'sinon'
import sinonChai from 'sinon-chai'
import { RedisClient } from 'redis'
import { createClient, RedisClientType } from 'redis'

chai.use(sinonChai)
const expect = chai.expect
const redisClient = new RedisClient({})
const redisClient = createClient() as RedisClientType<any>

const cacheStore = new RedisCacheStore(redisClient)

function delay(durationMs: number): Promise<void> {
Expand All @@ -20,13 +21,12 @@ function delay(durationMs: number): Promise<void> {

describe('Leprechaun Cache (integration)', () => {
const sandbox = sinon.sandbox.create()
before(async () => {
await redisClient.connect()
})

beforeEach(async () => {
await new Promise(resolve => {
redisClient.FLUSHALL(() => {
resolve()
})
})
await redisClient.FLUSHALL()
})

afterEach(() => {
Expand Down
11 changes: 11 additions & 0 deletions tsconfig-test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "@acuris/eslint-config/tsconfig.json",
"compilerOptions": {
"noEmit": false,
"module": "commonjs",
"target": "es2017",
"declaration": true,
"outDir": "./dist-test"
},
"include": ["test/**/*"]
}
Loading