mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-27 15:58:48 +00:00
619ce40237
* build(deps): update dependency @redis/client to v6 | datasource | package | from | to | | ---------- | ------------- | ------ | ----- | | npm | @redis/client | 5.12.1 | 6.2.1 | * fix(cache): keep RESP 2 protocol with @redis/client v6 @redis/client v6 defaults to RESP 3, which requires Redis server 6.0 or newer and changed the default type parameters, breaking the RedisClient type alias. Pin RESP 2 explicitly and type the client accordingly. The switch to RESP 3 is deferred to the next major release. --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: secustor <sebastian@poxhofer.at>
162 lines
4.0 KiB
TypeScript
162 lines
4.0 KiB
TypeScript
import type {
|
|
RedisClientType,
|
|
RedisClusterOptions,
|
|
RedisClusterType,
|
|
RedisFunctions,
|
|
RedisModules,
|
|
RedisScripts,
|
|
TypeMapping,
|
|
} from '@redis/client';
|
|
import { RESP_TYPES, createClient, createCluster } from '@redis/client';
|
|
import { logger } from '../../../../logger/index.ts';
|
|
import { regEx } from '../../../regex.ts';
|
|
import { parseUrl } from '../../../url.ts';
|
|
import type { PackageCacheNamespace } from '../types.ts';
|
|
import { PackageCacheBase } from './base.ts';
|
|
|
|
export function normalizeRedisUrl(url: string): string {
|
|
return url.replace(
|
|
regEx(/^(?<scheme>rediss?)\+cluster:\/\//),
|
|
'$<scheme>://',
|
|
);
|
|
}
|
|
|
|
// TODO: switch to RESP 3 (the @redis/client v6 default) in the next major
|
|
// release, as it requires Redis server 6.0 or newer.
|
|
const RESP = 2;
|
|
|
|
type RedisClient =
|
|
| RedisClientType<
|
|
RedisModules,
|
|
RedisFunctions,
|
|
RedisScripts,
|
|
typeof RESP,
|
|
TypeMapping
|
|
>
|
|
| RedisClusterType<
|
|
RedisModules,
|
|
RedisFunctions,
|
|
RedisScripts,
|
|
typeof RESP,
|
|
TypeMapping
|
|
>;
|
|
|
|
interface RedisBinaryClient {
|
|
get(key: string): Promise<Buffer | null>;
|
|
}
|
|
|
|
export class PackageCacheRedis extends PackageCacheBase {
|
|
static async create(
|
|
url: string,
|
|
prefix: string | undefined,
|
|
): Promise<PackageCacheRedis> {
|
|
const rprefix = prefix ?? '';
|
|
logger.debug('Redis cache init');
|
|
|
|
const rewrittenUrl = normalizeRedisUrl(url);
|
|
const clusteredMode = rewrittenUrl !== url;
|
|
|
|
const config = {
|
|
url: rewrittenUrl,
|
|
socket: {
|
|
reconnectStrategy: (retries: number) => Math.min(retries * 100, 3000),
|
|
},
|
|
pingInterval: 30000,
|
|
};
|
|
|
|
let client: RedisClient;
|
|
|
|
if (clusteredMode) {
|
|
const clusterConfig: RedisClusterOptions<
|
|
RedisModules,
|
|
RedisFunctions,
|
|
RedisScripts,
|
|
typeof RESP
|
|
> = { rootNodes: [config], RESP };
|
|
|
|
const parsedUrl = parseUrl(rewrittenUrl);
|
|
if (parsedUrl?.username) {
|
|
clusterConfig.defaults = {
|
|
username: parsedUrl.username,
|
|
};
|
|
}
|
|
|
|
if (parsedUrl?.password) {
|
|
clusterConfig.defaults ??= {};
|
|
clusterConfig.defaults.password = parsedUrl.password;
|
|
}
|
|
|
|
client = createCluster(clusterConfig);
|
|
} else {
|
|
client = createClient({ ...config, RESP });
|
|
}
|
|
|
|
await client.connect();
|
|
logger.debug('Redis cache connected');
|
|
const binaryClient = client.withTypeMapping({
|
|
[RESP_TYPES.BLOB_STRING]: Buffer,
|
|
}) as RedisBinaryClient;
|
|
return new PackageCacheRedis(client, binaryClient, rprefix);
|
|
}
|
|
|
|
private readonly client: RedisClient;
|
|
private readonly binaryClient: RedisBinaryClient;
|
|
private readonly rprefix: string;
|
|
|
|
private constructor(
|
|
client: RedisClient,
|
|
binaryClient: RedisBinaryClient,
|
|
rprefix: string,
|
|
) {
|
|
super();
|
|
this.client = client;
|
|
this.binaryClient = binaryClient;
|
|
this.rprefix = rprefix;
|
|
}
|
|
|
|
private getKey(namespace: PackageCacheNamespace, key: string): string {
|
|
return `${this.rprefix}${namespace}-${key}`;
|
|
}
|
|
|
|
override destroy(): Promise<void> {
|
|
try {
|
|
// https://github.com/redis/node-redis#disconnecting
|
|
this.client.destroy();
|
|
} catch (err) {
|
|
logger.warn({ err }, 'Redis cache destroy failed');
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
protected override async readRaw(
|
|
namespace: PackageCacheNamespace,
|
|
key: string,
|
|
): Promise<Buffer | undefined> {
|
|
const raw = await this.binaryClient.get(this.getKey(namespace, key));
|
|
|
|
return raw ?? undefined;
|
|
}
|
|
|
|
protected override async writeRaw(
|
|
namespace: PackageCacheNamespace,
|
|
key: string,
|
|
data: Buffer,
|
|
ttlSeconds: number,
|
|
): Promise<void> {
|
|
await this.client.set(this.getKey(namespace, key), data, {
|
|
EX: ttlSeconds,
|
|
});
|
|
}
|
|
|
|
protected override async rm(
|
|
namespace: PackageCacheNamespace,
|
|
key: string,
|
|
): Promise<void> {
|
|
logger.trace(
|
|
{ rprefix: this.rprefix, namespace, key },
|
|
'Removing cache entry',
|
|
);
|
|
await this.client.del(this.getKey(namespace, key));
|
|
}
|
|
}
|