bulltrackers-module 1.0.252 → 1.0.253
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FILENAME: computation-system/helpers/computation_dispatcher.js
|
|
3
3
|
* PURPOSE: "Smart Dispatcher" - Analyzes state and only dispatches valid, runnable tasks.
|
|
4
|
-
* UPDATED: Implements
|
|
4
|
+
* UPDATED: Implements Audit Ledger creation (PENDING state) before dispatch.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { getExpectedDateStrings, normalizeName, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils.js');
|
|
@@ -9,6 +9,7 @@ const { groupByPass, analyzeDateExecution } = require('../WorkflowOrchestrat
|
|
|
9
9
|
const { PubSubUtils } = require('../../core/utils/pubsub_utils');
|
|
10
10
|
const { fetchComputationStatus, updateComputationStatus } = require('../persistence/StatusRepository');
|
|
11
11
|
const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
|
|
12
|
+
const { commitBatchInChunks } = require('../persistence/FirestoreUtils'); // [NEW IMPORT]
|
|
12
13
|
const pLimit = require('p-limit');
|
|
13
14
|
|
|
14
15
|
const TOPIC_NAME = 'computation-tasks';
|
|
@@ -19,7 +20,7 @@ const STATUS_IMPOSSIBLE = 'IMPOSSIBLE';
|
|
|
19
20
|
* Performs full pre-flight checks (Root Data, Dependencies, History) before emitting.
|
|
20
21
|
*/
|
|
21
22
|
async function dispatchComputationPass(config, dependencies, computationManifest) {
|
|
22
|
-
const { logger }
|
|
23
|
+
const { logger, db } = dependencies; // Added db destructuring
|
|
23
24
|
const pubsubUtils = new PubSubUtils(dependencies);
|
|
24
25
|
const passToRun = String(config.COMPUTATION_PASS_TO_RUN);
|
|
25
26
|
|
|
@@ -86,7 +87,6 @@ async function dispatchComputationPass(config, dependencies, computationManifest
|
|
|
86
87
|
});
|
|
87
88
|
|
|
88
89
|
// Mark Blocked/Failed Deps (Temporary Failure)
|
|
89
|
-
// We write these so the status reflects reality, but we DO NOT dispatch them.
|
|
90
90
|
[...report.blocked, ...report.failedDependency].forEach(item => {
|
|
91
91
|
statusUpdates[item.name] = { hash: false, category: 'unknown', reason: item.reason };
|
|
92
92
|
});
|
|
@@ -103,6 +103,7 @@ async function dispatchComputationPass(config, dependencies, computationManifest
|
|
|
103
103
|
date: dateStr,
|
|
104
104
|
pass: passToRun,
|
|
105
105
|
computation: normalizeName(item.name),
|
|
106
|
+
hash: item.hash || item.newHash, // [NEW] Ensure Hash is passed for Ledger
|
|
106
107
|
timestamp: Date.now()
|
|
107
108
|
});
|
|
108
109
|
});
|
|
@@ -116,7 +117,30 @@ async function dispatchComputationPass(config, dependencies, computationManifest
|
|
|
116
117
|
|
|
117
118
|
// 4. Batch Dispatch Valid Tasks
|
|
118
119
|
if (tasksToDispatch.length > 0) {
|
|
119
|
-
|
|
120
|
+
// --- [NEW] STEP 4.1: CREATE AUDIT LEDGER ENTRIES ---
|
|
121
|
+
logger.log('INFO', `[Dispatcher] 📝 Creating Audit Ledger entries for ${tasksToDispatch.length} tasks...`);
|
|
122
|
+
|
|
123
|
+
const ledgerWrites = [];
|
|
124
|
+
for (const task of tasksToDispatch) {
|
|
125
|
+
const ledgerRef = db.collection(`computation_audit_ledger/${task.date}/passes/${task.pass}/tasks`).doc(task.computation);
|
|
126
|
+
ledgerWrites.push({
|
|
127
|
+
ref: ledgerRef,
|
|
128
|
+
data: {
|
|
129
|
+
status: 'PENDING',
|
|
130
|
+
computation: task.computation,
|
|
131
|
+
expectedHash: task.hash || 'unknown',
|
|
132
|
+
createdAt: new Date(),
|
|
133
|
+
retries: 0
|
|
134
|
+
},
|
|
135
|
+
options: { merge: true } // Merge allows updating retries/timestamps without wiping history
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Commit Ledger writes using chunked batch utility
|
|
140
|
+
await commitBatchInChunks(config, dependencies, ledgerWrites, 'AuditLedger Creation');
|
|
141
|
+
// ---------------------------------------------------
|
|
142
|
+
|
|
143
|
+
logger.log('INFO', `[Dispatcher] ✅ Generated ${tasksToDispatch.length} VALID tasks. Dispatching to Pub/Sub...`);
|
|
120
144
|
|
|
121
145
|
await pubsubUtils.batchPublishTasks(dependencies, {
|
|
122
146
|
topicName: TOPIC_NAME,
|
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Handles saving computation results with observability and Smart Cleanup.
|
|
3
|
+
* UPDATED: Implements Audit Ledger completion logic ("Closing the Ledger").
|
|
3
4
|
*/
|
|
4
5
|
const { commitBatchInChunks } = require('./FirestoreUtils');
|
|
5
6
|
const { updateComputationStatus } = require('./StatusRepository');
|
|
6
7
|
const { batchStoreSchemas } = require('../utils/schema_capture');
|
|
7
8
|
const { generateProcessId, PROCESS_TYPES } = require('../logger/logger');
|
|
9
|
+
// Note: normalizeName is typically needed for doc IDs, but keys in stateObj are usually already normalized.
|
|
10
|
+
// If not, ensure it is imported. Based on StandardExecutor, keys are normalized.
|
|
8
11
|
|
|
9
12
|
async function commitResults(stateObj, dStr, passName, config, deps, skipStatusWrite = false) {
|
|
10
13
|
const successUpdates = {};
|
|
11
14
|
const schemas = [];
|
|
12
15
|
const cleanupTasks = []; // Tasks to delete old data
|
|
13
|
-
const { logger }
|
|
16
|
+
const { logger, db } = deps;
|
|
14
17
|
const pid = generateProcessId(PROCESS_TYPES.STORAGE, passName, dStr);
|
|
18
|
+
|
|
19
|
+
// [NEW] Extract numeric pass ID from string (e.g., "Pass 1" -> "1")
|
|
20
|
+
const passNum = passName.replace(/[^0-9]/g, '');
|
|
15
21
|
|
|
16
22
|
for (const name in stateObj) {
|
|
17
23
|
const calc = stateObj[name];
|
|
18
24
|
try {
|
|
19
25
|
const result = await calc.getResult();
|
|
20
26
|
|
|
21
|
-
//
|
|
27
|
+
// Validate Result: Check for Null, Empty Object, or Zero
|
|
22
28
|
const isEmpty = !result ||
|
|
23
29
|
(typeof result === 'object' && Object.keys(result).length === 0) ||
|
|
24
30
|
(typeof result === 'number' && result === 0);
|
|
25
31
|
|
|
26
32
|
if (isEmpty) {
|
|
27
|
-
//
|
|
33
|
+
// Mark status as FALSE (Failed/Empty) so it re-runs or is flagged
|
|
28
34
|
if (calc.manifest.hash) {
|
|
29
35
|
successUpdates[name] = {
|
|
30
36
|
hash: false,
|
|
@@ -35,7 +41,7 @@ async function commitResults(stateObj, dStr, passName, config, deps, skipStatusW
|
|
|
35
41
|
continue;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
const mainDocRef =
|
|
44
|
+
const mainDocRef = db.collection(config.resultsCollection)
|
|
39
45
|
.doc(dStr)
|
|
40
46
|
.collection(config.resultsSubcollection)
|
|
41
47
|
.doc(calc.manifest.category)
|
|
@@ -44,6 +50,22 @@ async function commitResults(stateObj, dStr, passName, config, deps, skipStatusW
|
|
|
44
50
|
|
|
45
51
|
const updates = await prepareAutoShardedWrites(result, mainDocRef, logger);
|
|
46
52
|
|
|
53
|
+
// --- [NEW] ADD AUDIT LEDGER COMPLETION TO BATCH ---
|
|
54
|
+
if (passNum && calc.manifest) {
|
|
55
|
+
const ledgerRef = db.collection(`computation_audit_ledger/${dStr}/passes/${passNum}/tasks`).doc(name);
|
|
56
|
+
updates.push({
|
|
57
|
+
ref: ledgerRef,
|
|
58
|
+
data: {
|
|
59
|
+
status: 'COMPLETED',
|
|
60
|
+
completedAt: new Date(),
|
|
61
|
+
actualHash: calc.manifest.hash,
|
|
62
|
+
_verified: true
|
|
63
|
+
},
|
|
64
|
+
options: { merge: true }
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// --------------------------------------------------
|
|
68
|
+
|
|
47
69
|
// Capture Schema
|
|
48
70
|
if (calc.manifest.class.getSchema) {
|
|
49
71
|
const { class: _cls, ...safeMetadata } = calc.manifest;
|