Persist runtime task catalog entries on backend failure

- Track runtime catalog entries and status locally
- Fall back to cached entries when backend catalog/status lookups fail
- Clear cached runtime task state when tasks are removed
This commit is contained in:
Jacob Schmidt 2026-05-20 19:12:23 -05:00
parent 12dc262136
commit da5dccd2c0
2 changed files with 90 additions and 9 deletions

View File

@ -147,6 +147,7 @@ GVAR(TaskStore) = createHashMapObject [[
GVAR(TaskLifecycleReporter) call ["clearTaskLifecycle", [_taskID]]; GVAR(TaskLifecycleReporter) call ["clearTaskLifecycle", [_taskID]];
GVAR(TaskParticipantTracker) call ["clearTaskParticipants", [_taskID]]; GVAR(TaskParticipantTracker) call ["clearTaskParticipants", [_taskID]];
GVAR(TaskCatalogStore) call ["clearRuntimeTask", [_taskID]];
GVAR(TaskStateGateway) call ["callTaskState", ["task:clear", [_taskID], false]]; GVAR(TaskStateGateway) call ["callTaskState", ["task:clear", [_taskID], false]];
_self call ["clearTaskEntities", [_taskID]]; _self call ["clearTaskEntities", [_taskID]];
true true

View File

@ -26,6 +26,7 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
["resetRuntimeState", compileFinal { ["resetRuntimeState", compileFinal {
_self set ["completedTaskRegistry", createHashMap]; _self set ["completedTaskRegistry", createHashMap];
_self set ["taskDependencyRegistry", createHashMap]; _self set ["taskDependencyRegistry", createHashMap];
_self set ["runtimeCatalogRegistry", createHashMap];
true true
}], }],
["bindTaskOwnership", compileFinal { ["bindTaskOwnership", compileFinal {
@ -194,6 +195,7 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
private _initialStatus = ["available", "locked"] select !(_self call ["areTaskPrerequisitesSatisfied", [_taskID, _entry]]); private _initialStatus = ["available", "locked"] select !(_self call ["areTaskPrerequisitesSatisfied", [_taskID, _entry]]);
_entry set ["locked", _initialStatus isEqualTo "locked"]; _entry set ["locked", _initialStatus isEqualTo "locked"];
_entry set ["status", _initialStatus];
private _envelope = GVAR(TaskStateGateway) call [ private _envelope = GVAR(TaskStateGateway) call [
"callTaskStateEnvelope", "callTaskStateEnvelope",
@ -205,6 +207,10 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
private _registered = _envelope getOrDefault ["success", false]; private _registered = _envelope getOrDefault ["success", false];
if (_registered) then { if (_registered) then {
private _runtimeCatalogRegistry = _self getOrDefault ["runtimeCatalogRegistry", createHashMap];
_runtimeCatalogRegistry set [_taskID, +_entry];
_self set ["runtimeCatalogRegistry", _runtimeCatalogRegistry];
GVAR(TaskLifecycleReporter) call ["recordTaskCreated", [_taskID]]; GVAR(TaskLifecycleReporter) call ["recordTaskCreated", [_taskID]];
GVAR(TaskLifecycleReporter) call ["emitTaskLifecycleEvent", ["task.created", _taskID, "created", createHashMap]]; GVAR(TaskLifecycleReporter) call ["emitTaskLifecycleEvent", ["task.created", _taskID, "created", createHashMap]];
_self call ["setTaskStatus", [_taskID, _initialStatus]]; _self call ["setTaskStatus", [_taskID, _initialStatus]];
@ -214,21 +220,50 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
}], }],
["getActiveTaskCatalog", compileFinal { ["getActiveTaskCatalog", compileFinal {
private _entries = GVAR(TaskStateGateway) call ["callTaskState", ["task:catalog:active", [], []]]; private _entries = GVAR(TaskStateGateway) call ["callTaskState", ["task:catalog:active", [], []]];
if !(_entries isEqualType []) exitWith { [] }; if !(_entries isEqualType []) then { _entries = [] };
private _entryRegistry = createHashMap;
{
if !(_x isEqualType createHashMap) then { continue; };
private _taskID = _x getOrDefault ["taskID", _x getOrDefault ["taskId", ""]];
if (_taskID isEqualTo "") then { continue; };
_entryRegistry set [_taskID, +_x];
} forEach _entries;
private _runtimeCatalogRegistry = _self getOrDefault ["runtimeCatalogRegistry", createHashMap];
{
if (_x in _entryRegistry) then { continue; };
_entryRegistry set [_x, +_y];
} forEach _runtimeCatalogRegistry;
if (_entries isEqualTo [] && { _runtimeCatalogRegistry isNotEqualTo createHashMap }) then {
["WARNING", format [
"Task active catalog backend returned no entries; using %1 runtime catalog entr%2 as fallback.",
count _runtimeCatalogRegistry,
["y", "ies"] select ((count _runtimeCatalogRegistry) isNotEqualTo 1)
]] call EFUNC(common,log);
};
private _visibleEntries = []; private _visibleEntries = [];
{ {
if !(_x isEqualType createHashMap) then { continue; }; private _taskID = _x;
private _entry = _y;
private _taskID = _x getOrDefault ["taskID", _x getOrDefault ["taskId", ""]]; if !(_entry isEqualType createHashMap) then { continue; };
if (_taskID isEqualTo "") then { continue; }; if (_taskID isEqualTo "") then { continue; };
private _entryStatus = toLowerANSI (_entry getOrDefault ["status", ""]);
private _status = _self call ["getTaskStatus", [_taskID]]; private _status = _self call ["getTaskStatus", [_taskID]];
if !(_status in ["available", "assigned", "active"]) then {
if (_entryStatus in ["available", "assigned", "active"]) then {
_status = _entryStatus;
};
};
if !(_status in ["available", "assigned", "active"]) then { continue; }; if !(_status in ["available", "assigned", "active"]) then { continue; };
if !(_self call ["areTaskPrerequisitesSatisfied", [_taskID, _x]]) then { continue; }; if !(_self call ["areTaskPrerequisitesSatisfied", [_taskID, _entry]]) then { continue; };
_visibleEntries pushBack _x; _entry set ["status", _status];
} forEach _entries; _visibleEntries pushBack _entry;
} forEach _entryRegistry;
_visibleEntries _visibleEntries
}], }],
@ -238,7 +273,10 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
if (_taskID isEqualTo "") exitWith { false }; if (_taskID isEqualTo "") exitWith { false };
private _entry = GVAR(TaskStateGateway) call ["callTaskState", ["task:catalog:get", [_taskID], objNull]]; private _entry = GVAR(TaskStateGateway) call ["callTaskState", ["task:catalog:get", [_taskID], objNull]];
_entry isEqualType createHashMap if (_entry isEqualType createHashMap) exitWith { true };
private _runtimeCatalogRegistry = _self getOrDefault ["runtimeCatalogRegistry", createHashMap];
(_runtimeCatalogRegistry getOrDefault [_taskID, createHashMap]) isNotEqualTo createHashMap
}], }],
["getTaskCatalogEntry", compileFinal { ["getTaskCatalogEntry", compileFinal {
params [["_taskID", "", [""]]]; params [["_taskID", "", [""]]];
@ -246,6 +284,11 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
if (_taskID isEqualTo "") exitWith { createHashMap }; if (_taskID isEqualTo "") exitWith { createHashMap };
[(GVAR(TaskStateGateway) call ["callTaskState", ["task:catalog:get", [_taskID], createHashMap]])] params [["_entry", createHashMap, [createHashMap]]]; [(GVAR(TaskStateGateway) call ["callTaskState", ["task:catalog:get", [_taskID], createHashMap]])] params [["_entry", createHashMap, [createHashMap]]];
if !(_entry isEqualType createHashMap) then { _entry = createHashMap };
if (_entry isEqualTo createHashMap) then {
private _runtimeCatalogRegistry = _self getOrDefault ["runtimeCatalogRegistry", createHashMap];
_entry = +(_runtimeCatalogRegistry getOrDefault [_taskID, createHashMap]);
};
if !(_entry isEqualType createHashMap) exitWith { createHashMap }; if !(_entry isEqualType createHashMap) exitWith { createHashMap };
_entry _entry
@ -315,11 +358,20 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
if (_taskID isEqualTo "" || { _status isEqualTo "" }) exitWith { false }; if (_taskID isEqualTo "" || { _status isEqualTo "" }) exitWith { false };
private _normalizedStatus = toLowerANSI _status;
private _runtimeCatalogRegistry = _self getOrDefault ["runtimeCatalogRegistry", createHashMap];
private _runtimeEntry = +(_runtimeCatalogRegistry getOrDefault [_taskID, createHashMap]);
if (_runtimeEntry isNotEqualTo createHashMap) then {
_runtimeEntry set ["status", _normalizedStatus];
_runtimeEntry set ["locked", _normalizedStatus isEqualTo "locked"];
_runtimeCatalogRegistry set [_taskID, _runtimeEntry];
_self set ["runtimeCatalogRegistry", _runtimeCatalogRegistry];
};
private _envelope = GVAR(TaskStateGateway) call ["callTaskStateEnvelope", ["task:status:set", [_taskID, _status]]]; private _envelope = GVAR(TaskStateGateway) call ["callTaskStateEnvelope", ["task:status:set", [_taskID, _status]]];
private _statusResult = _envelope getOrDefault ["success", false]; private _statusResult = _envelope getOrDefault ["success", false];
if (_statusResult) then { if (_statusResult) then {
private _normalizedStatus = toLowerANSI _status;
private _eventName = GVAR(TaskLifecycleReporter) call ["recordTaskStatus", [_taskID, _normalizedStatus]]; private _eventName = GVAR(TaskLifecycleReporter) call ["recordTaskStatus", [_taskID, _normalizedStatus]];
if (_eventName isNotEqualTo "") then { if (_eventName isNotEqualTo "") then {
@ -330,6 +382,12 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
_self call ["markTaskCompleted", [_taskID]]; _self call ["markTaskCompleted", [_taskID]];
_self call ["unlockDependentTasks", [_taskID]]; _self call ["unlockDependentTasks", [_taskID]];
}; };
} else {
["WARNING", format [
"Task status backend update failed for %1 -> %2; runtime status was retained for CAD visibility.",
_taskID,
_normalizedStatus
]] call EFUNC(common,log);
}; };
_statusResult _statusResult
@ -340,6 +398,13 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
if (_taskID isEqualTo "") exitWith { "" }; if (_taskID isEqualTo "") exitWith { "" };
[(GVAR(TaskStateGateway) call ["callTaskState", ["task:status:get", [_taskID], ""]])] params [["_status", "", [""]]]; [(GVAR(TaskStateGateway) call ["callTaskState", ["task:status:get", [_taskID], ""]])] params [["_status", "", [""]]];
if (_status isEqualTo "") then {
private _runtimeCatalogRegistry = _self getOrDefault ["runtimeCatalogRegistry", createHashMap];
private _entry = _runtimeCatalogRegistry getOrDefault [_taskID, createHashMap];
if (_entry isNotEqualTo createHashMap) then {
_status = _entry getOrDefault ["status", ["available", "locked"] select (_entry getOrDefault ["locked", false])];
};
};
_status _status
}], }],
["clearTaskStatus", compileFinal { ["clearTaskStatus", compileFinal {
@ -350,6 +415,21 @@ GVAR(TaskCatalogStore) = createHashMapObject [[
[(GVAR(TaskStateGateway) call ["callTaskState", ["task:status:clear", [_taskID], false]])] params [["_statusResult", false, [false]]]; [(GVAR(TaskStateGateway) call ["callTaskState", ["task:status:clear", [_taskID], false]])] params [["_statusResult", false, [false]]];
_statusResult _statusResult
}],
["clearRuntimeTask", compileFinal {
params [["_taskID", "", [""]]];
if (_taskID isEqualTo "") exitWith { false };
private _runtimeCatalogRegistry = _self getOrDefault ["runtimeCatalogRegistry", createHashMap];
_runtimeCatalogRegistry deleteAt _taskID;
_self set ["runtimeCatalogRegistry", _runtimeCatalogRegistry];
private _dependencyRegistry = _self getOrDefault ["taskDependencyRegistry", createHashMap];
_dependencyRegistry deleteAt _taskID;
_self set ["taskDependencyRegistry", _dependencyRegistry];
true
}] }]
]]; ]];