bulltrackers-module 1.0.241 → 1.0.242
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.
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
* @fileoverview Build Reporter & Auto-Runner.
|
|
3
3
|
* Generates a "Pre-Flight" report of what the computation system WILL do.
|
|
4
4
|
* Simulates execution logic (Hash Mismatches) respecting DEFINITIVE start dates.
|
|
5
|
+
* UPDATED: Implements Parallel Execution to prevent DEADLINE_EXCEEDED on 90-day scans.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
const { analyzeDateExecution } = require('../WorkflowOrchestrator');
|
|
8
9
|
const { fetchComputationStatus } = require('../persistence/StatusRepository');
|
|
9
10
|
const { normalizeName, getExpectedDateStrings, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils');
|
|
10
11
|
const { FieldValue } = require('@google-cloud/firestore');
|
|
12
|
+
const pLimit = require('p-limit');
|
|
11
13
|
|
|
12
14
|
// Attempt to load package.json to get version. Path depends on where this is invoked.
|
|
13
15
|
let packageVersion = '1.0.300';
|
|
@@ -60,8 +62,6 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
|
|
|
60
62
|
const startDate = new Date();
|
|
61
63
|
startDate.setDate(today.getDate() - daysBack);
|
|
62
64
|
|
|
63
|
-
// We check UP TO yesterday usually, as today might be partial.
|
|
64
|
-
// But let's check today too to see immediate effects.
|
|
65
65
|
const datesToCheck = getExpectedDateStrings(startDate, today);
|
|
66
66
|
const manifestMap = new Map(manifest.map(c => [normalizeName(c.name), c]));
|
|
67
67
|
|
|
@@ -76,86 +76,106 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
|
|
|
76
76
|
let totalReRuns = 0;
|
|
77
77
|
let totalNew = 0;
|
|
78
78
|
|
|
79
|
-
// 2.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
79
|
+
// 2. PARALLEL PROCESSING (Fix for DEADLINE_EXCEEDED)
|
|
80
|
+
// Run 20 reads in parallel.
|
|
81
|
+
// 90 days / 20 concurrent = ~5 batches = ~2-3 seconds total vs 45+ seconds sequentially.
|
|
82
|
+
const limit = pLimit(20);
|
|
83
|
+
|
|
84
|
+
const processingPromises = datesToCheck.map(dateStr => limit(async () => {
|
|
85
|
+
try {
|
|
86
|
+
// A. Fetch REAL status from DB (What ran previously?)
|
|
87
|
+
const dailyStatus = await fetchComputationStatus(dateStr, config, dependencies);
|
|
88
|
+
|
|
89
|
+
// B. SMART MOCK Root Data
|
|
90
|
+
const dateObj = new Date(dateStr + 'T00:00:00Z');
|
|
91
|
+
const mockRootDataStatus = {
|
|
92
|
+
hasPortfolio: dateObj >= DEFINITIVE_EARLIEST_DATES.portfolio,
|
|
93
|
+
hasHistory: dateObj >= DEFINITIVE_EARLIEST_DATES.history,
|
|
94
|
+
hasSocial: dateObj >= DEFINITIVE_EARLIEST_DATES.social,
|
|
95
|
+
hasInsights: dateObj >= DEFINITIVE_EARLIEST_DATES.insights,
|
|
96
|
+
hasPrices: dateObj >= DEFINITIVE_EARLIEST_DATES.price
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// C. Run Logic Analysis
|
|
100
|
+
const analysis = analyzeDateExecution(dateStr, manifest, mockRootDataStatus, dailyStatus, manifestMap);
|
|
101
|
+
|
|
102
|
+
// D. Format Findings
|
|
103
|
+
const dateSummary = {
|
|
104
|
+
willRun: [],
|
|
105
|
+
willReRun: [],
|
|
106
|
+
blocked: [],
|
|
107
|
+
impossible: []
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// -- Runnable (New) --
|
|
111
|
+
analysis.runnable.forEach(item => {
|
|
112
|
+
dateSummary.willRun.push({ name: item.name, reason: "New / No Previous Record" });
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// -- Re-Runs (Hash Mismatch / Migration) --
|
|
116
|
+
analysis.reRuns.forEach(item => {
|
|
117
|
+
let reason = "Hash Mismatch";
|
|
118
|
+
let details = `Old: ${item.oldHash?.substring(0,6)}... New: ${item.newHash?.substring(0,6)}...`;
|
|
119
|
+
|
|
120
|
+
if (item.previousCategory) {
|
|
121
|
+
reason = "Migration";
|
|
122
|
+
details = `Moving ${item.previousCategory} -> ${item.newCategory}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
dateSummary.willReRun.push({ name: item.name, reason, details });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// -- Impossible (Permanent) --
|
|
129
|
+
analysis.impossible.forEach(item => {
|
|
130
|
+
dateSummary.impossible.push({ name: item.name, reason: item.reason });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// -- Blocked (Retriable) --
|
|
134
|
+
analysis.blocked.forEach(item => {
|
|
135
|
+
dateSummary.blocked.push({ name: item.name, reason: item.reason });
|
|
136
|
+
});
|
|
137
|
+
analysis.failedDependency.forEach(item => {
|
|
138
|
+
dateSummary.blocked.push({ name: item.name, reason: `Dependency Missing: ${item.missing.join(', ')}` });
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Return result for aggregation
|
|
142
|
+
const hasUpdates = dateSummary.willRun.length || dateSummary.willReRun.length || dateSummary.blocked.length || dateSummary.impossible.length;
|
|
119
143
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// -- Blocked (Retriable) --
|
|
135
|
-
// Mapped from analysis.blocked (Waiting for data) AND failedDependency
|
|
136
|
-
analysis.blocked.forEach(item => {
|
|
137
|
-
dateSummary.blocked.push({ name: item.name, reason: item.reason });
|
|
138
|
-
});
|
|
139
|
-
analysis.failedDependency.forEach(item => {
|
|
140
|
-
// If a dependency failed, it's blocked, not necessarily impossible (unless dependency is impossible)
|
|
141
|
-
dateSummary.blocked.push({ name: item.name, reason: `Dependency Missing: ${item.missing.join(', ')}` });
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Only add date to report if something interesting is happening
|
|
145
|
-
if (dateSummary.willRun.length || dateSummary.willReRun.length || dateSummary.blocked.length || dateSummary.impossible.length) {
|
|
146
|
-
reportData.dates[dateStr] = dateSummary;
|
|
147
|
-
totalNew += dateSummary.willRun.length;
|
|
148
|
-
totalReRuns += dateSummary.willReRun.length;
|
|
144
|
+
return {
|
|
145
|
+
dateStr,
|
|
146
|
+
dateSummary,
|
|
147
|
+
hasUpdates,
|
|
148
|
+
stats: {
|
|
149
|
+
new: dateSummary.willRun.length,
|
|
150
|
+
rerun: dateSummary.willReRun.length
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logger.log('ERROR', `[BuildReporter] Error analyzing date ${dateStr}: ${err.message}`);
|
|
156
|
+
return null;
|
|
149
157
|
}
|
|
150
|
-
}
|
|
158
|
+
}));
|
|
159
|
+
|
|
160
|
+
// Wait for all dates to process
|
|
161
|
+
const results = await Promise.all(processingPromises);
|
|
162
|
+
|
|
163
|
+
// 3. Aggregate Results
|
|
164
|
+
results.forEach(res => {
|
|
165
|
+
if (res && res.hasUpdates) {
|
|
166
|
+
reportData.dates[res.dateStr] = res.dateSummary;
|
|
167
|
+
totalNew += res.stats.new;
|
|
168
|
+
totalReRuns += res.stats.rerun;
|
|
169
|
+
}
|
|
170
|
+
});
|
|
151
171
|
|
|
152
172
|
reportData.summary = { totalReRuns, totalNew, scanRange: `${datesToCheck[0]} to ${datesToCheck[datesToCheck.length-1]}` };
|
|
153
173
|
|
|
154
|
-
//
|
|
174
|
+
// 4. Store Report
|
|
155
175
|
const reportRef = db.collection('computation_build_records').doc(buildId);
|
|
156
176
|
await reportRef.set(reportData);
|
|
157
177
|
|
|
158
|
-
//
|
|
178
|
+
// 5. Update 'latest' pointer
|
|
159
179
|
await db.collection('computation_build_records').doc('latest').set({
|
|
160
180
|
...reportData,
|
|
161
181
|
note: "Latest build report pointer."
|