@zintrust/workers 0.1.30 → 0.1.43
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/ClusterLock.js +3 -2
- package/dist/DeadLetterQueue.js +3 -2
- package/dist/HealthMonitor.js +24 -13
- package/dist/Observability.js +8 -0
- package/dist/WorkerFactory.d.ts +4 -0
- package/dist/WorkerFactory.js +384 -42
- package/dist/WorkerInit.js +122 -43
- package/dist/WorkerMetrics.js +5 -1
- package/dist/WorkerRegistry.js +8 -0
- package/dist/WorkerShutdown.d.ts +0 -13
- package/dist/WorkerShutdown.js +1 -44
- package/dist/build-manifest.json +99 -83
- package/dist/config/workerConfig.d.ts +1 -0
- package/dist/config/workerConfig.js +7 -1
- package/dist/createQueueWorker.js +281 -42
- package/dist/dashboard/workers-api.js +8 -1
- package/dist/http/WorkerController.js +90 -35
- package/dist/http/WorkerMonitoringService.js +29 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/routes/workers.js +10 -7
- package/dist/storage/WorkerStore.d.ts +6 -3
- package/dist/storage/WorkerStore.js +16 -0
- package/dist/telemetry/api/TelemetryMonitoringService.js +29 -2
- package/dist/ui/router/ui.js +58 -29
- package/dist/ui/workers/index.html +202 -0
- package/dist/ui/workers/main.js +1952 -0
- package/dist/ui/workers/styles.css +1350 -0
- package/dist/ui/workers/zintrust.svg +30 -0
- package/package.json +5 -5
- package/src/ClusterLock.ts +13 -7
- package/src/ComplianceManager.ts +3 -2
- package/src/DeadLetterQueue.ts +6 -4
- package/src/HealthMonitor.ts +33 -17
- package/src/Observability.ts +11 -0
- package/src/WorkerFactory.ts +446 -43
- package/src/WorkerInit.ts +167 -48
- package/src/WorkerMetrics.ts +14 -8
- package/src/WorkerRegistry.ts +11 -0
- package/src/WorkerShutdown.ts +1 -69
- package/src/config/workerConfig.ts +9 -1
- package/src/createQueueWorker.ts +428 -43
- package/src/dashboard/workers-api.ts +8 -1
- package/src/http/WorkerController.ts +111 -36
- package/src/http/WorkerMonitoringService.ts +35 -2
- package/src/index.ts +2 -3
- package/src/routes/workers.ts +10 -8
- package/src/storage/WorkerStore.ts +21 -3
- package/src/telemetry/api/TelemetryMonitoringService.ts +35 -2
- package/src/types/queue-monitor.d.ts +2 -1
- package/src/ui/router/EmbeddedAssets.ts +3 -0
- package/src/ui/router/ui.ts +57 -39
- package/src/WorkerShutdownDurableObject.ts +0 -64
package/src/WorkerInit.ts
CHANGED
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
* - Ensures graceful startup and shutdown
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { Env, Logger
|
|
11
|
+
import { Env, Logger } from '@zintrust/core';
|
|
12
12
|
import { ResourceMonitor } from './ResourceMonitor';
|
|
13
|
+
import type { WorkerPersistenceConfig } from './WorkerFactory';
|
|
13
14
|
import { WorkerFactory } from './WorkerFactory';
|
|
14
15
|
import { WorkerShutdown } from './WorkerShutdown';
|
|
16
|
+
import { keyPrefix } from './config/workerConfig';
|
|
15
17
|
|
|
16
18
|
// ============================================================================
|
|
17
19
|
// Types
|
|
@@ -105,6 +107,22 @@ function initializeResourceMonitoring(
|
|
|
105
107
|
return false;
|
|
106
108
|
}
|
|
107
109
|
|
|
110
|
+
const getPersistenceOverride = (driver: string): WorkerPersistenceConfig => {
|
|
111
|
+
if (driver === 'redis') {
|
|
112
|
+
return { driver: 'redis', keyPrefix: keyPrefix() };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (driver === 'memory') {
|
|
116
|
+
return { driver: 'memory' };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
driver: 'database',
|
|
121
|
+
connection: Env.get('WORKER_PERSISTENCE_DB_CONNECTION', 'default') ?? 'default',
|
|
122
|
+
table: Env.get('WORKER_PERSISTENCE_TABLE', 'zintrust_workers') ?? 'zintrust_workers',
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
108
126
|
/**
|
|
109
127
|
* Check if any workers have resource monitoring enabled
|
|
110
128
|
*/
|
|
@@ -120,6 +138,146 @@ function shouldStartResourceMonitoring(): boolean {
|
|
|
120
138
|
}
|
|
121
139
|
}
|
|
122
140
|
|
|
141
|
+
type AutoStartCandidate = {
|
|
142
|
+
name: string;
|
|
143
|
+
autoStart: boolean;
|
|
144
|
+
activeStatus?: boolean;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
type PersistenceOverride = WorkerPersistenceConfig;
|
|
148
|
+
|
|
149
|
+
type AutoStartTask = AutoStartCandidate & {
|
|
150
|
+
persistenceOverride: PersistenceOverride;
|
|
151
|
+
source: 'database' | 'redis' | 'memory';
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const resolveAutoStartCandidates = (records: AutoStartCandidate[]): AutoStartCandidate[] => {
|
|
155
|
+
return records.filter((record) => record.activeStatus !== false && record.autoStart === true);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const resolvePersistenceTargets = (): Array<{
|
|
159
|
+
source: 'database' | 'redis' | 'memory';
|
|
160
|
+
persistenceOverride: PersistenceOverride;
|
|
161
|
+
}> => {
|
|
162
|
+
const configuredDriver = (Env.get('WORKER_PERSISTENCE_DRIVER', 'memory') || '')
|
|
163
|
+
.toLowerCase()
|
|
164
|
+
.trim();
|
|
165
|
+
const targets: Array<{
|
|
166
|
+
source: 'database' | 'redis' | 'memory';
|
|
167
|
+
persistenceOverride: PersistenceOverride;
|
|
168
|
+
}> =
|
|
169
|
+
configuredDriver === 'database'
|
|
170
|
+
? [
|
|
171
|
+
{ source: 'database', persistenceOverride: getPersistenceOverride('database') },
|
|
172
|
+
{ source: 'redis', persistenceOverride: getPersistenceOverride('redis') },
|
|
173
|
+
{ source: 'memory', persistenceOverride: { driver: 'memory' } },
|
|
174
|
+
]
|
|
175
|
+
: [
|
|
176
|
+
{ source: 'redis', persistenceOverride: getPersistenceOverride('redis') },
|
|
177
|
+
{ source: 'memory', persistenceOverride: { driver: 'memory' } },
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
// Sort so the configured driver comes first (priority)
|
|
181
|
+
return targets.sort((a, b) => {
|
|
182
|
+
const aIsConfigured = a.persistenceOverride.driver === configuredDriver;
|
|
183
|
+
const bIsConfigured = b.persistenceOverride.driver === configuredDriver;
|
|
184
|
+
|
|
185
|
+
if (aIsConfigured && !bIsConfigured) return -1;
|
|
186
|
+
if (!aIsConfigured && bIsConfigured) return 1;
|
|
187
|
+
return 0;
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const collectAutoStartTasks = async (): Promise<AutoStartTask[]> => {
|
|
192
|
+
const targets = resolvePersistenceTargets();
|
|
193
|
+
const tasks: AutoStartTask[] = [];
|
|
194
|
+
const seenWorkerNames = new Set<string>();
|
|
195
|
+
|
|
196
|
+
for (const target of targets) {
|
|
197
|
+
try {
|
|
198
|
+
// eslint-disable-next-line no-await-in-loop
|
|
199
|
+
const records = await WorkerFactory.listPersistedRecords(target.persistenceOverride);
|
|
200
|
+
const candidates = resolveAutoStartCandidates(records);
|
|
201
|
+
|
|
202
|
+
Logger.debug('Auto-start discovery', {
|
|
203
|
+
source: target.source,
|
|
204
|
+
totalRecords: records.length,
|
|
205
|
+
candidateCount: candidates.length,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
for (const record of candidates) {
|
|
209
|
+
if (seenWorkerNames.has(record.name)) {
|
|
210
|
+
Logger.warn(
|
|
211
|
+
`Worker ${record.name} appears in multiple persistence stores; keeping first discovered source and skipping duplicate from ${target.source}.`
|
|
212
|
+
);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
seenWorkerNames.add(record.name);
|
|
217
|
+
tasks.push({
|
|
218
|
+
...record,
|
|
219
|
+
persistenceOverride: target.persistenceOverride,
|
|
220
|
+
source: target.source,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
225
|
+
Logger.warn(`Auto-start discovery failed for ${target.source} persistence: ${message}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return tasks;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const isWorkerTrulyRunning = async (name: string): Promise<boolean> => {
|
|
233
|
+
const existing = WorkerFactory.get(name);
|
|
234
|
+
if (!existing) return false;
|
|
235
|
+
|
|
236
|
+
const workerLike = existing.worker as {
|
|
237
|
+
isRunning?: () => boolean | Promise<boolean>;
|
|
238
|
+
isPaused?: () => boolean;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const isRunning =
|
|
242
|
+
typeof workerLike.isRunning === 'function'
|
|
243
|
+
? await Promise.resolve(workerLike.isRunning())
|
|
244
|
+
: false;
|
|
245
|
+
const isPaused = typeof workerLike.isPaused === 'function' ? workerLike.isPaused() : false;
|
|
246
|
+
return isRunning && !isPaused;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const autoStartOneWorker = async (
|
|
250
|
+
record: AutoStartTask
|
|
251
|
+
): Promise<{ name: string; started: boolean; skipped: boolean }> => {
|
|
252
|
+
const existing = WorkerFactory.get(record.name);
|
|
253
|
+
if (existing) {
|
|
254
|
+
try {
|
|
255
|
+
if (await isWorkerTrulyRunning(record.name)) {
|
|
256
|
+
return { name: record.name, started: false, skipped: true };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
Logger.warn(
|
|
260
|
+
`Worker ${record.name} was registered but not truly running. Restarting to recover from stale state.`
|
|
261
|
+
);
|
|
262
|
+
await WorkerFactory.restart(record.name, record.persistenceOverride);
|
|
263
|
+
return { name: record.name, started: true, skipped: false };
|
|
264
|
+
} catch (error) {
|
|
265
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
266
|
+
Logger.warn(`Auto-start recovery failed for worker ${record.name}: ${message}`);
|
|
267
|
+
return { name: record.name, started: false, skipped: false };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
await WorkerFactory.startFromPersisted(record.name, record.persistenceOverride);
|
|
273
|
+
return { name: record.name, started: true, skipped: false };
|
|
274
|
+
} catch (error) {
|
|
275
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
276
|
+
Logger.warn(`Auto-start failed for worker ${record.name}: ${message}`);
|
|
277
|
+
return { name: record.name, started: false, skipped: false };
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
123
281
|
/**
|
|
124
282
|
* Initialize the worker management system
|
|
125
283
|
*/
|
|
@@ -185,62 +343,23 @@ async function initialize(options: IWorkerInitOptions = {}): Promise<void> {
|
|
|
185
343
|
}
|
|
186
344
|
|
|
187
345
|
async function autoStartPersistedWorkers(): Promise<void> {
|
|
188
|
-
|
|
346
|
+
const envAutoStart = Env.getBool('WORKER_AUTO_START', false);
|
|
347
|
+
const shouldAutoStart = envAutoStart;
|
|
348
|
+
|
|
189
349
|
Logger.debug('Auto-start check', {
|
|
190
|
-
envAutoStart
|
|
191
|
-
|
|
350
|
+
envAutoStart,
|
|
351
|
+
shouldAutoStart,
|
|
192
352
|
});
|
|
193
353
|
|
|
194
|
-
if (
|
|
354
|
+
if (!shouldAutoStart) {
|
|
195
355
|
Logger.debug('Auto-start disabled - WORKER_AUTO_START is not true');
|
|
196
356
|
return;
|
|
197
357
|
}
|
|
198
358
|
|
|
199
359
|
try {
|
|
200
|
-
const
|
|
201
|
-
Logger.debug('Found persisted records', {
|
|
202
|
-
count: records.length,
|
|
203
|
-
records: records.map((r) => ({ name: r.name, autoStart: r.autoStart })),
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
const candidates = records.filter((record) => {
|
|
207
|
-
if (record.activeStatus === false) {
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
// If autoStart is explicitly true, always include
|
|
211
|
-
if (record.autoStart === true) {
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
// If autoStart is null or undefined and global auto-start is enabled, include
|
|
215
|
-
if (
|
|
216
|
-
(record.autoStart === null || record.autoStart === undefined) &&
|
|
217
|
-
workersConfig.defaultWorker?.autoStart === true
|
|
218
|
-
) {
|
|
219
|
-
return true;
|
|
220
|
-
}
|
|
360
|
+
const candidates = await collectAutoStartTasks();
|
|
221
361
|
|
|
222
|
-
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
Logger.debug('Auto-start candidates', {
|
|
226
|
-
count: candidates.length,
|
|
227
|
-
candidates: candidates.map((c) => c.name),
|
|
228
|
-
});
|
|
229
|
-
const results = await Promise.all(
|
|
230
|
-
candidates.map(async (record) => {
|
|
231
|
-
if (WorkerFactory.get(record.name)) {
|
|
232
|
-
return { name: record.name, started: false, skipped: true };
|
|
233
|
-
}
|
|
234
|
-
try {
|
|
235
|
-
await WorkerFactory.startFromPersisted(record.name);
|
|
236
|
-
return { name: record.name, started: true, skipped: false };
|
|
237
|
-
} catch (error) {
|
|
238
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
239
|
-
Logger.warn(`Auto-start failed for worker ${record.name}: ${message}`);
|
|
240
|
-
return { name: record.name, started: false, skipped: false };
|
|
241
|
-
}
|
|
242
|
-
})
|
|
243
|
-
);
|
|
362
|
+
const results = await Promise.all(candidates.map(async (record) => autoStartOneWorker(record)));
|
|
244
363
|
|
|
245
364
|
const startedCount = results.filter((item) => item.started).length;
|
|
246
365
|
const skippedCount = results.filter((item) => item.skipped).length;
|
package/src/WorkerMetrics.ts
CHANGED
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
createRedisConnection,
|
|
12
12
|
type RedisConfig,
|
|
13
13
|
} from '@zintrust/core';
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
|
|
15
|
+
type RedisConnection = ReturnType<typeof createRedisConnection>;
|
|
16
|
+
type RedisPipeline = ReturnType<RedisConnection['pipeline']>;
|
|
16
17
|
|
|
17
18
|
export type MetricType =
|
|
18
19
|
| 'processed'
|
|
@@ -96,14 +97,14 @@ const runInBatches = async <T>(
|
|
|
96
97
|
};
|
|
97
98
|
|
|
98
99
|
// Internal state
|
|
99
|
-
let redisClient:
|
|
100
|
+
let redisClient: RedisConnection | null = null;
|
|
100
101
|
let cachedConfig: RedisConfig | null = null;
|
|
101
102
|
let keepLoggin = 0;
|
|
102
103
|
|
|
103
104
|
/**
|
|
104
105
|
* Helper: Get valid Redis client
|
|
105
106
|
*/
|
|
106
|
-
const getValidClient = async (): Promise<
|
|
107
|
+
const getValidClient = async (): Promise<RedisConnection> => {
|
|
107
108
|
if (!cachedConfig) {
|
|
108
109
|
throw ErrorFactory.createWorkerError('WorkerMetrics not initialized. Call initialize() first.');
|
|
109
110
|
}
|
|
@@ -113,7 +114,12 @@ const getValidClient = async (): Promise<IORedis> => {
|
|
|
113
114
|
redisClient = createRedisConnection(cachedConfig);
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
const client = redisClient;
|
|
118
|
+
if (!client) {
|
|
119
|
+
throw ErrorFactory.createConnectionError('Failed to initialize Redis client');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return client;
|
|
117
123
|
};
|
|
118
124
|
|
|
119
125
|
/**
|
|
@@ -166,7 +172,7 @@ const roundTimestamp = (date: Date, granularity: MetricGranularity): Date => {
|
|
|
166
172
|
* Helper: Clean up old metrics based on retention policy
|
|
167
173
|
*/
|
|
168
174
|
const cleanupOldMetrics = async (
|
|
169
|
-
client:
|
|
175
|
+
client: RedisConnection,
|
|
170
176
|
key: string,
|
|
171
177
|
granularity: MetricGranularity
|
|
172
178
|
): Promise<void> => {
|
|
@@ -280,9 +286,9 @@ const handleUninitializedMetrics = (optionsList: MetricQueryOptions[]): Aggregat
|
|
|
280
286
|
* Helper: Build Redis pipeline for batch metrics query
|
|
281
287
|
*/
|
|
282
288
|
const buildMetricsPipeline = (
|
|
283
|
-
client:
|
|
289
|
+
client: RedisConnection,
|
|
284
290
|
optionsList: MetricQueryOptions[]
|
|
285
|
-
):
|
|
291
|
+
): RedisPipeline => {
|
|
286
292
|
const pipeline = client.pipeline();
|
|
287
293
|
|
|
288
294
|
for (const options of optionsList) {
|
package/src/WorkerRegistry.ts
CHANGED
|
@@ -82,6 +82,13 @@ const registrations = new Map<string, RegisterWorkerOptions>();
|
|
|
82
82
|
const STOPPED_WORKER_CLEANUP_DELAY = 5 * 60 * 1000; // 5 minutes
|
|
83
83
|
const cleanupTimers = new Map<string, NodeJS.Timeout>();
|
|
84
84
|
|
|
85
|
+
type UnrefableTimer = { unref: () => void };
|
|
86
|
+
|
|
87
|
+
const isUnrefableTimer = (value: unknown): value is UnrefableTimer => {
|
|
88
|
+
if (typeof value !== 'object' || value === null) return false;
|
|
89
|
+
return 'unref' in value && typeof (value as UnrefableTimer).unref === 'function';
|
|
90
|
+
};
|
|
91
|
+
|
|
85
92
|
/**
|
|
86
93
|
* Helper: Schedule cleanup of stopped worker
|
|
87
94
|
*/
|
|
@@ -109,6 +116,10 @@ const scheduleStoppedWorkerCleanup = (name: string): void => {
|
|
|
109
116
|
}
|
|
110
117
|
}, STOPPED_WORKER_CLEANUP_DELAY);
|
|
111
118
|
|
|
119
|
+
if (isUnrefableTimer(timer)) {
|
|
120
|
+
timer.unref();
|
|
121
|
+
}
|
|
122
|
+
|
|
112
123
|
cleanupTimers.set(name, timer);
|
|
113
124
|
};
|
|
114
125
|
|
package/src/WorkerShutdown.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Coordinates orderly shutdown of all worker modules and the WorkerFactory.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { Logger } from '@zintrust/core';
|
|
9
9
|
import { WorkerFactory } from './WorkerFactory';
|
|
10
10
|
|
|
11
11
|
// ============================================================================
|
|
@@ -36,12 +36,6 @@ interface IShutdownState {
|
|
|
36
36
|
reason: string | null;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
type DurableShutdownState = {
|
|
40
|
-
shuttingDown: boolean;
|
|
41
|
-
startedAt?: string;
|
|
42
|
-
reason?: string;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
39
|
// ============================================================================
|
|
46
40
|
// Implementation
|
|
47
41
|
// ============================================================================
|
|
@@ -53,58 +47,6 @@ const state: IShutdownState = {
|
|
|
53
47
|
reason: null,
|
|
54
48
|
};
|
|
55
49
|
|
|
56
|
-
const getDurableShutdownStub = (): {
|
|
57
|
-
fetch: (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
58
|
-
} | null => {
|
|
59
|
-
const env = Cloudflare.getWorkersEnv();
|
|
60
|
-
if (env === null) return null;
|
|
61
|
-
|
|
62
|
-
const namespace = env['WORKER_SHUTDOWN'] as
|
|
63
|
-
| {
|
|
64
|
-
idFromName?: (name: string) => unknown;
|
|
65
|
-
get?: (id: unknown) => {
|
|
66
|
-
fetch: (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
| undefined;
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
!namespace ||
|
|
73
|
-
typeof namespace.idFromName !== 'function' ||
|
|
74
|
-
typeof namespace.get !== 'function'
|
|
75
|
-
) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const id = namespace.idFromName('zintrust-shutdown');
|
|
80
|
-
return namespace.get(id) ?? null;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const requestDurableShutdown = async (reason = 'manual'): Promise<boolean> => {
|
|
84
|
-
const stub = getDurableShutdownStub();
|
|
85
|
-
if (!stub) {
|
|
86
|
-
Logger.warn('Worker shutdown Durable Object binding not configured');
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const res = await stub.fetch('https://worker-shutdown/shutdown', {
|
|
91
|
-
method: 'POST',
|
|
92
|
-
headers: { 'content-type': 'application/json' },
|
|
93
|
-
body: JSON.stringify({ reason }),
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
return res.ok;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const getDurableShutdownState = async (): Promise<DurableShutdownState | null> => {
|
|
100
|
-
const stub = getDurableShutdownStub();
|
|
101
|
-
if (!stub) return null;
|
|
102
|
-
|
|
103
|
-
const res = await stub.fetch('https://worker-shutdown/status');
|
|
104
|
-
if (!res.ok) return null;
|
|
105
|
-
return (await res.json()) as DurableShutdownState;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
50
|
let shutdownHandlersRegistered = false;
|
|
109
51
|
|
|
110
52
|
const signalHandlers: {
|
|
@@ -300,14 +242,4 @@ export const WorkerShutdown = Object.freeze({
|
|
|
300
242
|
* Get current shutdown state
|
|
301
243
|
*/
|
|
302
244
|
getShutdownState,
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Request shutdown via Durable Object (Workers)
|
|
306
|
-
*/
|
|
307
|
-
requestDurableShutdown,
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Read shutdown state from Durable Object (Workers)
|
|
311
|
-
*/
|
|
312
|
-
getDurableShutdownState,
|
|
313
245
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Env } from '@zintrust/core';
|
|
1
|
+
import { Env, appConfig } from '@zintrust/core';
|
|
2
2
|
|
|
3
3
|
const normalizeBaseUrl = (value: string): string => {
|
|
4
4
|
let end = value.length;
|
|
@@ -23,3 +23,11 @@ const resolveWorkerApiUrl = (): string => {
|
|
|
23
23
|
export const WorkerConfig = Object.freeze({
|
|
24
24
|
getWorkerBaseUrl: resolveWorkerApiUrl,
|
|
25
25
|
});
|
|
26
|
+
|
|
27
|
+
export const keyPrefix = (): string => {
|
|
28
|
+
const redisKeyPrefix = (Env.get('WORKER_PERSISTENCE_REDIS_KEY_PREFIX', '') ?? '').trim();
|
|
29
|
+
|
|
30
|
+
return redisKeyPrefix
|
|
31
|
+
? `${redisKeyPrefix}_worker_${appConfig.prefix}`
|
|
32
|
+
: `worker_${appConfig.prefix}`;
|
|
33
|
+
};
|