Skip to content

Commit 3f74347

Browse files
authored
Merge pull request #75 from Cimpress-MCP/mh/FixConnectToRedis
Release 7.0.6
2 parents 1133a6e + c6eb1b3 commit 3f74347

4 files changed

Lines changed: 72 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66

7-
## [7.0.5] - 2025-11-14
7+
## [7.0.6] - 2026-01-19
8+
9+
### Changed
10+
11+
- Switch back to redis, fix concurrent redis connection issues
12+
13+
## [7.0.5] - 2026-01-14
814

915
### Changed
1016

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "lambda-essentials-ts",
3-
"version": "7.0.5",
3+
"version": "7.0.6",
44
"description": "A selection of the finest modules supporting authorization, API routing, error handling, logging and sending HTTP requests.",
55
"main": "lib/index.js",
66
"private": false,
@@ -80,6 +80,6 @@
8080
},
8181
"peerDependencies": {
8282
"newrelic": "^12.0.0",
83-
"ioredis": "^5.9.1"
83+
"redis": "5.10.0"
8484
}
8585
}

src/httpClient/redisStorage.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,36 @@
11
import { buildStorage, canStale } from 'axios-cache-interceptor';
22
import type { StorageValue } from 'axios-cache-interceptor';
3+
// eslint-disable-next-line import/no-extraneous-dependencies
4+
// @ts-ignore
5+
import { createClient } from 'redis';
36

47
const KEY_PREFIX = 'axios-cache-';
58

69
const MIN_TTL = 60000;
710

8-
export default function createRedisStorage(redisEndpoint: string) {
9-
// eslint-disable-next-line import/no-extraneous-dependencies
10-
const Redis = require('ioredis');
11+
export default function createRedisStorage(client: ReturnType<typeof createClient>) {
12+
const connectionPromise: Promise<any> = client.connect();
1113

12-
const client = new Redis(redisEndpoint);
14+
const connectIfNeeded = async () => {
15+
if (client.isReady) {
16+
return;
17+
}
18+
19+
await connectionPromise;
20+
};
1321

1422
// source https://axios-cache-interceptor.js.org/guide/storages#node-redis-storage
1523
return buildStorage({
1624
async find(key) {
25+
await connectIfNeeded();
1726
const result = await client.get(`${KEY_PREFIX}${key}`);
1827
return result ? (JSON.parse(result) as StorageValue) : undefined;
1928
},
2029

2130
// eslint-disable-next-line complexity
2231
async set(key, value, req) {
32+
await connectIfNeeded();
33+
2334
await client.set(`${KEY_PREFIX}${key}`, JSON.stringify(value), {
2435
PXAT:
2536
// We don't want to keep indefinitely values in the storage if
@@ -38,6 +49,7 @@ export default function createRedisStorage(redisEndpoint: string) {
3849
},
3950

4051
async remove(key) {
52+
await connectIfNeeded();
4153
await client.del(`${KEY_PREFIX}${key}`);
4254
},
4355
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
jest.mock(
2+
'redis',
3+
() => ({
4+
createClient: jest.fn(),
5+
}),
6+
{ virtual: true },
7+
);
8+
9+
import createRedisStorage from '../../src/httpClient/redisStorage';
10+
11+
const mClient = {
12+
connect: jest.fn().mockImplementation(async () => {
13+
await new Promise((resolve) => setTimeout(resolve, 50));
14+
mClient.isReady = true;
15+
}),
16+
get: jest.fn().mockResolvedValue(null),
17+
set: jest.fn().mockResolvedValue('OK'),
18+
del: jest.fn().mockResolvedValue(1),
19+
isReady: false,
20+
on: jest.fn(),
21+
};
22+
23+
describe('redisStorage', () => {
24+
beforeEach(() => {
25+
jest.clearAllMocks();
26+
mClient.isReady = false;
27+
});
28+
29+
it('should connect when not ready', async () => {
30+
const storage = createRedisStorage(mClient as any);
31+
mClient.isReady = false;
32+
33+
await storage.get('key');
34+
35+
expect(mClient.connect).toHaveBeenCalledTimes(1);
36+
expect(mClient.isReady).toBe(true);
37+
});
38+
39+
it('should connect only once when multiple requests are made', async () => {
40+
mClient.isReady = false;
41+
const storage = createRedisStorage(mClient as any);
42+
43+
await Promise.all([storage.get('key1'), storage.get('key2')]);
44+
45+
expect(mClient.connect).toHaveBeenCalledTimes(1);
46+
});
47+
});

0 commit comments

Comments
 (0)