ultravisor 1.0.23 → 1.0.25

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.
Files changed (26) hide show
  1. package/docs/queue-followups/03-config-migration.md +123 -0
  2. package/docs/queue-followups/04-direct-dispatch-phase-emission.md +128 -0
  3. package/package.json +3 -1
  4. package/source/Ultravisor.cjs +4 -0
  5. package/source/cli/Ultravisor-CLIProgram.cjs +18 -0
  6. package/source/datamodel/Ultravisor-BeaconQueue.json +165 -0
  7. package/source/datamodel/Ultravisor-Fleet.json +66 -0
  8. package/source/services/Ultravisor-Beacon-ActionDefaults.cjs +174 -0
  9. package/source/services/Ultravisor-Beacon-Coordinator.cjs +479 -8
  10. package/source/services/Ultravisor-Beacon-RunManager.cjs +169 -0
  11. package/source/services/Ultravisor-Beacon-Scheduler.cjs +789 -0
  12. package/source/services/Ultravisor-DirectoryDistributor.cjs +280 -0
  13. package/source/services/Ultravisor-ExecutionEngine.cjs +227 -4
  14. package/source/services/Ultravisor-ExecutionManifest.cjs +1 -0
  15. package/source/services/Ultravisor-FleetManager.cjs +853 -0
  16. package/source/services/persistence/Ultravisor-Beacon-FleetStore.cjs +570 -0
  17. package/source/services/persistence/Ultravisor-Beacon-QueueStore.cjs +886 -0
  18. package/source/services/tasks/file-system/Ultravisor-TaskConfigs-FileSystem.cjs +81 -0
  19. package/source/services/tasks/file-system/definitions/chunked-write.json +38 -0
  20. package/source/web_server/Ultravisor-API-Server.cjs +588 -0
  21. package/test/Ultravisor_BeaconQueue_tests.js +502 -0
  22. package/test/fleetstore-smoke.js +152 -0
  23. package/webinterface/source/Pict-Application-Ultravisor.js +2 -0
  24. package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +4 -0
  25. package/webinterface/source/views/PictView-Ultravisor-Fleet.js +489 -0
  26. package/webinterface/source/views/PictView-Ultravisor-TopBar.js +1 -0
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Ultravisor Beacon Action Defaults
3
+ *
4
+ * Resolves per-action runtime defaults (timeout, retry policy, priority,
5
+ * expected-wait baseline) from the BeaconActionDefault table. Live
6
+ * config changes land here instead of in Fable settings, which means
7
+ * an operator can tune a slow capability without restarting the hub.
8
+ *
9
+ * Fallback order for any single field: per-request Settings → per-action
10
+ * row → per-capability row (Action="") → Fable setting → hard default.
11
+ * The last two levels are the compatibility shim while we migrate the
12
+ * scattered settings into rows.
13
+ *
14
+ * @module Ultravisor-Beacon-ActionDefaults
15
+ */
16
+
17
+ const libPictService = require('pict-serviceproviderbase');
18
+
19
+ const HARD_DEFAULTS = {
20
+ TimeoutMs: 300000,
21
+ MaxAttempts: 1,
22
+ RetryBackoffMs: 5000,
23
+ DefaultPriority: 0,
24
+ ExpectedWaitP95Ms: 0,
25
+ HeartbeatExpectedMs: 60000,
26
+ MinSamplesForBaseline: 20
27
+ };
28
+
29
+ class UltravisorBeaconActionDefaults extends libPictService
30
+ {
31
+ constructor(pPict, pOptions, pServiceHash)
32
+ {
33
+ super(pPict, pOptions, pServiceHash);
34
+ this.serviceType = 'UltravisorBeaconActionDefaults';
35
+ this._Cache = {};
36
+ this._CacheTTLMs = 10000;
37
+ }
38
+
39
+ _getStore()
40
+ {
41
+ let tmpMap = this.fable.servicesMap && this.fable.servicesMap.UltravisorBeaconQueueStore;
42
+ if (!tmpMap) return null;
43
+ let tmpStore = Object.values(tmpMap)[0];
44
+ return (tmpStore && tmpStore.isEnabled()) ? tmpStore : null;
45
+ }
46
+
47
+ _cacheKey(pCap, pAction)
48
+ {
49
+ return `${pCap}||${pAction || ''}`;
50
+ }
51
+
52
+ _fableSettingFallback(pKey)
53
+ {
54
+ let tmpSettings = this.fable.settings || {};
55
+ // Legacy settings names kept during the migration period.
56
+ if (pKey === 'TimeoutMs' && tmpSettings.UltravisorBeaconWorkItemTimeoutMs)
57
+ {
58
+ return tmpSettings.UltravisorBeaconWorkItemTimeoutMs;
59
+ }
60
+ if (pKey === 'HeartbeatExpectedMs' && tmpSettings.UltravisorBeaconHeartbeatMs)
61
+ {
62
+ return tmpSettings.UltravisorBeaconHeartbeatMs;
63
+ }
64
+ return null;
65
+ }
66
+
67
+ invalidate()
68
+ {
69
+ this._Cache = {};
70
+ }
71
+
72
+ resolve(pCapability, pAction)
73
+ {
74
+ let tmpKey = this._cacheKey(pCapability, pAction);
75
+ let tmpCached = this._Cache[tmpKey];
76
+ let tmpNow = Date.now();
77
+ if (tmpCached && (tmpNow - tmpCached.At) < this._CacheTTLMs)
78
+ {
79
+ return tmpCached.Value;
80
+ }
81
+
82
+ let tmpStore = this._getStore();
83
+ let tmpSpecific = null;
84
+ let tmpWildcard = null;
85
+ if (tmpStore)
86
+ {
87
+ tmpSpecific = tmpStore.getActionDefault(pCapability, pAction || '');
88
+ if (!tmpSpecific && pAction)
89
+ {
90
+ tmpWildcard = tmpStore.getActionDefault(pCapability, '');
91
+ }
92
+ }
93
+
94
+ let tmpResolved = {};
95
+ for (let tmpField of Object.keys(HARD_DEFAULTS))
96
+ {
97
+ let tmpVal = null;
98
+ if (tmpSpecific && tmpSpecific[tmpField] != null && tmpSpecific[tmpField] !== 0)
99
+ {
100
+ tmpVal = tmpSpecific[tmpField];
101
+ }
102
+ else if (tmpWildcard && tmpWildcard[tmpField] != null && tmpWildcard[tmpField] !== 0)
103
+ {
104
+ tmpVal = tmpWildcard[tmpField];
105
+ }
106
+ else
107
+ {
108
+ tmpVal = this._fableSettingFallback(tmpField);
109
+ }
110
+ if (tmpVal == null) tmpVal = HARD_DEFAULTS[tmpField];
111
+ tmpResolved[tmpField] = tmpVal;
112
+ }
113
+
114
+ this._Cache[tmpKey] = { At: tmpNow, Value: tmpResolved };
115
+ return tmpResolved;
116
+ }
117
+
118
+ applyToWorkItem(pWorkItem, pRequestSettings)
119
+ {
120
+ let tmpResolved = this.resolve(pWorkItem.Capability, pWorkItem.Action);
121
+ let tmpSettings = pRequestSettings || {};
122
+
123
+ pWorkItem.TimeoutMs = pWorkItem.TimeoutMs
124
+ || parseInt(tmpSettings.timeoutMs, 10)
125
+ || parseInt(tmpSettings.TimeoutMs, 10)
126
+ || tmpResolved.TimeoutMs;
127
+
128
+ pWorkItem.MaxAttempts = pWorkItem.MaxAttempts
129
+ || parseInt(tmpSettings.maxRetries, 10)
130
+ || parseInt(tmpSettings.MaxAttempts, 10)
131
+ || tmpResolved.MaxAttempts;
132
+
133
+ pWorkItem.RetryBackoffMs = pWorkItem.RetryBackoffMs
134
+ || parseInt(tmpSettings.retryBackoffMs, 10)
135
+ || parseInt(tmpSettings.RetryBackoffMs, 10)
136
+ || tmpResolved.RetryBackoffMs;
137
+
138
+ if (pWorkItem.Priority == null)
139
+ {
140
+ let tmpPri = parseInt(tmpSettings.priority, 10);
141
+ if (isNaN(tmpPri)) tmpPri = parseInt(tmpSettings.Priority, 10);
142
+ if (isNaN(tmpPri)) tmpPri = tmpResolved.DefaultPriority;
143
+ pWorkItem.Priority = tmpPri;
144
+ }
145
+
146
+ return pWorkItem;
147
+ }
148
+
149
+ /**
150
+ * Compute a live p95 estimate from the last N completed samples.
151
+ * Used when no ExpectedWaitP95Ms is set on the row yet. Stored
152
+ * back into the row so future lookups are fast.
153
+ */
154
+ recomputeWaitBaseline(pCapability, pAction, pMinSamples)
155
+ {
156
+ let tmpStore = this._getStore();
157
+ if (!tmpStore) return null;
158
+ let tmpMin = pMinSamples || HARD_DEFAULTS.MinSamplesForBaseline;
159
+ let tmpSamples = tmpStore.queueWaitSamples(pCapability, pAction || '', 500);
160
+ if (tmpSamples.length < tmpMin) return null;
161
+ tmpSamples.sort((a, b) => a - b);
162
+ let tmpIdx = Math.max(0, Math.floor(tmpSamples.length * 0.95) - 1);
163
+ let tmpP95 = tmpSamples[tmpIdx];
164
+ tmpStore.upsertActionDefault({
165
+ Capability: pCapability,
166
+ Action: pAction || '',
167
+ ExpectedWaitP95Ms: tmpP95
168
+ });
169
+ this.invalidate();
170
+ return tmpP95;
171
+ }
172
+ }
173
+
174
+ module.exports = UltravisorBeaconActionDefaults;