@rushstack/rush-redis-cobuild-plugin 5.175.1 → 5.177.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib-commonjs/RedisCobuildLockProvider.js +5 -1
- package/lib-commonjs/RedisCobuildLockProvider.js.map +1 -1
- package/lib-dts/RedisCobuildLockProvider.d.ts.map +1 -1
- package/lib-dts/tsdoc-metadata.json +1 -1
- package/lib-esm/RedisCobuildLockProvider.js +5 -1
- package/lib-esm/RedisCobuildLockProvider.js.map +1 -1
- package/package.json +7 -7
|
@@ -13,8 +13,12 @@ class RedisCobuildLockProvider {
|
|
|
13
13
|
this._lockKeyIdentifierMap = new WeakMap();
|
|
14
14
|
this._completedStateKeyIdentifierMap = new WeakMap();
|
|
15
15
|
this._options = RedisCobuildLockProvider.expandOptionsWithEnvironmentVariables(options);
|
|
16
|
-
//
|
|
16
|
+
// Detect half-dead connections quickly. Without `socketTimeout`, a silently-dropped
|
|
17
|
+
// TCP connection (NAT/firewall) can stall in-flight commands for many minutes while
|
|
18
|
+
// the kernel waits to surface the failure.
|
|
17
19
|
this._options.socket = {
|
|
20
|
+
connectTimeout: 10000,
|
|
21
|
+
socketTimeout: 30000,
|
|
18
22
|
reconnectStrategy: (count) => {
|
|
19
23
|
this._terminal.writeErrorLine(`Redis client reconnecting attempt #${count}`);
|
|
20
24
|
return count < 5 ? count * 1000 : false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisCobuildLockProvider.js","sourceRoot":"","sources":["../src/RedisCobuildLockProvider.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { createClient } from '@redis/client';\nimport type {\n RedisClientOptions,\n RedisClientType,\n RedisFunctions,\n RedisModules,\n RedisScripts\n} from '@redis/client';\n\nimport type {\n ICobuildLockProvider,\n ICobuildContext,\n ICobuildCompletedState,\n RushSession\n} from '@rushstack/rush-sdk';\nimport type { ITerminal } from '@rushstack/terminal';\n\n/**\n * The redis client options\n * @beta\n */\nexport interface IRedisCobuildLockProviderOptions extends RedisClientOptions {\n /**\n * The environment variable name for the redis password\n */\n passwordEnvironmentVariable?: string;\n}\n\nconst COMPLETED_STATE_SEPARATOR: ';' = ';';\n\n/**\n * @beta\n */\nexport class RedisCobuildLockProvider implements ICobuildLockProvider {\n private readonly _options: IRedisCobuildLockProviderOptions;\n private readonly _terminal: ITerminal;\n private readonly _lockKeyIdentifierMap: WeakMap<ICobuildContext, string> = new WeakMap<\n ICobuildContext,\n string\n >();\n private readonly _completedStateKeyIdentifierMap: WeakMap<ICobuildContext, string> = new WeakMap<\n ICobuildContext,\n string\n >();\n\n private readonly _redisClient: RedisClientType<RedisModules, RedisFunctions, RedisScripts, 2 | 3>;\n\n public constructor(options: IRedisCobuildLockProviderOptions, rushSession: RushSession) {\n this._options = RedisCobuildLockProvider.expandOptionsWithEnvironmentVariables(options);\n // Provide a default reconnect strategy that prevents more than 5 reconnect attempts.\n this._options.socket = {\n reconnectStrategy: (count: number) => {\n this._terminal.writeErrorLine(`Redis client reconnecting attempt #${count}`);\n return count < 5 ? count * 1000 : false;\n },\n ...this._options.socket\n };\n this._terminal = rushSession.getLogger('RedisCobuildLockProvider').terminal;\n try {\n this._redisClient = createClient(this._options);\n } catch (e) {\n throw new Error(`Failed to create redis client: ${e.message}`);\n }\n }\n\n public static expandOptionsWithEnvironmentVariables(\n options: IRedisCobuildLockProviderOptions,\n environment: NodeJS.ProcessEnv = process.env\n ): IRedisCobuildLockProviderOptions {\n const finalOptions: IRedisCobuildLockProviderOptions = { ...options };\n const missingEnvironmentVariables: Set<string> = new Set<string>();\n\n if (finalOptions.passwordEnvironmentVariable) {\n const password: string | undefined = environment[finalOptions.passwordEnvironmentVariable];\n if (password !== undefined) {\n finalOptions.password = password;\n } else {\n missingEnvironmentVariables.add(finalOptions.passwordEnvironmentVariable);\n }\n finalOptions.passwordEnvironmentVariable = undefined;\n }\n\n if (missingEnvironmentVariables.size) {\n throw new Error(\n `The \"RedisCobuildLockProvider\" tries to access missing environment variable${\n missingEnvironmentVariables.size > 1 ? 's' : ''\n }: ${Array.from(missingEnvironmentVariables).join(\n ', '\n )}\\nPlease check the configuration in rush-redis-cobuild-plugin.json file`\n );\n }\n return finalOptions;\n }\n\n public async connectAsync(): Promise<void> {\n try {\n await this._redisClient.connect();\n // Check the connection works at early stage\n await this._redisClient.ping();\n } catch (e) {\n throw new Error(`Failed to connect to redis server: ${e.message}`);\n }\n\n // Register error event handler to avoid process exit when redis client error occurs.\n this._redisClient.on('error', (e: Error) => {\n if (e.message) {\n this._terminal.writeErrorLine(`Redis client error: ${e.message}`);\n } else {\n this._terminal.writeErrorLine(`Redis client error: ${e}`);\n }\n });\n }\n\n public async disconnectAsync(): Promise<void> {\n try {\n await this._redisClient.destroy();\n } catch (e) {\n throw new Error(`Failed to disconnect to redis server: ${e.message}`);\n }\n }\n\n /**\n * Acquiring the lock based on the specific context.\n *\n * NOTE: this is a reentrant lock implementation\n */\n public async acquireLockAsync(context: ICobuildContext): Promise<boolean> {\n const { _terminal: terminal } = this;\n const { lockKey, lockExpireTimeInSeconds, runnerId } = context;\n let result: boolean = false;\n const lockKeyIdentifier: string = this._getLockKeyIdentifier(context);\n try {\n // According to the doc, the reply of set command is either \"OK\" or nil. The reply doesn't matter\n await this._redisClient.set(lockKey, runnerId, {\n NX: true,\n // call EXPIRE in an atomic command\n EX: lockExpireTimeInSeconds\n // Do not specify GET here since using NX ane GET together requires Redis@7.\n });\n // Just read the value by lock key to see wether it equals current runner id\n const value: string | null = await this._redisClient.get(lockKey);\n if (value === null) {\n // This should not happen.\n throw new Error(`Get redis key failed: ${lockKey}`);\n }\n result = value === runnerId;\n if (result) {\n terminal.writeDebugLine(\n `Successfully acquired ${lockKeyIdentifier} to runner(${runnerId}) and it expires in ${lockExpireTimeInSeconds}s`\n );\n } else {\n terminal.writeDebugLine(`Failed to acquire ${lockKeyIdentifier}, locked by runner ${value}`);\n }\n } catch (e) {\n throw new Error(`Error occurs when acquiring ${lockKeyIdentifier}: ${e.message}`);\n }\n return result;\n }\n\n public async renewLockAsync(context: ICobuildContext): Promise<void> {\n const { _terminal: terminal } = this;\n const { lockKey, lockExpireTimeInSeconds } = context;\n const lockKeyIdentifier: string = this._getLockKeyIdentifier(context);\n try {\n await this._redisClient.expire(lockKey, lockExpireTimeInSeconds);\n } catch (e) {\n throw new Error(`Failed to renew ${lockKeyIdentifier}: ${e.message}`);\n }\n terminal.writeDebugLine(`Renewed ${lockKeyIdentifier} expires in ${lockExpireTimeInSeconds} seconds`);\n }\n\n public async setCompletedStateAsync(\n context: ICobuildContext,\n state: ICobuildCompletedState\n ): Promise<void> {\n const { _terminal: terminal } = this;\n const { completedStateKey: key } = context;\n const value: string = this._serializeCompletedState(state);\n const completedStateKeyIdentifier: string = this._getCompletedStateKeyIdentifier(context);\n try {\n await this._redisClient.set(key, value);\n } catch (e) {\n throw new Error(`Failed to set ${completedStateKeyIdentifier}: ${e.message}`);\n }\n terminal.writeDebugLine(`Set ${completedStateKeyIdentifier}: ${value}`);\n }\n\n public async getCompletedStateAsync(context: ICobuildContext): Promise<ICobuildCompletedState | undefined> {\n const { _terminal: terminal } = this;\n const { completedStateKey: key } = context;\n const completedStateKeyIdentifier: string = this._getCompletedStateKeyIdentifier(context);\n let state: ICobuildCompletedState | undefined;\n try {\n const value: string | null = await this._redisClient.get(key);\n if (value) {\n state = this._deserializeCompletedState(value);\n }\n terminal.writeDebugLine(`Get ${completedStateKeyIdentifier}: ${value}`);\n } catch (e) {\n throw new Error(`Failed to get ${completedStateKeyIdentifier}: ${e.message}`);\n }\n return state;\n }\n\n private _serializeCompletedState(state: ICobuildCompletedState): string {\n // Example: SUCCESS;1234567890\n // Example: FAILURE;1234567890\n const { status, cacheId } = state;\n return [status, cacheId].join(COMPLETED_STATE_SEPARATOR);\n }\n\n private _deserializeCompletedState(state: string): ICobuildCompletedState | undefined {\n const [status, cacheId] = state.split(COMPLETED_STATE_SEPARATOR);\n return { status: status as ICobuildCompletedState['status'], cacheId };\n }\n\n private _getLockKeyIdentifier(context: ICobuildContext): string {\n let lockKeyIdentifier: string | undefined = this._lockKeyIdentifierMap.get(context);\n if (lockKeyIdentifier === undefined) {\n const { lockKey, packageName, phaseName } = context;\n lockKeyIdentifier = `lock(${lockKey})_package(${packageName})_phase(${phaseName})`;\n this._lockKeyIdentifierMap.set(context, lockKeyIdentifier);\n }\n return lockKeyIdentifier;\n }\n\n private _getCompletedStateKeyIdentifier(context: ICobuildContext): string {\n let completedStateKeyIdentifier: string | undefined = this._completedStateKeyIdentifierMap.get(context);\n if (completedStateKeyIdentifier === undefined) {\n const { completedStateKey, packageName, phaseName } = context;\n completedStateKeyIdentifier = `completed_state(${completedStateKey})_package(${packageName})_phase(${phaseName})`;\n this._completedStateKeyIdentifierMap.set(context, completedStateKeyIdentifier);\n }\n return completedStateKeyIdentifier;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"RedisCobuildLockProvider.js","sourceRoot":"","sources":["../src/RedisCobuildLockProvider.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { createClient } from '@redis/client';\nimport type {\n RedisClientOptions,\n RedisClientType,\n RedisFunctions,\n RedisModules,\n RedisScripts\n} from '@redis/client';\n\nimport type {\n ICobuildLockProvider,\n ICobuildContext,\n ICobuildCompletedState,\n RushSession\n} from '@rushstack/rush-sdk';\nimport type { ITerminal } from '@rushstack/terminal';\n\n/**\n * The redis client options\n * @beta\n */\nexport interface IRedisCobuildLockProviderOptions extends RedisClientOptions {\n /**\n * The environment variable name for the redis password\n */\n passwordEnvironmentVariable?: string;\n}\n\nconst COMPLETED_STATE_SEPARATOR: ';' = ';';\n\n/**\n * @beta\n */\nexport class RedisCobuildLockProvider implements ICobuildLockProvider {\n private readonly _options: IRedisCobuildLockProviderOptions;\n private readonly _terminal: ITerminal;\n private readonly _lockKeyIdentifierMap: WeakMap<ICobuildContext, string> = new WeakMap<\n ICobuildContext,\n string\n >();\n private readonly _completedStateKeyIdentifierMap: WeakMap<ICobuildContext, string> = new WeakMap<\n ICobuildContext,\n string\n >();\n\n private readonly _redisClient: RedisClientType<RedisModules, RedisFunctions, RedisScripts, 2 | 3>;\n\n public constructor(options: IRedisCobuildLockProviderOptions, rushSession: RushSession) {\n this._options = RedisCobuildLockProvider.expandOptionsWithEnvironmentVariables(options);\n // Detect half-dead connections quickly. Without `socketTimeout`, a silently-dropped\n // TCP connection (NAT/firewall) can stall in-flight commands for many minutes while\n // the kernel waits to surface the failure.\n this._options.socket = {\n connectTimeout: 10_000,\n socketTimeout: 30_000,\n reconnectStrategy: (count: number) => {\n this._terminal.writeErrorLine(`Redis client reconnecting attempt #${count}`);\n return count < 5 ? count * 1000 : false;\n },\n ...this._options.socket\n };\n this._terminal = rushSession.getLogger('RedisCobuildLockProvider').terminal;\n try {\n this._redisClient = createClient(this._options);\n } catch (e) {\n throw new Error(`Failed to create redis client: ${e.message}`);\n }\n }\n\n public static expandOptionsWithEnvironmentVariables(\n options: IRedisCobuildLockProviderOptions,\n environment: NodeJS.ProcessEnv = process.env\n ): IRedisCobuildLockProviderOptions {\n const finalOptions: IRedisCobuildLockProviderOptions = { ...options };\n const missingEnvironmentVariables: Set<string> = new Set<string>();\n\n if (finalOptions.passwordEnvironmentVariable) {\n const password: string | undefined = environment[finalOptions.passwordEnvironmentVariable];\n if (password !== undefined) {\n finalOptions.password = password;\n } else {\n missingEnvironmentVariables.add(finalOptions.passwordEnvironmentVariable);\n }\n finalOptions.passwordEnvironmentVariable = undefined;\n }\n\n if (missingEnvironmentVariables.size) {\n throw new Error(\n `The \"RedisCobuildLockProvider\" tries to access missing environment variable${\n missingEnvironmentVariables.size > 1 ? 's' : ''\n }: ${Array.from(missingEnvironmentVariables).join(\n ', '\n )}\\nPlease check the configuration in rush-redis-cobuild-plugin.json file`\n );\n }\n return finalOptions;\n }\n\n public async connectAsync(): Promise<void> {\n try {\n await this._redisClient.connect();\n // Check the connection works at early stage\n await this._redisClient.ping();\n } catch (e) {\n throw new Error(`Failed to connect to redis server: ${e.message}`);\n }\n\n // Register error event handler to avoid process exit when redis client error occurs.\n this._redisClient.on('error', (e: Error) => {\n if (e.message) {\n this._terminal.writeErrorLine(`Redis client error: ${e.message}`);\n } else {\n this._terminal.writeErrorLine(`Redis client error: ${e}`);\n }\n });\n }\n\n public async disconnectAsync(): Promise<void> {\n try {\n await this._redisClient.destroy();\n } catch (e) {\n throw new Error(`Failed to disconnect to redis server: ${e.message}`);\n }\n }\n\n /**\n * Acquiring the lock based on the specific context.\n *\n * NOTE: this is a reentrant lock implementation\n */\n public async acquireLockAsync(context: ICobuildContext): Promise<boolean> {\n const { _terminal: terminal } = this;\n const { lockKey, lockExpireTimeInSeconds, runnerId } = context;\n let result: boolean = false;\n const lockKeyIdentifier: string = this._getLockKeyIdentifier(context);\n try {\n // According to the doc, the reply of set command is either \"OK\" or nil. The reply doesn't matter\n await this._redisClient.set(lockKey, runnerId, {\n NX: true,\n // call EXPIRE in an atomic command\n EX: lockExpireTimeInSeconds\n // Do not specify GET here since using NX ane GET together requires Redis@7.\n });\n // Just read the value by lock key to see wether it equals current runner id\n const value: string | null = await this._redisClient.get(lockKey);\n if (value === null) {\n // This should not happen.\n throw new Error(`Get redis key failed: ${lockKey}`);\n }\n result = value === runnerId;\n if (result) {\n terminal.writeDebugLine(\n `Successfully acquired ${lockKeyIdentifier} to runner(${runnerId}) and it expires in ${lockExpireTimeInSeconds}s`\n );\n } else {\n terminal.writeDebugLine(`Failed to acquire ${lockKeyIdentifier}, locked by runner ${value}`);\n }\n } catch (e) {\n throw new Error(`Error occurs when acquiring ${lockKeyIdentifier}: ${e.message}`);\n }\n return result;\n }\n\n public async renewLockAsync(context: ICobuildContext): Promise<void> {\n const { _terminal: terminal } = this;\n const { lockKey, lockExpireTimeInSeconds } = context;\n const lockKeyIdentifier: string = this._getLockKeyIdentifier(context);\n try {\n await this._redisClient.expire(lockKey, lockExpireTimeInSeconds);\n } catch (e) {\n throw new Error(`Failed to renew ${lockKeyIdentifier}: ${e.message}`);\n }\n terminal.writeDebugLine(`Renewed ${lockKeyIdentifier} expires in ${lockExpireTimeInSeconds} seconds`);\n }\n\n public async setCompletedStateAsync(\n context: ICobuildContext,\n state: ICobuildCompletedState\n ): Promise<void> {\n const { _terminal: terminal } = this;\n const { completedStateKey: key } = context;\n const value: string = this._serializeCompletedState(state);\n const completedStateKeyIdentifier: string = this._getCompletedStateKeyIdentifier(context);\n try {\n await this._redisClient.set(key, value);\n } catch (e) {\n throw new Error(`Failed to set ${completedStateKeyIdentifier}: ${e.message}`);\n }\n terminal.writeDebugLine(`Set ${completedStateKeyIdentifier}: ${value}`);\n }\n\n public async getCompletedStateAsync(context: ICobuildContext): Promise<ICobuildCompletedState | undefined> {\n const { _terminal: terminal } = this;\n const { completedStateKey: key } = context;\n const completedStateKeyIdentifier: string = this._getCompletedStateKeyIdentifier(context);\n let state: ICobuildCompletedState | undefined;\n try {\n const value: string | null = await this._redisClient.get(key);\n if (value) {\n state = this._deserializeCompletedState(value);\n }\n terminal.writeDebugLine(`Get ${completedStateKeyIdentifier}: ${value}`);\n } catch (e) {\n throw new Error(`Failed to get ${completedStateKeyIdentifier}: ${e.message}`);\n }\n return state;\n }\n\n private _serializeCompletedState(state: ICobuildCompletedState): string {\n // Example: SUCCESS;1234567890\n // Example: FAILURE;1234567890\n const { status, cacheId } = state;\n return [status, cacheId].join(COMPLETED_STATE_SEPARATOR);\n }\n\n private _deserializeCompletedState(state: string): ICobuildCompletedState | undefined {\n const [status, cacheId] = state.split(COMPLETED_STATE_SEPARATOR);\n return { status: status as ICobuildCompletedState['status'], cacheId };\n }\n\n private _getLockKeyIdentifier(context: ICobuildContext): string {\n let lockKeyIdentifier: string | undefined = this._lockKeyIdentifierMap.get(context);\n if (lockKeyIdentifier === undefined) {\n const { lockKey, packageName, phaseName } = context;\n lockKeyIdentifier = `lock(${lockKey})_package(${packageName})_phase(${phaseName})`;\n this._lockKeyIdentifierMap.set(context, lockKeyIdentifier);\n }\n return lockKeyIdentifier;\n }\n\n private _getCompletedStateKeyIdentifier(context: ICobuildContext): string {\n let completedStateKeyIdentifier: string | undefined = this._completedStateKeyIdentifierMap.get(context);\n if (completedStateKeyIdentifier === undefined) {\n const { completedStateKey, packageName, phaseName } = context;\n completedStateKeyIdentifier = `completed_state(${completedStateKey})_package(${packageName})_phase(${phaseName})`;\n this._completedStateKeyIdentifierMap.set(context, completedStateKeyIdentifier);\n }\n return completedStateKeyIdentifier;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisCobuildLockProvider.d.ts","sourceRoot":"","sources":["../src/RedisCobuildLockProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAKnB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAG7B;;;GAGG;AACH,MAAM,WAAW,gCAAiC,SAAQ,kBAAkB;IAC1E;;OAEG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAID;;GAEG;AACH,qBAAa,wBAAyB,YAAW,oBAAoB;IACnE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAGlC;IACJ,OAAO,CAAC,QAAQ,CAAC,+BAA+B,CAG5C;IAEJ,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqE;gBAE/E,OAAO,EAAE,gCAAgC,EAAE,WAAW,EAAE,WAAW;
|
|
1
|
+
{"version":3,"file":"RedisCobuildLockProvider.d.ts","sourceRoot":"","sources":["../src/RedisCobuildLockProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAKnB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAG7B;;;GAGG;AACH,MAAM,WAAW,gCAAiC,SAAQ,kBAAkB;IAC1E;;OAEG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAID;;GAEG;AACH,qBAAa,wBAAyB,YAAW,oBAAoB;IACnE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAGlC;IACJ,OAAO,CAAC,QAAQ,CAAC,+BAA+B,CAG5C;IAEJ,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqE;gBAE/E,OAAO,EAAE,gCAAgC,EAAE,WAAW,EAAE,WAAW;WAsBxE,qCAAqC,CACjD,OAAO,EAAE,gCAAgC,EACzC,WAAW,GAAE,MAAM,CAAC,UAAwB,GAC3C,gCAAgC;IA0BtB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB7B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7C;;;;OAIG;IACU,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAiC5D,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvD,sBAAsB,CACjC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAaH,sBAAsB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC;IAiB1G,OAAO,CAAC,wBAAwB;IAOhC,OAAO,CAAC,0BAA0B;IAKlC,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,+BAA+B;CASxC"}
|
|
@@ -10,8 +10,12 @@ export class RedisCobuildLockProvider {
|
|
|
10
10
|
this._lockKeyIdentifierMap = new WeakMap();
|
|
11
11
|
this._completedStateKeyIdentifierMap = new WeakMap();
|
|
12
12
|
this._options = RedisCobuildLockProvider.expandOptionsWithEnvironmentVariables(options);
|
|
13
|
-
//
|
|
13
|
+
// Detect half-dead connections quickly. Without `socketTimeout`, a silently-dropped
|
|
14
|
+
// TCP connection (NAT/firewall) can stall in-flight commands for many minutes while
|
|
15
|
+
// the kernel waits to surface the failure.
|
|
14
16
|
this._options.socket = {
|
|
17
|
+
connectTimeout: 10000,
|
|
18
|
+
socketTimeout: 30000,
|
|
15
19
|
reconnectStrategy: (count) => {
|
|
16
20
|
this._terminal.writeErrorLine(`Redis client reconnecting attempt #${count}`);
|
|
17
21
|
return count < 5 ? count * 1000 : false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisCobuildLockProvider.js","sourceRoot":"","sources":["../src/RedisCobuildLockProvider.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { createClient } from '@redis/client';\nimport type {\n RedisClientOptions,\n RedisClientType,\n RedisFunctions,\n RedisModules,\n RedisScripts\n} from '@redis/client';\n\nimport type {\n ICobuildLockProvider,\n ICobuildContext,\n ICobuildCompletedState,\n RushSession\n} from '@rushstack/rush-sdk';\nimport type { ITerminal } from '@rushstack/terminal';\n\n/**\n * The redis client options\n * @beta\n */\nexport interface IRedisCobuildLockProviderOptions extends RedisClientOptions {\n /**\n * The environment variable name for the redis password\n */\n passwordEnvironmentVariable?: string;\n}\n\nconst COMPLETED_STATE_SEPARATOR: ';' = ';';\n\n/**\n * @beta\n */\nexport class RedisCobuildLockProvider implements ICobuildLockProvider {\n private readonly _options: IRedisCobuildLockProviderOptions;\n private readonly _terminal: ITerminal;\n private readonly _lockKeyIdentifierMap: WeakMap<ICobuildContext, string> = new WeakMap<\n ICobuildContext,\n string\n >();\n private readonly _completedStateKeyIdentifierMap: WeakMap<ICobuildContext, string> = new WeakMap<\n ICobuildContext,\n string\n >();\n\n private readonly _redisClient: RedisClientType<RedisModules, RedisFunctions, RedisScripts, 2 | 3>;\n\n public constructor(options: IRedisCobuildLockProviderOptions, rushSession: RushSession) {\n this._options = RedisCobuildLockProvider.expandOptionsWithEnvironmentVariables(options);\n // Provide a default reconnect strategy that prevents more than 5 reconnect attempts.\n this._options.socket = {\n reconnectStrategy: (count: number) => {\n this._terminal.writeErrorLine(`Redis client reconnecting attempt #${count}`);\n return count < 5 ? count * 1000 : false;\n },\n ...this._options.socket\n };\n this._terminal = rushSession.getLogger('RedisCobuildLockProvider').terminal;\n try {\n this._redisClient = createClient(this._options);\n } catch (e) {\n throw new Error(`Failed to create redis client: ${e.message}`);\n }\n }\n\n public static expandOptionsWithEnvironmentVariables(\n options: IRedisCobuildLockProviderOptions,\n environment: NodeJS.ProcessEnv = process.env\n ): IRedisCobuildLockProviderOptions {\n const finalOptions: IRedisCobuildLockProviderOptions = { ...options };\n const missingEnvironmentVariables: Set<string> = new Set<string>();\n\n if (finalOptions.passwordEnvironmentVariable) {\n const password: string | undefined = environment[finalOptions.passwordEnvironmentVariable];\n if (password !== undefined) {\n finalOptions.password = password;\n } else {\n missingEnvironmentVariables.add(finalOptions.passwordEnvironmentVariable);\n }\n finalOptions.passwordEnvironmentVariable = undefined;\n }\n\n if (missingEnvironmentVariables.size) {\n throw new Error(\n `The \"RedisCobuildLockProvider\" tries to access missing environment variable${\n missingEnvironmentVariables.size > 1 ? 's' : ''\n }: ${Array.from(missingEnvironmentVariables).join(\n ', '\n )}\\nPlease check the configuration in rush-redis-cobuild-plugin.json file`\n );\n }\n return finalOptions;\n }\n\n public async connectAsync(): Promise<void> {\n try {\n await this._redisClient.connect();\n // Check the connection works at early stage\n await this._redisClient.ping();\n } catch (e) {\n throw new Error(`Failed to connect to redis server: ${e.message}`);\n }\n\n // Register error event handler to avoid process exit when redis client error occurs.\n this._redisClient.on('error', (e: Error) => {\n if (e.message) {\n this._terminal.writeErrorLine(`Redis client error: ${e.message}`);\n } else {\n this._terminal.writeErrorLine(`Redis client error: ${e}`);\n }\n });\n }\n\n public async disconnectAsync(): Promise<void> {\n try {\n await this._redisClient.destroy();\n } catch (e) {\n throw new Error(`Failed to disconnect to redis server: ${e.message}`);\n }\n }\n\n /**\n * Acquiring the lock based on the specific context.\n *\n * NOTE: this is a reentrant lock implementation\n */\n public async acquireLockAsync(context: ICobuildContext): Promise<boolean> {\n const { _terminal: terminal } = this;\n const { lockKey, lockExpireTimeInSeconds, runnerId } = context;\n let result: boolean = false;\n const lockKeyIdentifier: string = this._getLockKeyIdentifier(context);\n try {\n // According to the doc, the reply of set command is either \"OK\" or nil. The reply doesn't matter\n await this._redisClient.set(lockKey, runnerId, {\n NX: true,\n // call EXPIRE in an atomic command\n EX: lockExpireTimeInSeconds\n // Do not specify GET here since using NX ane GET together requires Redis@7.\n });\n // Just read the value by lock key to see wether it equals current runner id\n const value: string | null = await this._redisClient.get(lockKey);\n if (value === null) {\n // This should not happen.\n throw new Error(`Get redis key failed: ${lockKey}`);\n }\n result = value === runnerId;\n if (result) {\n terminal.writeDebugLine(\n `Successfully acquired ${lockKeyIdentifier} to runner(${runnerId}) and it expires in ${lockExpireTimeInSeconds}s`\n );\n } else {\n terminal.writeDebugLine(`Failed to acquire ${lockKeyIdentifier}, locked by runner ${value}`);\n }\n } catch (e) {\n throw new Error(`Error occurs when acquiring ${lockKeyIdentifier}: ${e.message}`);\n }\n return result;\n }\n\n public async renewLockAsync(context: ICobuildContext): Promise<void> {\n const { _terminal: terminal } = this;\n const { lockKey, lockExpireTimeInSeconds } = context;\n const lockKeyIdentifier: string = this._getLockKeyIdentifier(context);\n try {\n await this._redisClient.expire(lockKey, lockExpireTimeInSeconds);\n } catch (e) {\n throw new Error(`Failed to renew ${lockKeyIdentifier}: ${e.message}`);\n }\n terminal.writeDebugLine(`Renewed ${lockKeyIdentifier} expires in ${lockExpireTimeInSeconds} seconds`);\n }\n\n public async setCompletedStateAsync(\n context: ICobuildContext,\n state: ICobuildCompletedState\n ): Promise<void> {\n const { _terminal: terminal } = this;\n const { completedStateKey: key } = context;\n const value: string = this._serializeCompletedState(state);\n const completedStateKeyIdentifier: string = this._getCompletedStateKeyIdentifier(context);\n try {\n await this._redisClient.set(key, value);\n } catch (e) {\n throw new Error(`Failed to set ${completedStateKeyIdentifier}: ${e.message}`);\n }\n terminal.writeDebugLine(`Set ${completedStateKeyIdentifier}: ${value}`);\n }\n\n public async getCompletedStateAsync(context: ICobuildContext): Promise<ICobuildCompletedState | undefined> {\n const { _terminal: terminal } = this;\n const { completedStateKey: key } = context;\n const completedStateKeyIdentifier: string = this._getCompletedStateKeyIdentifier(context);\n let state: ICobuildCompletedState | undefined;\n try {\n const value: string | null = await this._redisClient.get(key);\n if (value) {\n state = this._deserializeCompletedState(value);\n }\n terminal.writeDebugLine(`Get ${completedStateKeyIdentifier}: ${value}`);\n } catch (e) {\n throw new Error(`Failed to get ${completedStateKeyIdentifier}: ${e.message}`);\n }\n return state;\n }\n\n private _serializeCompletedState(state: ICobuildCompletedState): string {\n // Example: SUCCESS;1234567890\n // Example: FAILURE;1234567890\n const { status, cacheId } = state;\n return [status, cacheId].join(COMPLETED_STATE_SEPARATOR);\n }\n\n private _deserializeCompletedState(state: string): ICobuildCompletedState | undefined {\n const [status, cacheId] = state.split(COMPLETED_STATE_SEPARATOR);\n return { status: status as ICobuildCompletedState['status'], cacheId };\n }\n\n private _getLockKeyIdentifier(context: ICobuildContext): string {\n let lockKeyIdentifier: string | undefined = this._lockKeyIdentifierMap.get(context);\n if (lockKeyIdentifier === undefined) {\n const { lockKey, packageName, phaseName } = context;\n lockKeyIdentifier = `lock(${lockKey})_package(${packageName})_phase(${phaseName})`;\n this._lockKeyIdentifierMap.set(context, lockKeyIdentifier);\n }\n return lockKeyIdentifier;\n }\n\n private _getCompletedStateKeyIdentifier(context: ICobuildContext): string {\n let completedStateKeyIdentifier: string | undefined = this._completedStateKeyIdentifierMap.get(context);\n if (completedStateKeyIdentifier === undefined) {\n const { completedStateKey, packageName, phaseName } = context;\n completedStateKeyIdentifier = `completed_state(${completedStateKey})_package(${packageName})_phase(${phaseName})`;\n this._completedStateKeyIdentifierMap.set(context, completedStateKeyIdentifier);\n }\n return completedStateKeyIdentifier;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"RedisCobuildLockProvider.js","sourceRoot":"","sources":["../src/RedisCobuildLockProvider.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { createClient } from '@redis/client';\nimport type {\n RedisClientOptions,\n RedisClientType,\n RedisFunctions,\n RedisModules,\n RedisScripts\n} from '@redis/client';\n\nimport type {\n ICobuildLockProvider,\n ICobuildContext,\n ICobuildCompletedState,\n RushSession\n} from '@rushstack/rush-sdk';\nimport type { ITerminal } from '@rushstack/terminal';\n\n/**\n * The redis client options\n * @beta\n */\nexport interface IRedisCobuildLockProviderOptions extends RedisClientOptions {\n /**\n * The environment variable name for the redis password\n */\n passwordEnvironmentVariable?: string;\n}\n\nconst COMPLETED_STATE_SEPARATOR: ';' = ';';\n\n/**\n * @beta\n */\nexport class RedisCobuildLockProvider implements ICobuildLockProvider {\n private readonly _options: IRedisCobuildLockProviderOptions;\n private readonly _terminal: ITerminal;\n private readonly _lockKeyIdentifierMap: WeakMap<ICobuildContext, string> = new WeakMap<\n ICobuildContext,\n string\n >();\n private readonly _completedStateKeyIdentifierMap: WeakMap<ICobuildContext, string> = new WeakMap<\n ICobuildContext,\n string\n >();\n\n private readonly _redisClient: RedisClientType<RedisModules, RedisFunctions, RedisScripts, 2 | 3>;\n\n public constructor(options: IRedisCobuildLockProviderOptions, rushSession: RushSession) {\n this._options = RedisCobuildLockProvider.expandOptionsWithEnvironmentVariables(options);\n // Detect half-dead connections quickly. Without `socketTimeout`, a silently-dropped\n // TCP connection (NAT/firewall) can stall in-flight commands for many minutes while\n // the kernel waits to surface the failure.\n this._options.socket = {\n connectTimeout: 10_000,\n socketTimeout: 30_000,\n reconnectStrategy: (count: number) => {\n this._terminal.writeErrorLine(`Redis client reconnecting attempt #${count}`);\n return count < 5 ? count * 1000 : false;\n },\n ...this._options.socket\n };\n this._terminal = rushSession.getLogger('RedisCobuildLockProvider').terminal;\n try {\n this._redisClient = createClient(this._options);\n } catch (e) {\n throw new Error(`Failed to create redis client: ${e.message}`);\n }\n }\n\n public static expandOptionsWithEnvironmentVariables(\n options: IRedisCobuildLockProviderOptions,\n environment: NodeJS.ProcessEnv = process.env\n ): IRedisCobuildLockProviderOptions {\n const finalOptions: IRedisCobuildLockProviderOptions = { ...options };\n const missingEnvironmentVariables: Set<string> = new Set<string>();\n\n if (finalOptions.passwordEnvironmentVariable) {\n const password: string | undefined = environment[finalOptions.passwordEnvironmentVariable];\n if (password !== undefined) {\n finalOptions.password = password;\n } else {\n missingEnvironmentVariables.add(finalOptions.passwordEnvironmentVariable);\n }\n finalOptions.passwordEnvironmentVariable = undefined;\n }\n\n if (missingEnvironmentVariables.size) {\n throw new Error(\n `The \"RedisCobuildLockProvider\" tries to access missing environment variable${\n missingEnvironmentVariables.size > 1 ? 's' : ''\n }: ${Array.from(missingEnvironmentVariables).join(\n ', '\n )}\\nPlease check the configuration in rush-redis-cobuild-plugin.json file`\n );\n }\n return finalOptions;\n }\n\n public async connectAsync(): Promise<void> {\n try {\n await this._redisClient.connect();\n // Check the connection works at early stage\n await this._redisClient.ping();\n } catch (e) {\n throw new Error(`Failed to connect to redis server: ${e.message}`);\n }\n\n // Register error event handler to avoid process exit when redis client error occurs.\n this._redisClient.on('error', (e: Error) => {\n if (e.message) {\n this._terminal.writeErrorLine(`Redis client error: ${e.message}`);\n } else {\n this._terminal.writeErrorLine(`Redis client error: ${e}`);\n }\n });\n }\n\n public async disconnectAsync(): Promise<void> {\n try {\n await this._redisClient.destroy();\n } catch (e) {\n throw new Error(`Failed to disconnect to redis server: ${e.message}`);\n }\n }\n\n /**\n * Acquiring the lock based on the specific context.\n *\n * NOTE: this is a reentrant lock implementation\n */\n public async acquireLockAsync(context: ICobuildContext): Promise<boolean> {\n const { _terminal: terminal } = this;\n const { lockKey, lockExpireTimeInSeconds, runnerId } = context;\n let result: boolean = false;\n const lockKeyIdentifier: string = this._getLockKeyIdentifier(context);\n try {\n // According to the doc, the reply of set command is either \"OK\" or nil. The reply doesn't matter\n await this._redisClient.set(lockKey, runnerId, {\n NX: true,\n // call EXPIRE in an atomic command\n EX: lockExpireTimeInSeconds\n // Do not specify GET here since using NX ane GET together requires Redis@7.\n });\n // Just read the value by lock key to see wether it equals current runner id\n const value: string | null = await this._redisClient.get(lockKey);\n if (value === null) {\n // This should not happen.\n throw new Error(`Get redis key failed: ${lockKey}`);\n }\n result = value === runnerId;\n if (result) {\n terminal.writeDebugLine(\n `Successfully acquired ${lockKeyIdentifier} to runner(${runnerId}) and it expires in ${lockExpireTimeInSeconds}s`\n );\n } else {\n terminal.writeDebugLine(`Failed to acquire ${lockKeyIdentifier}, locked by runner ${value}`);\n }\n } catch (e) {\n throw new Error(`Error occurs when acquiring ${lockKeyIdentifier}: ${e.message}`);\n }\n return result;\n }\n\n public async renewLockAsync(context: ICobuildContext): Promise<void> {\n const { _terminal: terminal } = this;\n const { lockKey, lockExpireTimeInSeconds } = context;\n const lockKeyIdentifier: string = this._getLockKeyIdentifier(context);\n try {\n await this._redisClient.expire(lockKey, lockExpireTimeInSeconds);\n } catch (e) {\n throw new Error(`Failed to renew ${lockKeyIdentifier}: ${e.message}`);\n }\n terminal.writeDebugLine(`Renewed ${lockKeyIdentifier} expires in ${lockExpireTimeInSeconds} seconds`);\n }\n\n public async setCompletedStateAsync(\n context: ICobuildContext,\n state: ICobuildCompletedState\n ): Promise<void> {\n const { _terminal: terminal } = this;\n const { completedStateKey: key } = context;\n const value: string = this._serializeCompletedState(state);\n const completedStateKeyIdentifier: string = this._getCompletedStateKeyIdentifier(context);\n try {\n await this._redisClient.set(key, value);\n } catch (e) {\n throw new Error(`Failed to set ${completedStateKeyIdentifier}: ${e.message}`);\n }\n terminal.writeDebugLine(`Set ${completedStateKeyIdentifier}: ${value}`);\n }\n\n public async getCompletedStateAsync(context: ICobuildContext): Promise<ICobuildCompletedState | undefined> {\n const { _terminal: terminal } = this;\n const { completedStateKey: key } = context;\n const completedStateKeyIdentifier: string = this._getCompletedStateKeyIdentifier(context);\n let state: ICobuildCompletedState | undefined;\n try {\n const value: string | null = await this._redisClient.get(key);\n if (value) {\n state = this._deserializeCompletedState(value);\n }\n terminal.writeDebugLine(`Get ${completedStateKeyIdentifier}: ${value}`);\n } catch (e) {\n throw new Error(`Failed to get ${completedStateKeyIdentifier}: ${e.message}`);\n }\n return state;\n }\n\n private _serializeCompletedState(state: ICobuildCompletedState): string {\n // Example: SUCCESS;1234567890\n // Example: FAILURE;1234567890\n const { status, cacheId } = state;\n return [status, cacheId].join(COMPLETED_STATE_SEPARATOR);\n }\n\n private _deserializeCompletedState(state: string): ICobuildCompletedState | undefined {\n const [status, cacheId] = state.split(COMPLETED_STATE_SEPARATOR);\n return { status: status as ICobuildCompletedState['status'], cacheId };\n }\n\n private _getLockKeyIdentifier(context: ICobuildContext): string {\n let lockKeyIdentifier: string | undefined = this._lockKeyIdentifierMap.get(context);\n if (lockKeyIdentifier === undefined) {\n const { lockKey, packageName, phaseName } = context;\n lockKeyIdentifier = `lock(${lockKey})_package(${packageName})_phase(${phaseName})`;\n this._lockKeyIdentifierMap.set(context, lockKeyIdentifier);\n }\n return lockKeyIdentifier;\n }\n\n private _getCompletedStateKeyIdentifier(context: ICobuildContext): string {\n let completedStateKeyIdentifier: string | undefined = this._completedStateKeyIdentifierMap.get(context);\n if (completedStateKeyIdentifier === undefined) {\n const { completedStateKey, packageName, phaseName } = context;\n completedStateKeyIdentifier = `completed_state(${completedStateKey})_package(${packageName})_phase(${phaseName})`;\n this._completedStateKeyIdentifierMap.set(context, completedStateKeyIdentifier);\n }\n return completedStateKeyIdentifier;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rushstack/rush-redis-cobuild-plugin",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.177.0",
|
|
4
4
|
"description": "Rush plugin for Redis cobuild lock",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,15 +38,15 @@
|
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@redis/client": "~5.8.2",
|
|
41
|
-
"@rushstack/
|
|
42
|
-
"@rushstack/
|
|
41
|
+
"@rushstack/node-core-library": "5.23.1",
|
|
42
|
+
"@rushstack/rush-sdk": "5.177.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"eslint": "~9.37.0",
|
|
46
|
-
"@
|
|
47
|
-
"
|
|
48
|
-
"@rushstack/
|
|
49
|
-
"
|
|
46
|
+
"@rushstack/heft": "1.2.19",
|
|
47
|
+
"@microsoft/rush-lib": "5.177.0",
|
|
48
|
+
"@rushstack/terminal": "0.24.0",
|
|
49
|
+
"local-node-rig": "1.0.0"
|
|
50
50
|
},
|
|
51
51
|
"sideEffects": false,
|
|
52
52
|
"scripts": {
|