bulltrackers-module 1.0.115 → 1.0.117
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.
|
@@ -157,7 +157,12 @@ async function runComputationOrchestrator(config, dependencies, computationManif
|
|
|
157
157
|
|
|
158
158
|
logger.log('INFO', `[Orchestrator] Processing all passes for ${dateStr}...`);
|
|
159
159
|
|
|
160
|
+
// This is the cache that accumulates results.
|
|
161
|
+
// Pass 1 (A,B,C) results are put in.
|
|
162
|
+
// Pass 2 gets (A,B,C) and adds (E,F,G).
|
|
163
|
+
// Pass 3 gets (A,B,C,E,F,G) and adds (I,J,K).
|
|
160
164
|
const dailyResultsCache = new Map();
|
|
165
|
+
|
|
161
166
|
// --- NEW: Keep track of skipped calcs ---
|
|
162
167
|
const skippedCalculations = new Set();
|
|
163
168
|
let passSuccess = true;
|
|
@@ -170,13 +175,15 @@ async function runComputationOrchestrator(config, dependencies, computationManif
|
|
|
170
175
|
|
|
171
176
|
const calcsInPass = passes[passNum] || [];
|
|
172
177
|
|
|
178
|
+
// "Standard" calcs are your Pass 1 (A,B,C,D)
|
|
173
179
|
const standardCalcs = calcsInPass.filter(c => c.type === 'standard');
|
|
180
|
+
// "Meta" calcs are your Pass 2, 3, 4 (E,F,G, etc.)
|
|
174
181
|
const metaCalcs = calcsInPass.filter(c => c.type === 'meta');
|
|
175
182
|
|
|
176
183
|
logger.log('INFO', `[Orchestrator] Starting Pass ${passNum} for ${dateStr} (${standardCalcs.length} standard, ${metaCalcs.length} meta).`);
|
|
177
184
|
|
|
178
185
|
try {
|
|
179
|
-
// --- 1. Run standard calcs for this pass ---
|
|
186
|
+
// --- 1. Run standard calcs for this pass (e.g., Pass 1) ---
|
|
180
187
|
if (standardCalcs.length > 0) {
|
|
181
188
|
|
|
182
189
|
// --- NEW: Filter calcs based on root data availability ---
|
|
@@ -192,6 +199,7 @@ async function runComputationOrchestrator(config, dependencies, computationManif
|
|
|
192
199
|
// --- END NEW FILTER ---
|
|
193
200
|
|
|
194
201
|
if (standardCalcsToRun.length > 0) {
|
|
202
|
+
// This function runs all "standard" calcs
|
|
195
203
|
const standardResults = await runUnifiedComputation(
|
|
196
204
|
dateToProcess,
|
|
197
205
|
standardCalcsToRun, // Pass the filtered list
|
|
@@ -201,13 +209,14 @@ async function runComputationOrchestrator(config, dependencies, computationManif
|
|
|
201
209
|
rootData
|
|
202
210
|
);
|
|
203
211
|
|
|
212
|
+
// Add results to the accumulating cache
|
|
204
213
|
for (const [calcName, result] of Object.entries(standardResults)) {
|
|
205
214
|
dailyResultsCache.set(calcName, result);
|
|
206
215
|
}
|
|
207
216
|
}
|
|
208
217
|
}
|
|
209
218
|
|
|
210
|
-
// --- 2. Run meta calcs for this pass ---
|
|
219
|
+
// --- 2. Run meta calcs for this pass (e.g., Pass 2, 3, 4) ---
|
|
211
220
|
if (metaCalcs.length > 0) {
|
|
212
221
|
|
|
213
222
|
// --- NEW: Filter calcs based on root data AND computation dependencies ---
|
|
@@ -244,16 +253,19 @@ async function runComputationOrchestrator(config, dependencies, computationManif
|
|
|
244
253
|
// --- END NEW FILTER ---
|
|
245
254
|
|
|
246
255
|
if (metaCalcsToRun.length > 0) {
|
|
256
|
+
// This function runs all "meta" calcs,
|
|
257
|
+
// giving it the *entire cache* of results from all previous passes
|
|
247
258
|
const metaResults = await runMetaComputation(
|
|
248
259
|
dateToProcess,
|
|
249
260
|
metaCalcsToRun, // Pass the filtered list
|
|
250
261
|
`Pass ${passNum} (Meta)`,
|
|
251
262
|
config,
|
|
252
263
|
dependencies,
|
|
253
|
-
dailyResultsCache,
|
|
264
|
+
dailyResultsCache, // <-- This is the accumulating cache
|
|
254
265
|
rootData
|
|
255
266
|
);
|
|
256
267
|
|
|
268
|
+
// Add results to the accumulating cache
|
|
257
269
|
for (const [calcName, result] of Object.entries(metaResults)) {
|
|
258
270
|
dailyResultsCache.set(calcName, result);
|
|
259
271
|
}
|
|
@@ -431,9 +443,10 @@ async function streamAndProcess(
|
|
|
431
443
|
|
|
432
444
|
|
|
433
445
|
/**
|
|
434
|
-
* Internal sub-pipe: Runs "standard" computations for a single date.
|
|
446
|
+
* Internal sub-pipe: Runs "standard" computations (Pass 1) for a single date.
|
|
435
447
|
* MODIFIED: Accepts pre-fetched rootData.
|
|
436
448
|
* MODIFIED: Returns a map of results for the in-memory cache.
|
|
449
|
+
* MODIFIED: Applies robustness fix.
|
|
437
450
|
*/
|
|
438
451
|
async function runUnifiedComputation(dateToProcess, calculationsToRun, passName, config, dependencies, rootData) {
|
|
439
452
|
const { db, logger } = dependencies;
|
|
@@ -533,12 +546,12 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
533
546
|
for (const calcName in state) { // calcName is already normalized
|
|
534
547
|
const calc = state[calcName];
|
|
535
548
|
|
|
536
|
-
// ---
|
|
549
|
+
// --- FIX #1: ROBUSTNESS FOR STANDARD CALCS ---
|
|
537
550
|
// Ensure every calc from the state has a result (null by default)
|
|
538
551
|
// This prevents the "Missing dependency" error downstream
|
|
539
|
-
// if the calculation failed to initialize.
|
|
552
|
+
// if the calculation failed to initialize or was skipped.
|
|
540
553
|
passResults[calcName] = null;
|
|
541
|
-
// --- END FIX ---
|
|
554
|
+
// --- END FIX #1 ---
|
|
542
555
|
|
|
543
556
|
if (!calc || typeof calc.getResult !== 'function') {
|
|
544
557
|
if (!calc) {
|
|
@@ -654,7 +667,8 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
654
667
|
|
|
655
668
|
|
|
656
669
|
/**
|
|
657
|
-
* Internal sub-pipe: Runs "meta" or "backtest" computations for a single date.
|
|
670
|
+
* Internal sub-pipe: Runs "meta" or "backtest" computations (Pass 2, 3, 4) for a single date.
|
|
671
|
+
* MODIFIED: Applies robustness fix.
|
|
658
672
|
*/
|
|
659
673
|
async function runMetaComputation(
|
|
660
674
|
dateToProcess,
|
|
@@ -662,13 +676,14 @@ async function runMetaComputation(
|
|
|
662
676
|
passName,
|
|
663
677
|
config,
|
|
664
678
|
dependencies,
|
|
665
|
-
dailyResultsCache,
|
|
679
|
+
dailyResultsCache, // <-- This is the accumulating cache from all previous passes
|
|
666
680
|
rootData
|
|
667
681
|
) {
|
|
668
682
|
const { db, logger } = dependencies;
|
|
669
683
|
const dateStr = dateToProcess.toISOString().slice(0, 10);
|
|
670
684
|
logger.log('INFO', `[${passName}] Starting run for ${dateStr} with ${calculationsToRun.length} calcs.`);
|
|
671
685
|
|
|
686
|
+
// This cache is *only* for the results of this specific pass
|
|
672
687
|
const passResults = {};
|
|
673
688
|
|
|
674
689
|
try {
|
|
@@ -685,13 +700,13 @@ async function runMetaComputation(
|
|
|
685
700
|
const category = manifestCalc.category || 'unknown';
|
|
686
701
|
const CalcClass = manifestCalc.class;
|
|
687
702
|
|
|
688
|
-
// ---
|
|
689
|
-
// Set a default null result. This ensures that if
|
|
690
|
-
// calculation is skipped (due to missing deps),
|
|
691
|
-
// its *own* dependents will see a
|
|
692
|
-
// instead of a "Missing dependency" error.
|
|
703
|
+
// --- FIX #2: ROBUSTNESS FOR META CALCS ---
|
|
704
|
+
// Set a default null result. This ensures that if this
|
|
705
|
+
// calculation is skipped (due to its own missing deps),
|
|
706
|
+
// its *own* dependents (later in this same pass) will see a
|
|
707
|
+
// 'null' value instead of a "Missing dependency" error.
|
|
693
708
|
passResults[calcName] = null;
|
|
694
|
-
// --- END FIX ---
|
|
709
|
+
// --- END FIX #2 ---
|
|
695
710
|
|
|
696
711
|
if (typeof CalcClass !== 'function') {
|
|
697
712
|
logger.log('ERROR', `[${passName}] Invalid class in manifest for ${calcName}. Skipping.`);
|
|
@@ -757,11 +772,51 @@ async function runMetaComputation(
|
|
|
757
772
|
const summaryData = {};
|
|
758
773
|
|
|
759
774
|
if (result && Object.keys(result).length > 0) {
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
775
|
+
|
|
776
|
+
// --- START SHARDING FIX ---
|
|
777
|
+
let isSharded = false;
|
|
778
|
+
const shardedCollections = {
|
|
779
|
+
// Add keys from sharded meta-calcs here
|
|
780
|
+
'sharded_user_profile': config.shardedUserProfileCollection,
|
|
781
|
+
'sharded_user_profitability': config.shardedProfitabilityCollection
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
for (const resultKey in shardedCollections) {
|
|
785
|
+
if (result[resultKey]) {
|
|
786
|
+
isSharded = true;
|
|
787
|
+
const shardCollectionName = shardedCollections[resultKey];
|
|
788
|
+
if (!shardCollectionName) {
|
|
789
|
+
logger.log('ERROR', `[${passName}] Missing config key for sharded collection: ${resultKey}`);
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const shardedData = result[resultKey];
|
|
794
|
+
|
|
795
|
+
for (const shardId in shardedData) {
|
|
796
|
+
const shardDocData = shardedData[shardId];
|
|
797
|
+
if (shardDocData && (Object.keys(shardDocData).length > 0)) {
|
|
798
|
+
const shardRef = db.collection(shardCollectionName).doc(shardId);
|
|
799
|
+
// Use { merge: true } to safely write to sharded docs
|
|
800
|
+
pendingWrites.push({ ref: shardRef, data: shardDocData, merge: true });
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// De-structure the sharded key from the result
|
|
805
|
+
const { [resultKey]: _, ...otherResults } = result;
|
|
806
|
+
result = otherResults; // Re-assign 'result' to be only the non-sharded data
|
|
807
|
+
}
|
|
808
|
+
}
|
|
764
809
|
|
|
810
|
+
// After all sharding is handled, save any *remaining* data
|
|
811
|
+
// (e.g., 'daily_investor_scores' from user-investment-profile)
|
|
812
|
+
if (result && Object.keys(result).length > 0) {
|
|
813
|
+
const computationDocRef = resultsCollectionRef.doc(category)
|
|
814
|
+
.collection(compsSub)
|
|
815
|
+
.doc(calcName);
|
|
816
|
+
pendingWrites.push({ ref: computationDocRef, data: result });
|
|
817
|
+
}
|
|
818
|
+
// --- END SHARDING FIX ---
|
|
819
|
+
|
|
765
820
|
if (!summaryData[category]) summaryData[category] = {};
|
|
766
821
|
summaryData[category][calcName] = true;
|
|
767
822
|
|
|
@@ -797,7 +852,7 @@ async function runMetaComputation(
|
|
|
797
852
|
const completionStatus = successCount === calculationsToRun.length ? 'SUCCESS' : 'WARN';
|
|
798
853
|
logger.log(completionStatus, `[${passName}] Completed ${dateStr}. Success: ${successCount}/${calculationsToRun.length}.`);
|
|
799
854
|
|
|
800
|
-
return passResults;
|
|
855
|
+
return passResults; // Return the results *from this pass only*
|
|
801
856
|
|
|
802
857
|
} catch (err) {
|
|
803
858
|
logger.log('ERROR', `[${passName}] Fatal error for ${dateStr}`, { errorMessage: err.message, stack: err.stack });
|
|
@@ -808,4 +863,4 @@ async function runMetaComputation(
|
|
|
808
863
|
|
|
809
864
|
module.exports = {
|
|
810
865
|
runComputationOrchestrator,
|
|
811
|
-
};
|
|
866
|
+
};
|