@rushstack/rush-redis-cobuild-plugin 5.97.1-pr3949.2 → 5.102.0-pr3949.4
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/dist/rush-redis-cobuild-plugin.d.ts +14 -17
- package/lib/RedisCobuildLockProvider.d.ts +10 -13
- package/lib/RedisCobuildLockProvider.d.ts.map +1 -1
- package/lib/RedisCobuildLockProvider.js +61 -46
- package/lib/RedisCobuildLockProvider.js.map +1 -1
- package/lib/RushRedisCobuildPlugin.d.ts +1 -1
- package/lib/RushRedisCobuildPlugin.d.ts.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +11 -11
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
3
|
+
import { ICobuildCompletedState } from '@rushstack/rush-sdk';
|
|
4
|
+
import { ICobuildContext } from '@rushstack/rush-sdk';
|
|
5
|
+
import { ICobuildLockProvider } from '@rushstack/rush-sdk';
|
|
6
6
|
import type { IRushPlugin } from '@rushstack/rush-sdk';
|
|
7
7
|
import type { RedisClientOptions } from '@redis/client';
|
|
8
8
|
import type { RushConfiguration } from '@rushstack/rush-sdk';
|
|
9
|
-
import
|
|
9
|
+
import { RushSession } from '@rushstack/rush-sdk';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* The redis client options
|
|
@@ -22,7 +22,7 @@ export declare interface IRedisCobuildLockProviderOptions extends RedisClientOpt
|
|
|
22
22
|
/**
|
|
23
23
|
* @public
|
|
24
24
|
*/
|
|
25
|
-
declare type IRushRedisCobuildPluginOptions = IRedisCobuildLockProviderOptions;
|
|
25
|
+
export declare type IRushRedisCobuildPluginOptions = IRedisCobuildLockProviderOptions;
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* @beta
|
|
@@ -30,29 +30,26 @@ declare type IRushRedisCobuildPluginOptions = IRedisCobuildLockProviderOptions;
|
|
|
30
30
|
export declare class RedisCobuildLockProvider implements ICobuildLockProvider {
|
|
31
31
|
private readonly _options;
|
|
32
32
|
private readonly _terminal;
|
|
33
|
+
private readonly _lockKeyIdentifierMap;
|
|
34
|
+
private readonly _completedStateKeyIdentifierMap;
|
|
33
35
|
private readonly _redisClient;
|
|
34
|
-
private readonly _lockKeyMap;
|
|
35
|
-
private readonly _completedKeyMap;
|
|
36
36
|
constructor(options: IRedisCobuildLockProviderOptions, rushSession: RushSession);
|
|
37
37
|
static expandOptionsWithEnvironmentVariables(options: IRedisCobuildLockProviderOptions, environment?: NodeJS.ProcessEnv): IRedisCobuildLockProviderOptions;
|
|
38
38
|
connectAsync(): Promise<void>;
|
|
39
39
|
disconnectAsync(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Acquiring the lock based on the specific context.
|
|
42
|
+
*
|
|
43
|
+
* NOTE: this is a reentrant lock implementation
|
|
44
|
+
*/
|
|
40
45
|
acquireLockAsync(context: ICobuildContext): Promise<boolean>;
|
|
41
46
|
renewLockAsync(context: ICobuildContext): Promise<void>;
|
|
42
47
|
setCompletedStateAsync(context: ICobuildContext, state: ICobuildCompletedState): Promise<void>;
|
|
43
48
|
getCompletedStateAsync(context: ICobuildContext): Promise<ICobuildCompletedState | undefined>;
|
|
44
|
-
/**
|
|
45
|
-
* Returns the lock key for the given context
|
|
46
|
-
* Example: cobuild:v1:<contextId>:<cacheId>:lock
|
|
47
|
-
*/
|
|
48
|
-
getLockKey(context: ICobuildContext): string;
|
|
49
|
-
/**
|
|
50
|
-
* Returns the completed key for the given context
|
|
51
|
-
* Example: cobuild:v1:<contextId>:<cacheId>:completed
|
|
52
|
-
*/
|
|
53
|
-
getCompletedStateKey(context: ICobuildContext): string;
|
|
54
49
|
private _serializeCompletedState;
|
|
55
50
|
private _deserializeCompletedState;
|
|
51
|
+
private _getLockKeyIdentifier;
|
|
52
|
+
private _getCompletedStateKeyIdentifier;
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import
|
|
2
|
+
import { ICobuildLockProvider, ICobuildContext, ICobuildCompletedState, RushSession } from '@rushstack/rush-sdk';
|
|
3
3
|
import type { RedisClientOptions } from '@redis/client';
|
|
4
4
|
/**
|
|
5
5
|
* The redis client options
|
|
@@ -17,28 +17,25 @@ export interface IRedisCobuildLockProviderOptions extends RedisClientOptions {
|
|
|
17
17
|
export declare class RedisCobuildLockProvider implements ICobuildLockProvider {
|
|
18
18
|
private readonly _options;
|
|
19
19
|
private readonly _terminal;
|
|
20
|
+
private readonly _lockKeyIdentifierMap;
|
|
21
|
+
private readonly _completedStateKeyIdentifierMap;
|
|
20
22
|
private readonly _redisClient;
|
|
21
|
-
private readonly _lockKeyMap;
|
|
22
|
-
private readonly _completedKeyMap;
|
|
23
23
|
constructor(options: IRedisCobuildLockProviderOptions, rushSession: RushSession);
|
|
24
24
|
static expandOptionsWithEnvironmentVariables(options: IRedisCobuildLockProviderOptions, environment?: NodeJS.ProcessEnv): IRedisCobuildLockProviderOptions;
|
|
25
25
|
connectAsync(): Promise<void>;
|
|
26
26
|
disconnectAsync(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Acquiring the lock based on the specific context.
|
|
29
|
+
*
|
|
30
|
+
* NOTE: this is a reentrant lock implementation
|
|
31
|
+
*/
|
|
27
32
|
acquireLockAsync(context: ICobuildContext): Promise<boolean>;
|
|
28
33
|
renewLockAsync(context: ICobuildContext): Promise<void>;
|
|
29
34
|
setCompletedStateAsync(context: ICobuildContext, state: ICobuildCompletedState): Promise<void>;
|
|
30
35
|
getCompletedStateAsync(context: ICobuildContext): Promise<ICobuildCompletedState | undefined>;
|
|
31
|
-
/**
|
|
32
|
-
* Returns the lock key for the given context
|
|
33
|
-
* Example: cobuild:v1:<contextId>:<cacheId>:lock
|
|
34
|
-
*/
|
|
35
|
-
getLockKey(context: ICobuildContext): string;
|
|
36
|
-
/**
|
|
37
|
-
* Returns the completed key for the given context
|
|
38
|
-
* Example: cobuild:v1:<contextId>:<cacheId>:completed
|
|
39
|
-
*/
|
|
40
|
-
getCompletedStateKey(context: ICobuildContext): string;
|
|
41
36
|
private _serializeCompletedState;
|
|
42
37
|
private _deserializeCompletedState;
|
|
38
|
+
private _getLockKeyIdentifier;
|
|
39
|
+
private _getCompletedStateKeyIdentifier;
|
|
43
40
|
}
|
|
44
41
|
//# sourceMappingURL=RedisCobuildLockProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisCobuildLockProvider.d.ts","sourceRoot":"","sources":["../src/RedisCobuildLockProvider.ts"],"names":[],"mappings":";AAKA,OAAO,
|
|
1
|
+
{"version":3,"file":"RedisCobuildLockProvider.d.ts","sourceRoot":"","sources":["../src/RedisCobuildLockProvider.ts"],"names":[],"mappings":";AAKA,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,kBAAkB,EAKnB,MAAM,eAAe,CAAC;AAGvB;;;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,CAA8D;gBAExE,OAAO,EAAE,gCAAgC,EAAE,WAAW,EAAE,WAAW;WAUxE,qCAAqC,CACjD,OAAO,EAAE,gCAAgC,EACzC,WAAW,GAAE,MAAM,CAAC,UAAwB,GAC3C,gCAAgC;IA0BtB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAU7B,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"}
|
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.RedisCobuildLockProvider = void 0;
|
|
6
6
|
const client_1 = require("@redis/client");
|
|
7
|
-
const KEY_SEPARATOR = ':';
|
|
8
7
|
const COMPLETED_STATE_SEPARATOR = ';';
|
|
9
8
|
/**
|
|
10
9
|
* @beta
|
|
11
10
|
*/
|
|
12
11
|
class RedisCobuildLockProvider {
|
|
13
12
|
constructor(options, rushSession) {
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
13
|
+
this._lockKeyIdentifierMap = new WeakMap();
|
|
14
|
+
this._completedStateKeyIdentifierMap = new WeakMap();
|
|
16
15
|
this._options = RedisCobuildLockProvider.expandOptionsWithEnvironmentVariables(options);
|
|
17
16
|
this._terminal = rushSession.getLogger('RedisCobuildLockProvider').terminal;
|
|
18
17
|
try {
|
|
@@ -58,97 +57,113 @@ class RedisCobuildLockProvider {
|
|
|
58
57
|
throw new Error(`Failed to disconnect to redis server: ${e.message}`);
|
|
59
58
|
}
|
|
60
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Acquiring the lock based on the specific context.
|
|
62
|
+
*
|
|
63
|
+
* NOTE: this is a reentrant lock implementation
|
|
64
|
+
*/
|
|
61
65
|
async acquireLockAsync(context) {
|
|
62
66
|
const { _terminal: terminal } = this;
|
|
63
|
-
const lockKey =
|
|
67
|
+
const { lockKey, lockExpireTimeInSeconds, runnerId } = context;
|
|
64
68
|
let result = false;
|
|
69
|
+
const lockKeyIdentifier = this._getLockKeyIdentifier(context);
|
|
65
70
|
try {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
// According to the doc, the reply of set command is either "OK" or nil. The reply doesn't matter
|
|
72
|
+
await this._redisClient.set(lockKey, runnerId, {
|
|
73
|
+
NX: true,
|
|
74
|
+
// call EXPIRE in an atomic command
|
|
75
|
+
EX: lockExpireTimeInSeconds
|
|
76
|
+
// Do not specify GET here since using NX ane GET together requires Redis@7.
|
|
77
|
+
});
|
|
78
|
+
// Just read the value by lock key to see wether it equals current runner id
|
|
79
|
+
const value = await this._redisClient.get(lockKey);
|
|
80
|
+
if (value === null) {
|
|
81
|
+
// This should not happen.
|
|
82
|
+
throw new Error(`Get redis key failed: ${lockKey}`);
|
|
83
|
+
}
|
|
84
|
+
result = value === runnerId;
|
|
69
85
|
if (result) {
|
|
70
|
-
|
|
86
|
+
terminal.writeDebugLine(`Successfully acquired ${lockKeyIdentifier} to runner(${runnerId}) and it expires in ${lockExpireTimeInSeconds}s`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
terminal.writeDebugLine(`Failed to acquire ${lockKeyIdentifier}, locked by runner ${value}`);
|
|
71
90
|
}
|
|
72
91
|
}
|
|
73
92
|
catch (e) {
|
|
74
|
-
throw new Error(`
|
|
93
|
+
throw new Error(`Error occurs when acquiring ${lockKeyIdentifier}: ${e.message}`);
|
|
75
94
|
}
|
|
76
95
|
return result;
|
|
77
96
|
}
|
|
78
97
|
async renewLockAsync(context) {
|
|
79
98
|
const { _terminal: terminal } = this;
|
|
80
|
-
const lockKey =
|
|
99
|
+
const { lockKey, lockExpireTimeInSeconds } = context;
|
|
100
|
+
const lockKeyIdentifier = this._getLockKeyIdentifier(context);
|
|
81
101
|
try {
|
|
82
|
-
await this._redisClient.expire(lockKey,
|
|
102
|
+
await this._redisClient.expire(lockKey, lockExpireTimeInSeconds);
|
|
83
103
|
}
|
|
84
104
|
catch (e) {
|
|
85
|
-
throw new Error(`Failed to renew
|
|
105
|
+
throw new Error(`Failed to renew ${lockKeyIdentifier}: ${e.message}`);
|
|
86
106
|
}
|
|
87
|
-
terminal.writeDebugLine(`Renewed
|
|
107
|
+
terminal.writeDebugLine(`Renewed ${lockKeyIdentifier} expires in ${lockExpireTimeInSeconds} seconds`);
|
|
88
108
|
}
|
|
89
109
|
async setCompletedStateAsync(context, state) {
|
|
90
110
|
const { _terminal: terminal } = this;
|
|
91
|
-
const key =
|
|
111
|
+
const { completedStateKey: key } = context;
|
|
92
112
|
const value = this._serializeCompletedState(state);
|
|
113
|
+
const completedStateKeyIdentifier = this._getCompletedStateKeyIdentifier(context);
|
|
93
114
|
try {
|
|
94
115
|
await this._redisClient.set(key, value);
|
|
95
116
|
}
|
|
96
117
|
catch (e) {
|
|
97
|
-
throw new Error(`Failed to set
|
|
118
|
+
throw new Error(`Failed to set ${completedStateKeyIdentifier}: ${e.message}`);
|
|
98
119
|
}
|
|
99
|
-
terminal.writeDebugLine(`Set
|
|
120
|
+
terminal.writeDebugLine(`Set ${completedStateKeyIdentifier}: ${value}`);
|
|
100
121
|
}
|
|
101
122
|
async getCompletedStateAsync(context) {
|
|
102
123
|
const { _terminal: terminal } = this;
|
|
103
|
-
const key =
|
|
124
|
+
const { completedStateKey: key } = context;
|
|
125
|
+
const completedStateKeyIdentifier = this._getCompletedStateKeyIdentifier(context);
|
|
104
126
|
let state;
|
|
105
127
|
try {
|
|
106
128
|
const value = await this._redisClient.get(key);
|
|
107
129
|
if (value) {
|
|
108
130
|
state = this._deserializeCompletedState(value);
|
|
109
131
|
}
|
|
110
|
-
terminal.writeDebugLine(`Get
|
|
132
|
+
terminal.writeDebugLine(`Get ${completedStateKeyIdentifier}: ${value}`);
|
|
111
133
|
}
|
|
112
134
|
catch (e) {
|
|
113
|
-
throw new Error(`Failed to get
|
|
135
|
+
throw new Error(`Failed to get ${completedStateKeyIdentifier}: ${e.message}`);
|
|
114
136
|
}
|
|
115
137
|
return state;
|
|
116
138
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Returns the lock key for the given context
|
|
119
|
-
* Example: cobuild:v1:<contextId>:<cacheId>:lock
|
|
120
|
-
*/
|
|
121
|
-
getLockKey(context) {
|
|
122
|
-
const { version, contextId, cacheId } = context;
|
|
123
|
-
let lockKey = this._lockKeyMap.get(context);
|
|
124
|
-
if (!lockKey) {
|
|
125
|
-
lockKey = ['cobuild', `v${version}`, contextId, cacheId, 'lock'].join(KEY_SEPARATOR);
|
|
126
|
-
this._lockKeyMap.set(context, lockKey);
|
|
127
|
-
}
|
|
128
|
-
return lockKey;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Returns the completed key for the given context
|
|
132
|
-
* Example: cobuild:v1:<contextId>:<cacheId>:completed
|
|
133
|
-
*/
|
|
134
|
-
getCompletedStateKey(context) {
|
|
135
|
-
const { version, contextId, cacheId } = context;
|
|
136
|
-
let completedKey = this._completedKeyMap.get(context);
|
|
137
|
-
if (!completedKey) {
|
|
138
|
-
completedKey = ['cobuild', `v${version}`, contextId, cacheId, 'completed'].join(KEY_SEPARATOR);
|
|
139
|
-
this._completedKeyMap.set(context, completedKey);
|
|
140
|
-
}
|
|
141
|
-
return completedKey;
|
|
142
|
-
}
|
|
143
139
|
_serializeCompletedState(state) {
|
|
144
140
|
// Example: SUCCESS;1234567890
|
|
145
141
|
// Example: FAILURE;1234567890
|
|
146
|
-
|
|
142
|
+
const { status, cacheId } = state;
|
|
143
|
+
return [status, cacheId].join(COMPLETED_STATE_SEPARATOR);
|
|
147
144
|
}
|
|
148
145
|
_deserializeCompletedState(state) {
|
|
149
146
|
const [status, cacheId] = state.split(COMPLETED_STATE_SEPARATOR);
|
|
150
147
|
return { status: status, cacheId };
|
|
151
148
|
}
|
|
149
|
+
_getLockKeyIdentifier(context) {
|
|
150
|
+
let lockKeyIdentifier = this._lockKeyIdentifierMap.get(context);
|
|
151
|
+
if (lockKeyIdentifier === undefined) {
|
|
152
|
+
const { lockKey, packageName, phaseName } = context;
|
|
153
|
+
lockKeyIdentifier = `lock(${lockKey})_package(${packageName})_phase(${phaseName})`;
|
|
154
|
+
this._lockKeyIdentifierMap.set(context, lockKeyIdentifier);
|
|
155
|
+
}
|
|
156
|
+
return lockKeyIdentifier;
|
|
157
|
+
}
|
|
158
|
+
_getCompletedStateKeyIdentifier(context) {
|
|
159
|
+
let completedStateKeyIdentifier = this._completedStateKeyIdentifierMap.get(context);
|
|
160
|
+
if (completedStateKeyIdentifier === undefined) {
|
|
161
|
+
const { completedStateKey, packageName, phaseName } = context;
|
|
162
|
+
completedStateKeyIdentifier = `completed_state(${completedStateKey})_package(${packageName})_phase(${phaseName})`;
|
|
163
|
+
this._completedStateKeyIdentifierMap.set(context, completedStateKeyIdentifier);
|
|
164
|
+
}
|
|
165
|
+
return completedStateKeyIdentifier;
|
|
166
|
+
}
|
|
152
167
|
}
|
|
153
168
|
exports.RedisCobuildLockProvider = RedisCobuildLockProvider;
|
|
154
169
|
//# sourceMappingURL=RedisCobuildLockProvider.js.map
|
|
@@ -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';\n\nimport type {\n ICobuildLockProvider,\n ICobuildContext,\n ICobuildCompletedState,\n RushSession\n} from '@rushstack/rush-sdk';\nimport type {\n RedisClientOptions,\n RedisClientType,\n RedisFunctions,\n RedisModules,\n RedisScripts\n} from '@redis/client';\nimport type { ITerminal } from '@rushstack/node-core-library';\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 KEY_SEPARATOR: ':' = ':';\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\n private readonly _redisClient: RedisClientType<RedisModules, RedisFunctions, RedisScripts>;\n private readonly _lockKeyMap: WeakMap<ICobuildContext, string> = new WeakMap<ICobuildContext, string>();\n private readonly _completedKeyMap: WeakMap<ICobuildContext, string> = new WeakMap<\n ICobuildContext,\n string\n >();\n\n public constructor(options: IRedisCobuildLockProviderOptions, rushSession: RushSession) {\n this._options = RedisCobuildLockProvider.expandOptionsWithEnvironmentVariables(options);\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\n public async disconnectAsync(): Promise<void> {\n try {\n await this._redisClient.disconnect();\n } catch (e) {\n throw new Error(`Failed to disconnect to redis server: ${e.message}`);\n }\n }\n\n public async acquireLockAsync(context: ICobuildContext): Promise<boolean> {\n const { _terminal: terminal } = this;\n const lockKey: string = this.getLockKey(context);\n let result: boolean = false;\n try {\n const incrResult: number = await this._redisClient.incr(lockKey);\n result = incrResult === 1;\n terminal.writeDebugLine(`Acquired lock for ${lockKey}: ${incrResult}, 1 is success`);\n if (result) {\n await this.renewLockAsync(context);\n }\n } catch (e) {\n throw new Error(`Failed to acquire lock for ${lockKey}: ${e.message}`);\n }\n return result;\n }\n\n public async renewLockAsync(context: ICobuildContext): Promise<void> {\n const { _terminal: terminal } = this;\n const lockKey: string = this.getLockKey(context);\n try {\n await this._redisClient.expire(lockKey, 30);\n } catch (e) {\n throw new Error(`Failed to renew lock for ${lockKey}: ${e.message}`);\n }\n terminal.writeDebugLine(`Renewed lock for ${lockKey}`);\n }\n\n public async setCompletedStateAsync(\n context: ICobuildContext,\n state: ICobuildCompletedState\n ): Promise<void> {\n const { _terminal: terminal } = this;\n const key: string = this.getCompletedStateKey(context);\n const value: string = this._serializeCompletedState(state);\n try {\n await this._redisClient.set(key, value);\n } catch (e) {\n throw new Error(`Failed to set completed state for ${key}: ${e.message}`);\n }\n terminal.writeDebugLine(`Set completed state for ${key}: ${value}`);\n }\n\n public async getCompletedStateAsync(context: ICobuildContext): Promise<ICobuildCompletedState | undefined> {\n const { _terminal: terminal } = this;\n const key: string = this.getCompletedStateKey(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 completed state for ${key}: ${value}`);\n } catch (e) {\n throw new Error(`Failed to get completed state for ${key}: ${e.message}`);\n }\n return state;\n }\n\n /**\n * Returns the lock key for the given context\n * Example: cobuild:v1:<contextId>:<cacheId>:lock\n */\n public getLockKey(context: ICobuildContext): string {\n const { version, contextId, cacheId } = context;\n let lockKey: string | undefined = this._lockKeyMap.get(context);\n if (!lockKey) {\n lockKey = ['cobuild', `v${version}`, contextId, cacheId, 'lock'].join(KEY_SEPARATOR);\n this._lockKeyMap.set(context, lockKey);\n }\n return lockKey;\n }\n\n /**\n * Returns the completed key for the given context\n * Example: cobuild:v1:<contextId>:<cacheId>:completed\n */\n public getCompletedStateKey(context: ICobuildContext): string {\n const { version, contextId, cacheId } = context;\n let completedKey: string | undefined = this._completedKeyMap.get(context);\n if (!completedKey) {\n completedKey = ['cobuild', `v${version}`, contextId, cacheId, 'completed'].join(KEY_SEPARATOR);\n this._completedKeyMap.set(context, completedKey);\n }\n return completedKey;\n }\n\n private _serializeCompletedState(state: ICobuildCompletedState): string {\n // Example: SUCCESS;1234567890\n // Example: FAILURE;1234567890\n return `${state.status}${COMPLETED_STATE_SEPARATOR}${state.cacheId}`;\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"]}
|
|
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';\n\nimport {\n ICobuildLockProvider,\n ICobuildContext,\n ICobuildCompletedState,\n RushSession\n} from '@rushstack/rush-sdk';\nimport type {\n RedisClientOptions,\n RedisClientType,\n RedisFunctions,\n RedisModules,\n RedisScripts\n} from '@redis/client';\nimport type { ITerminal } from '@rushstack/node-core-library';\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>;\n\n public constructor(options: IRedisCobuildLockProviderOptions, rushSession: RushSession) {\n this._options = RedisCobuildLockProvider.expandOptionsWithEnvironmentVariables(options);\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\n public async disconnectAsync(): Promise<void> {\n try {\n await this._redisClient.disconnect();\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"]}
|
|
@@ -3,7 +3,7 @@ import type { IRedisCobuildLockProviderOptions } from './RedisCobuildLockProvide
|
|
|
3
3
|
/**
|
|
4
4
|
* @public
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
6
|
+
export type IRushRedisCobuildPluginOptions = IRedisCobuildLockProviderOptions;
|
|
7
7
|
/**
|
|
8
8
|
* @public
|
|
9
9
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RushRedisCobuildPlugin.d.ts","sourceRoot":"","sources":["../src/RushRedisCobuildPlugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,KAAK,EAAE,gCAAgC,EAA4B,MAAM,4BAA4B,CAAC;AAS7G;;GAEG;AACH,
|
|
1
|
+
{"version":3,"file":"RushRedisCobuildPlugin.d.ts","sourceRoot":"","sources":["../src/RushRedisCobuildPlugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,KAAK,EAAE,gCAAgC,EAA4B,MAAM,4BAA4B,CAAC;AAS7G;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,gCAAgC,CAAC;AAE9E;;GAEG;AACH,qBAAa,sBAAuB,YAAW,WAAW;IACjD,UAAU,EAAE,MAAM,CAAe;IAExC,OAAO,CAAC,QAAQ,CAAiC;gBAE9B,OAAO,EAAE,8BAA8B;IAInD,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,GAAG,IAAI;CAQnF"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { RushRedisCobuildPlugin } from './RushRedisCobuildPlugin';
|
|
1
|
+
import { RushRedisCobuildPlugin, IRushRedisCobuildPluginOptions } from './RushRedisCobuildPlugin';
|
|
2
2
|
export default RushRedisCobuildPlugin;
|
|
3
3
|
export { RedisCobuildLockProvider } from './RedisCobuildLockProvider';
|
|
4
4
|
export { IRedisCobuildLockProviderOptions } from './RedisCobuildLockProvider';
|
|
5
|
+
export { IRushRedisCobuildPluginOptions };
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,sBAAsB,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAElG,eAAe,sBAAsB,CAAC;AACtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,gCAAgC,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,8BAA8B,EAAE,CAAC"}
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,qEAAkG;AAElG,kBAAe,+CAAsB,CAAC;AACtC,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA","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 { RushRedisCobuildPlugin, IRushRedisCobuildPluginOptions } from './RushRedisCobuildPlugin';\n\nexport default RushRedisCobuildPlugin;\nexport { RedisCobuildLockProvider } from './RedisCobuildLockProvider';\nexport { IRedisCobuildLockProviderOptions } from './RedisCobuildLockProvider';\nexport { IRushRedisCobuildPluginOptions };\n"]}
|
package/lib/tsdoc-metadata.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rushstack/rush-redis-cobuild-plugin",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.102.0-pr3949.4",
|
|
4
4
|
"description": "Rush plugin for Redis cobuild lock",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,22 +13,22 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@redis/client": "~1.5.5",
|
|
16
|
-
"@rushstack/node-core-library": "3.
|
|
17
|
-
"@rushstack/rush-sdk": "5.
|
|
16
|
+
"@rushstack/node-core-library": "3.59.7",
|
|
17
|
+
"@rushstack/rush-sdk": "5.102.0-pr3949.4"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/heft-jest": "1.0.1",
|
|
21
21
|
"@types/node": "14.18.36",
|
|
22
|
-
"@microsoft/rush-lib": "5.
|
|
23
|
-
"@rushstack/
|
|
24
|
-
"@rushstack/heft": "
|
|
25
|
-
"@rushstack/
|
|
22
|
+
"@microsoft/rush-lib": "5.102.0-pr3949.4",
|
|
23
|
+
"@rushstack/heft": "0.58.2",
|
|
24
|
+
"@rushstack/heft-node-rig": "2.2.22",
|
|
25
|
+
"@rushstack/eslint-config": "3.3.3"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "heft build --clean",
|
|
29
|
-
"start": "heft test
|
|
30
|
-
"test": "heft test",
|
|
31
|
-
"_phase:build": "heft build --clean",
|
|
32
|
-
"_phase:test": "heft test --
|
|
29
|
+
"start": "heft test-watch",
|
|
30
|
+
"test": "heft test --clean",
|
|
31
|
+
"_phase:build": "heft run --only build -- --clean",
|
|
32
|
+
"_phase:test": "heft run --only test -- --clean"
|
|
33
33
|
}
|
|
34
34
|
}
|