@nosana/node 1.1.25 → 1.1.27-rc
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/package.json +1 -1
- package/dist/src/NodeManager/node/api/ApiHandler.js +1 -2
- package/dist/src/NodeManager/node/api/routes/post/job-operation-actions.d.ts +0 -4
- package/dist/src/NodeManager/node/api/routes/post/job-operation-actions.js +2 -18
- package/dist/src/NodeManager/node/task/TaskManager.d.ts +2 -2
- package/dist/src/NodeManager/node/task/TaskManager.js +10 -6
- package/dist/src/NodeManager/node/task/operations/restartTaskManagerOperation.js +57 -33
- package/dist/src/NodeManager/node/task/operations/stopAllTaskManagerOperations.d.ts +11 -7
- package/dist/src/NodeManager/node/task/operations/stopAllTaskManagerOperations.js +11 -7
- package/dist/src/NodeManager/node/task/operations/stopTaskManagerOperation.js +78 -64
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/src/NodeManager/node/task/operations/moveTaskManagerGroupOperation.d.ts +0 -2
- package/dist/src/NodeManager/node/task/operations/moveTaskManagerGroupOperation.js +0 -65
package/dist/package.json
CHANGED
|
@@ -7,7 +7,7 @@ import { sleep } from '../../utils/utils.js';
|
|
|
7
7
|
import { stateStreaming } from '../../monitoring/streaming/StateStreamer.js';
|
|
8
8
|
import { applyLoggingProxyToClass } from '../../monitoring/proxy/loggingProxy.js';
|
|
9
9
|
import { verifyJobOwnerSignatureMiddleware, verifyWSJobOwnerSignatureMiddleware, verifyWSNodeOrJobOwnerSignatureMiddleware, } from './middlewares/index.js';
|
|
10
|
-
import { getNodeInfoRoute, getJobResultsRoute, getServiceUrlRoute, postJobDefinitionRoute, postServiceStopRoute, wssLogRoute, wssStatusRoute, getCurrentGroupStatusHandler, getGroupStatusHandler, getOperationsStatusHandler, getOperationStatusHandler, restartGroupOperationHandler, restartOperationHandler, stopGroupOperationHandler, stopOperationHandler, wssTaskManagerLogRoute,
|
|
10
|
+
import { getNodeInfoRoute, getJobResultsRoute, getServiceUrlRoute, postJobDefinitionRoute, postServiceStopRoute, wssLogRoute, wssStatusRoute, getCurrentGroupStatusHandler, getGroupStatusHandler, getOperationsStatusHandler, getOperationStatusHandler, restartGroupOperationHandler, restartOperationHandler, stopGroupOperationHandler, stopOperationHandler, wssTaskManagerLogRoute, getJobDefinitionRoute, getJobInfoRoute, getJobStatsRoute, getJobStatsStreamRoute, } from './routes/index.js';
|
|
11
11
|
import { NodeAlreadyActiveError } from '../../errors/NodeAlreadyActiveError.js';
|
|
12
12
|
export class ApiHandler {
|
|
13
13
|
constructor(sdk, repository, provider, port) {
|
|
@@ -147,7 +147,6 @@ export class ApiHandler {
|
|
|
147
147
|
this.api.get('/job/:jobId/stats/stream', getJobStatsStreamRoute);
|
|
148
148
|
// POST Routes
|
|
149
149
|
this.api.post('/job/:jobId/job-definition', postJobDefinitionRoute);
|
|
150
|
-
this.api.post('/job/:jobId/group/:group/move', moveGroupOperationHandler);
|
|
151
150
|
this.api.post('/job/:jobId/group/:group/operation/:opId/restart', restartOperationHandler);
|
|
152
151
|
this.api.post('/job/:jobId/group/:group/restart', restartGroupOperationHandler);
|
|
153
152
|
this.api.post('/job/:jobId/group/:group/operation/:opId/stop', stopOperationHandler);
|
|
@@ -9,10 +9,6 @@ export declare function restartGroupOperationHandler(req: NodeAPIRequest<{
|
|
|
9
9
|
jobId: string;
|
|
10
10
|
group: string;
|
|
11
11
|
}>, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
12
|
-
export declare function moveGroupOperationHandler(req: NodeAPIRequest<{
|
|
13
|
-
jobId: string;
|
|
14
|
-
group: string;
|
|
15
|
-
}>, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
16
12
|
export declare function stopOperationHandler(req: NodeAPIRequest<{
|
|
17
13
|
jobId: string;
|
|
18
14
|
opId: string;
|
|
@@ -7,7 +7,7 @@ export async function restartOperationHandler(req, res) {
|
|
|
7
7
|
}
|
|
8
8
|
try {
|
|
9
9
|
await task.restartTaskManagerOperation(group, opId);
|
|
10
|
-
return res.status(
|
|
10
|
+
return res.status(202).json({ message: 'Operation restart initiated' });
|
|
11
11
|
}
|
|
12
12
|
catch (error) {
|
|
13
13
|
return res
|
|
@@ -23,23 +23,7 @@ export async function restartGroupOperationHandler(req, res) {
|
|
|
23
23
|
}
|
|
24
24
|
try {
|
|
25
25
|
await task.restartTaskManagerGroupOperations(group);
|
|
26
|
-
return res.status(
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
return res
|
|
30
|
-
.status(500)
|
|
31
|
-
.json({ error: 'Failed to restart group operations' });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
export async function moveGroupOperationHandler(req, res) {
|
|
35
|
-
const { jobId, group } = req.params;
|
|
36
|
-
const task = TaskManagerRegistry.getInstance().get(jobId);
|
|
37
|
-
if (!task) {
|
|
38
|
-
return res.status(400).send('invalid job id');
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
await task.moveTaskManagerGroupOperations(group);
|
|
42
|
-
return res.status(200).json({ message: 'Group Operation restarted' });
|
|
26
|
+
return res.status(202).json({ message: 'Group Operation restart initiated' });
|
|
43
27
|
}
|
|
44
28
|
catch (error) {
|
|
45
29
|
return res
|
|
@@ -212,7 +212,6 @@ export default class TaskManager {
|
|
|
212
212
|
stopTaskManagerOperation: (group: string, opId: string) => Promise<void>;
|
|
213
213
|
stopTaskManagerGroupOperations: (group: string) => Promise<void>;
|
|
214
214
|
restartTaskManagerGroupOperations: (group: string) => Promise<void>;
|
|
215
|
-
moveTaskManagerGroupOperations: (group: string) => Promise<void>;
|
|
216
215
|
stopAllTaskManagerOperations: (reason: StopReason) => void;
|
|
217
216
|
createOperationMap: () => Map<string, Operation<OperationType>>;
|
|
218
217
|
createExecutionPlan: () => ExecutionContext[];
|
|
@@ -282,7 +281,8 @@ export default class TaskManager {
|
|
|
282
281
|
* - Sets status to 'running' and updates the repository with start time.
|
|
283
282
|
* - Iterates through execution groups and runs each op concurrently.
|
|
284
283
|
* - Uses a dynamic while-loop to ensure all ops (including restarts) finish before advancing.
|
|
285
|
-
* - After all groups finish,
|
|
284
|
+
* - After all groups finish, sets the final flow status: 'failed' if any op
|
|
285
|
+
* failed, 'success' otherwise (including stopped/expired jobs).
|
|
286
286
|
* - On any uncaught failure, marks the flow as 'failed' with end time.
|
|
287
287
|
*/
|
|
288
288
|
start(): Promise<void>;
|
|
@@ -13,7 +13,6 @@ import { createDependencyMap } from './executions/createDependencyMap.js';
|
|
|
13
13
|
import { getCurrentGroup, getCurrentGroupStatus, getGroupStatus, getOperationsStatus, getOperationStatus, } from './operations/getOperationsInfos.js';
|
|
14
14
|
import { addLog, getAllLogs, getLogsByGroup, getLogsByOp, subscribe, unsubscribe, } from './loggers/logManager.js';
|
|
15
15
|
import { addStat, getAllStats, getStatsByOp, queryStats, getLatestStatPerOp, subscribeStats, unsubscribeStats, } from './loggers/statsManager.js';
|
|
16
|
-
import { moveTaskManagerGroupOperations } from './operations/moveTaskManagerGroupOperation.js';
|
|
17
16
|
import { setResult, setResults, setHost, setDefaults, setContainerIp, rehydrateEndpointsForOperation, getByPath, resolveLiteralsInString, interpolate, interpolateOperation, transformCollections, } from './globalStore/index.js';
|
|
18
17
|
import { configs } from '../../configs/configs.js';
|
|
19
18
|
import { getSDK } from '../../sdk/index.js';
|
|
@@ -148,8 +147,6 @@ export default class TaskManager {
|
|
|
148
147
|
stopTaskManagerGroupOperations.bind(this);
|
|
149
148
|
this.restartTaskManagerGroupOperations =
|
|
150
149
|
restartTaskManagerGroupOperations.bind(this);
|
|
151
|
-
this.moveTaskManagerGroupOperations =
|
|
152
|
-
moveTaskManagerGroupOperations.bind(this);
|
|
153
150
|
this.stopAllTaskManagerOperations = stopAllTaskManagerOperations.bind(this);
|
|
154
151
|
this.createOperationMap = createOperationMap.bind(this);
|
|
155
152
|
this.createExecutionPlan = createExecutionPlan.bind(this);
|
|
@@ -273,7 +270,8 @@ export default class TaskManager {
|
|
|
273
270
|
* - Sets status to 'running' and updates the repository with start time.
|
|
274
271
|
* - Iterates through execution groups and runs each op concurrently.
|
|
275
272
|
* - Uses a dynamic while-loop to ensure all ops (including restarts) finish before advancing.
|
|
276
|
-
* - After all groups finish,
|
|
273
|
+
* - After all groups finish, sets the final flow status: 'failed' if any op
|
|
274
|
+
* failed, 'success' otherwise (including stopped/expired jobs).
|
|
277
275
|
* - On any uncaught failure, marks the flow as 'failed' with end time.
|
|
278
276
|
*/
|
|
279
277
|
async start() {
|
|
@@ -362,8 +360,14 @@ export default class TaskManager {
|
|
|
362
360
|
});
|
|
363
361
|
}
|
|
364
362
|
}
|
|
365
|
-
//
|
|
366
|
-
|
|
363
|
+
// The final status only reflects the workload outcome: 'failed' if any
|
|
364
|
+
// operation failed, 'success' otherwise. How the job ended (stopped,
|
|
365
|
+
// expired) is tracked in the op statuses/diagnostics, so
|
|
366
|
+
// ops that were merely stopped don't fail the job.
|
|
367
|
+
const hasFailedOp = this.repository
|
|
368
|
+
.getFlowState(this.job)
|
|
369
|
+
.opStates.some((opState) => opState.status === Statuses.FAILED);
|
|
370
|
+
this.status = hasFailedOp ? Statuses.FAILED : Statuses.SUCCESS;
|
|
367
371
|
this.repository.updateflowState(this.job, {
|
|
368
372
|
status: this.status,
|
|
369
373
|
endTime: Date.now(),
|
|
@@ -33,6 +33,14 @@ export async function restartTaskManagerOperation(group, opId) {
|
|
|
33
33
|
if (this.lockedOperations.has(opId)) {
|
|
34
34
|
throw new Error(`OPERATION_${this.lockedOperations.get(opId)}`);
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate the operation exists up front, before we acquire the lock or kick
|
|
38
|
+
* off any async work. This lets the caller (HTTP handler) reject an invalid
|
|
39
|
+
* opId synchronously, and avoids leaving the operation permanently locked if
|
|
40
|
+
* the lookup were to fail later in the flow.
|
|
41
|
+
*/
|
|
42
|
+
if (!this.opMap.has(opId))
|
|
43
|
+
throw new Error(`INVALID_OPID`);
|
|
36
44
|
// Mark this op as "RESTARTING" so no one else touches it mid-process
|
|
37
45
|
this.lockedOperations.set(opId, 'RESTARTING');
|
|
38
46
|
this.operationStatus.set(opId, OperationProgressStatuses.RESTARTING);
|
|
@@ -77,41 +85,57 @@ export async function restartTaskManagerOperation(group, opId) {
|
|
|
77
85
|
// This should trigger container teardown, logs finalization, and state updates
|
|
78
86
|
controller?.abort('restart');
|
|
79
87
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
88
|
+
* The container teardown + relaunch below can take a while (graceful container
|
|
89
|
+
* stop, image checks, etc). We deliberately DO NOT await it here: this function
|
|
90
|
+
* is invoked from an HTTP handler whose response is tunneled through frp, and
|
|
91
|
+
* holding the response open for the whole teardown means frp has no live backend
|
|
92
|
+
* for that window and serves its "no backend" (503) page to the caller.
|
|
93
|
+
*
|
|
94
|
+
* Everything that guards against races — the lock, the RESTARTING status and the
|
|
95
|
+
* group-hold placeholder — has already been put in place synchronously above, so
|
|
96
|
+
* it is safe to let the heavy lifting run in the background and return now. The
|
|
97
|
+
* caller gets an immediate acknowledgement that the restart was initiated.
|
|
82
98
|
*/
|
|
83
|
-
|
|
84
|
-
if (originalPromise) {
|
|
99
|
+
void (async () => {
|
|
85
100
|
try {
|
|
86
|
-
|
|
101
|
+
/**
|
|
102
|
+
* If the operation had already started, wait for it to finish fully (even if it
|
|
103
|
+
* failed). This avoids stomping over any still-pending teardown work.
|
|
104
|
+
*/
|
|
105
|
+
const originalPromise = this.currentGroupOperationsPromises.get(opId);
|
|
106
|
+
if (originalPromise) {
|
|
107
|
+
try {
|
|
108
|
+
await originalPromise;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// We intentionally ignore any error here.
|
|
112
|
+
// Our goal is just to make sure the cleanup is finished, not whether it succeeded.
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Now that the original op has been stopped, we need to restart it.
|
|
117
|
+
* Get the latest flow data from storage — this could've been updated elsewhere
|
|
118
|
+
* in the meantime.
|
|
119
|
+
*/
|
|
120
|
+
const flow = this.repository.getFlow(this.job);
|
|
121
|
+
/**
|
|
122
|
+
* Re-register the operation for execution and begin tracking it again.
|
|
123
|
+
* This effectively restarts the operation from scratch.
|
|
124
|
+
*/
|
|
125
|
+
this.currentGroupOperationsPromises.set(opId, this.trackGroupOperationPromise(opId, this.setUpOperationFunc(flow, opId, [])));
|
|
87
126
|
}
|
|
88
|
-
catch {
|
|
89
|
-
|
|
90
|
-
// Our goal is just to make sure the cleanup is finished, not whether it succeeded.
|
|
127
|
+
catch (error) {
|
|
128
|
+
emitter?.emit('log', `Failed to restart operation: ${error?.message ?? error}`, 'error');
|
|
91
129
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (!op)
|
|
104
|
-
throw new Error(`INVALID_OPID`);
|
|
105
|
-
/**
|
|
106
|
-
* Re-register the operation for execution and begin tracking it again.
|
|
107
|
-
* This effectively restarts the operation from scratch.
|
|
108
|
-
*/
|
|
109
|
-
this.currentGroupOperationsPromises.set(opId, this.trackGroupOperationPromise(opId, this.setUpOperationFunc(flow, opId, [])));
|
|
110
|
-
/**
|
|
111
|
-
* Now that we've re-launched the op and it's being tracked,
|
|
112
|
-
* we can safely release the group hold so the group can eventually complete.
|
|
113
|
-
*/
|
|
114
|
-
groupHold.releaseGroupOperationBlock();
|
|
115
|
-
// Finally, unlock the operation so other calls (like restart or stop) can act on it again
|
|
116
|
-
this.lockedOperations.delete(opId);
|
|
130
|
+
finally {
|
|
131
|
+
/**
|
|
132
|
+
* Release the group hold so the group can eventually complete, and unlock the
|
|
133
|
+
* operation so other calls (like restart or stop) can act on it again. This runs
|
|
134
|
+
* regardless of whether the relaunch succeeded, so we never leave the group held
|
|
135
|
+
* or the operation permanently locked.
|
|
136
|
+
*/
|
|
137
|
+
groupHold.releaseGroupOperationBlock();
|
|
138
|
+
this.lockedOperations.delete(opId);
|
|
139
|
+
}
|
|
140
|
+
})();
|
|
117
141
|
}
|
|
@@ -7,15 +7,19 @@ import TaskManager, { StopReason } from '../TaskManager.js';
|
|
|
7
7
|
* - Lifecycle shutdown for any running ops
|
|
8
8
|
* - A consistent status applied to the flow and all ops
|
|
9
9
|
*
|
|
10
|
-
* The stop reason determines what status each op
|
|
10
|
+
* The stop reason determines what status each op will be marked with.
|
|
11
11
|
*
|
|
12
|
-
* | Reason | Ops Status |
|
|
13
|
-
*
|
|
14
|
-
* | expired | success |
|
|
15
|
-
* | stopped | stopped |
|
|
16
|
-
* | quit | failed |
|
|
17
|
-
* | unknown | failed |
|
|
12
|
+
* | Reason | Ops Status |
|
|
13
|
+
* |----------|------------|
|
|
14
|
+
* | expired | success |
|
|
15
|
+
* | stopped | stopped |
|
|
16
|
+
* | quit | failed |
|
|
17
|
+
* | unknown | failed |
|
|
18
18
|
*
|
|
19
19
|
* `getStatus()` is used internally to apply the correct status values.
|
|
20
|
+
*
|
|
21
|
+
* The flow status set here is transient: the final result status is derived
|
|
22
|
+
* from the op statuses when `start()` completes — 'failed' if any op failed,
|
|
23
|
+
* 'success' otherwise.
|
|
20
24
|
*/
|
|
21
25
|
export declare function stopAllTaskManagerOperations(this: TaskManager, reason: StopReason): void;
|
|
@@ -7,16 +7,20 @@ import { OperationProgressStatuses, } from '../TaskManager.js';
|
|
|
7
7
|
* - Lifecycle shutdown for any running ops
|
|
8
8
|
* - A consistent status applied to the flow and all ops
|
|
9
9
|
*
|
|
10
|
-
* The stop reason determines what status each op
|
|
10
|
+
* The stop reason determines what status each op will be marked with.
|
|
11
11
|
*
|
|
12
|
-
* | Reason | Ops Status |
|
|
13
|
-
*
|
|
14
|
-
* | expired | success |
|
|
15
|
-
* | stopped | stopped |
|
|
16
|
-
* | quit | failed |
|
|
17
|
-
* | unknown | failed |
|
|
12
|
+
* | Reason | Ops Status |
|
|
13
|
+
* |----------|------------|
|
|
14
|
+
* | expired | success |
|
|
15
|
+
* | stopped | stopped |
|
|
16
|
+
* | quit | failed |
|
|
17
|
+
* | unknown | failed |
|
|
18
18
|
*
|
|
19
19
|
* `getStatus()` is used internally to apply the correct status values.
|
|
20
|
+
*
|
|
21
|
+
* The flow status set here is transient: the final result status is derived
|
|
22
|
+
* from the op statuses when `start()` completes — 'failed' if any op failed,
|
|
23
|
+
* 'success' otherwise.
|
|
20
24
|
*/
|
|
21
25
|
export function stopAllTaskManagerOperations(reason) {
|
|
22
26
|
/**
|
|
@@ -63,75 +63,89 @@ export async function stopTaskManagerOperation(group, opId) {
|
|
|
63
63
|
*/
|
|
64
64
|
controller?.abort(StopReasons.STOPPED);
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
66
|
+
* The container teardown below can take a while (graceful container stop, etc).
|
|
67
|
+
* We deliberately DO NOT await it here: this function is invoked from an HTTP
|
|
68
|
+
* handler whose response is tunneled through frp, and holding the response open
|
|
69
|
+
* for the whole teardown means frp has no live backend for that window and serves
|
|
70
|
+
* its "no backend" (503) page to the caller.
|
|
71
|
+
*
|
|
72
|
+
* The lock and the STOPPING status have already been set synchronously above, so
|
|
73
|
+
* it is safe to let the teardown — and the STOPPED-state finalization that depends
|
|
74
|
+
* on it — run in the background and return now. The caller gets an immediate
|
|
75
|
+
* acknowledgement that the stop was initiated.
|
|
69
76
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
void (async () => {
|
|
78
|
+
/**
|
|
79
|
+
* Wait for the original promise tied to this operation to resolve.
|
|
80
|
+
* Even if it fails (e.g., due to abort), we silently swallow the error
|
|
81
|
+
* since the focus here is cleanup, not success.
|
|
82
|
+
*/
|
|
83
|
+
const originalPromise = this.currentGroupOperationsPromises.get(opId);
|
|
84
|
+
if (originalPromise) {
|
|
85
|
+
try {
|
|
86
|
+
await originalPromise;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// swallow error, we're only waiting for cleanup
|
|
90
|
+
}
|
|
74
91
|
}
|
|
75
|
-
|
|
76
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Ensure we clean up the operation's reference in the promise map
|
|
94
|
+
* to avoid holding stale references or leaking memory.
|
|
95
|
+
*/
|
|
96
|
+
this.currentGroupOperationsPromises.delete(opId);
|
|
97
|
+
// Unlock the operation so it can be restarted or reused later
|
|
98
|
+
this.lockedOperations.delete(opId);
|
|
99
|
+
// Ensure final STOPPED state is reflected in memory and repository
|
|
100
|
+
this.operationStatus.set(opId, OperationProgressStatuses.STOPPED);
|
|
101
|
+
const index = this.getOpStateIndex(opId);
|
|
102
|
+
const opState = this.repository.getOpState(this.job, index);
|
|
103
|
+
const alreadyEnded = !!opState.endTime;
|
|
104
|
+
if (!alreadyEnded) {
|
|
105
|
+
this.repository.updateOpState(this.job, index, {
|
|
106
|
+
status: this.getStatus(StopReasons.STOPPED, 'ops'),
|
|
107
|
+
endTime: Date.now(),
|
|
108
|
+
});
|
|
77
109
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
*/
|
|
83
|
-
this.currentGroupOperationsPromises.delete(opId);
|
|
84
|
-
// Unlock the operation so it can be restarted or reused later
|
|
85
|
-
this.lockedOperations.delete(opId);
|
|
86
|
-
// Ensure final STOPPED state is reflected in memory and repository
|
|
87
|
-
this.operationStatus.set(opId, OperationProgressStatuses.STOPPED);
|
|
88
|
-
const index = this.getOpStateIndex(opId);
|
|
89
|
-
const opState = this.repository.getOpState(this.job, index);
|
|
90
|
-
const alreadyEnded = !!opState.endTime;
|
|
91
|
-
if (!alreadyEnded) {
|
|
92
|
-
this.repository.updateOpState(this.job, index, {
|
|
93
|
-
status: this.getStatus(StopReasons.STOPPED, 'ops'),
|
|
94
|
-
endTime: Date.now(),
|
|
110
|
+
this.events.emit('flow:updated', {
|
|
111
|
+
jobId: this.job,
|
|
112
|
+
opId,
|
|
113
|
+
type: 'status:stopped',
|
|
95
114
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (secrets &&
|
|
107
|
-
typeof secrets[this.job] === 'object' &&
|
|
108
|
-
secrets[this.job] !== 'private' &&
|
|
109
|
-
secrets[this.job] !== 'public' &&
|
|
110
|
-
secrets[this.job]) {
|
|
111
|
-
jobSecrets = secrets[this.job];
|
|
112
|
-
}
|
|
113
|
-
let updated = false;
|
|
114
|
-
const newJobSecrets = {};
|
|
115
|
-
for (const [exposeId, value] of Object.entries(jobSecrets)) {
|
|
116
|
-
const v = value;
|
|
117
|
-
if (v && typeof v === 'object' && v.opID === opId) {
|
|
118
|
-
newJobSecrets[exposeId] = { ...v, status: 'OFFLINE' };
|
|
119
|
-
updated = true;
|
|
115
|
+
try {
|
|
116
|
+
const flow = this.repository.getFlow(this.job);
|
|
117
|
+
const secrets = flow?.state?.secrets;
|
|
118
|
+
let jobSecrets = {};
|
|
119
|
+
if (secrets &&
|
|
120
|
+
typeof secrets[this.job] === 'object' &&
|
|
121
|
+
secrets[this.job] !== 'private' &&
|
|
122
|
+
secrets[this.job] !== 'public' &&
|
|
123
|
+
secrets[this.job]) {
|
|
124
|
+
jobSecrets = secrets[this.job];
|
|
120
125
|
}
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
let updated = false;
|
|
127
|
+
const newJobSecrets = {};
|
|
128
|
+
for (const [exposeId, value] of Object.entries(jobSecrets)) {
|
|
129
|
+
const v = value;
|
|
130
|
+
if (v && typeof v === 'object' && v.opID === opId) {
|
|
131
|
+
newJobSecrets[exposeId] = { ...v, status: 'OFFLINE' };
|
|
132
|
+
updated = true;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
newJobSecrets[exposeId] = v;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (updated) {
|
|
139
|
+
this.repository.updateflowStateSecret(this.job, {
|
|
140
|
+
[this.job]: newJobSecrets,
|
|
141
|
+
});
|
|
142
|
+
this.events.emit('flow:updated', {
|
|
143
|
+
jobId: this.job,
|
|
144
|
+
opId,
|
|
145
|
+
type: 'flow:secrets-updated',
|
|
146
|
+
});
|
|
123
147
|
}
|
|
124
148
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
[this.job]: newJobSecrets,
|
|
128
|
-
});
|
|
129
|
-
this.events.emit('flow:updated', {
|
|
130
|
-
jobId: this.job,
|
|
131
|
-
opId,
|
|
132
|
-
type: 'flow:secrets-updated',
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
catch { }
|
|
149
|
+
catch { }
|
|
150
|
+
})();
|
|
137
151
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nosana/node",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.27-rc",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@nosana/node",
|
|
9
|
-
"version": "1.1.
|
|
9
|
+
"version": "1.1.27-rc",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@coral-xyz/anchor": "^0.28.1-beta.1",
|
package/package.json
CHANGED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
export async function moveTaskManagerGroupOperations(group) {
|
|
2
|
-
/**
|
|
3
|
-
* Look up the group context to get its operation IDs.
|
|
4
|
-
* If the group doesn't exist in the execution plan, it's invalid.
|
|
5
|
-
*/
|
|
6
|
-
const groupContext = this.executionPlan.find((ctx) => ctx.group === group);
|
|
7
|
-
if (!groupContext) {
|
|
8
|
-
throw new Error('GROUP_NOT_FOUND');
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Ensure we're currently within the provided group context before proceeding.
|
|
12
|
-
* Prevents stopping inactive or completed groups.
|
|
13
|
-
*/
|
|
14
|
-
if (this.currentGroup == group) {
|
|
15
|
-
throw new Error('GROUP_ACTIVE');
|
|
16
|
-
}
|
|
17
|
-
const currentGroupContext = this.executionPlan.find((ctx) => ctx.group === this.currentGroup);
|
|
18
|
-
if (!currentGroupContext) {
|
|
19
|
-
throw new Error('GROUP_NOT_FOUND');
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* The following block creates a "placeholder promise" that we temporarily insert into the group’s
|
|
23
|
-
* operation map. This ensures that even if all the original ops complete while we're restarting,
|
|
24
|
-
* the group will not advance to the next one until we explicitly release this block.
|
|
25
|
-
*
|
|
26
|
-
* This is CRITICAL to prevent race conditions where the restart is too late, and the system
|
|
27
|
-
* thinks the group has completed.
|
|
28
|
-
*/
|
|
29
|
-
const groupHold = (() => {
|
|
30
|
-
let release;
|
|
31
|
-
const placeholder = new Promise((res) => (release = res));
|
|
32
|
-
const abortHandler = () => {
|
|
33
|
-
release();
|
|
34
|
-
this.mainAbortController.signal.removeEventListener('abort', abortHandler);
|
|
35
|
-
};
|
|
36
|
-
this.mainAbortController.signal.addEventListener('abort', abortHandler);
|
|
37
|
-
return {
|
|
38
|
-
groupHoldPromisePlaceHolder: placeholder,
|
|
39
|
-
releaseGroupOperationBlock: () => {
|
|
40
|
-
release();
|
|
41
|
-
this.mainAbortController.signal.removeEventListener('abort', abortHandler);
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
})();
|
|
45
|
-
// Use a special ID to register this placeholder in the group tracking map
|
|
46
|
-
// The op will still retain its original ID for restart purposes
|
|
47
|
-
const placeholderId = `${group}::restart-group`;
|
|
48
|
-
this.trackGroupOperationPromise(placeholderId, groupHold.groupHoldPromisePlaceHolder);
|
|
49
|
-
if (this.currentGroup) {
|
|
50
|
-
await this.stopTaskManagerGroupOperations(this.currentGroup);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* update the current group to the group we about to start
|
|
54
|
-
*/
|
|
55
|
-
this.currentGroup = group;
|
|
56
|
-
/**
|
|
57
|
-
* restart the new current group
|
|
58
|
-
*/
|
|
59
|
-
await this.restartTaskManagerGroupOperations(group);
|
|
60
|
-
/**
|
|
61
|
-
* Now that we've re-launched the op and it's being tracked,
|
|
62
|
-
* we can safely release the group hold so the group can eventually complete.
|
|
63
|
-
*/
|
|
64
|
-
groupHold.releaseGroupOperationBlock();
|
|
65
|
-
}
|