bulltrackers-module 1.0.313 → 1.0.315
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.
|
@@ -26,6 +26,39 @@ const LAYER_GROUPS = {
|
|
|
26
26
|
'validators': ValidatorsLayer
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Heuristic to estimate the "weight" of a calculation based on its output structure.
|
|
31
|
+
*/
|
|
32
|
+
function estimateComplexity(Class, metadata) {
|
|
33
|
+
let weight = 1.0; // Base weight (for single aggregate values)
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const schema = typeof Class.getSchema === 'function' ? Class.getSchema() : {};
|
|
37
|
+
|
|
38
|
+
// 1. Detect Map-like outputs (per-ticker, per-sector, per-user)
|
|
39
|
+
// If the schema uses patternProperties, it's likely a dynamic map.
|
|
40
|
+
if (schema.patternProperties || (schema.type === 'object' && !schema.properties)) {
|
|
41
|
+
weight *= 5.0; // Higher weight for dynamic maps (e.g., Per Sector/Ticker)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. Metadata hints
|
|
45
|
+
const name = Class.name.toLowerCase();
|
|
46
|
+
if (name.includes('perstock') || name.includes('perticker')) weight *= 2.0;
|
|
47
|
+
if (name.includes('peruser')) weight *= 10.0; // Very high cost
|
|
48
|
+
|
|
49
|
+
// 3. Dependency hints
|
|
50
|
+
if (metadata.rootDataDependencies && metadata.rootDataDependencies.includes('portfolio')) {
|
|
51
|
+
// Portfolio-based calcs usually iterate over all users in the StandardExecutor
|
|
52
|
+
weight *= 1.5;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
} catch (e) {
|
|
56
|
+
// Fallback to base weight if schema is missing/broken
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return weight;
|
|
60
|
+
}
|
|
61
|
+
|
|
29
62
|
function generateLayerHashes(layerExports, layerName) {
|
|
30
63
|
const hashes = {};
|
|
31
64
|
const keys = Object.keys(layerExports).sort();
|
|
@@ -170,6 +203,7 @@ function buildManifest(productLinesToRun = [], calculations) {
|
|
|
170
203
|
if (typeof Class.getDependencies !== 'function') { log.fatal(`Calculation "${normalizedName}" missing static getDependencies().`); hasFatalError = true; return; }
|
|
171
204
|
|
|
172
205
|
const metadata = Class.getMetadata();
|
|
206
|
+
const weight = estimateComplexity(Class, metadata)
|
|
173
207
|
const dependencies = Class.getDependencies().map(normalizeName);
|
|
174
208
|
const codeStr = Class.toString();
|
|
175
209
|
const selfCodeHash = generateCodeHash(codeStr);
|
|
@@ -222,6 +256,7 @@ function buildManifest(productLinesToRun = [], calculations) {
|
|
|
222
256
|
dependencies: dependencies,
|
|
223
257
|
pass: 0,
|
|
224
258
|
hash: intrinsicHash,
|
|
259
|
+
weight: weight,
|
|
225
260
|
composition: {
|
|
226
261
|
epoch: SYSTEM_EPOCH,
|
|
227
262
|
code: selfCodeHash,
|
|
@@ -12,7 +12,7 @@ const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
|
|
|
12
12
|
const crypto = require('crypto');
|
|
13
13
|
|
|
14
14
|
const OOM_THRESHOLD_MB = 1500;
|
|
15
|
-
const
|
|
15
|
+
const BASE_SECONDS_PER_WEIGHT_UNIT = 15;
|
|
16
16
|
|
|
17
17
|
async function getHighMemReroutes(db, date, pass, tasks) {
|
|
18
18
|
const reroutes = [];
|
|
@@ -47,6 +47,8 @@ async function dispatchComputationPass(config, dependencies, computationManifest
|
|
|
47
47
|
const passes = groupByPass(computationManifest);
|
|
48
48
|
const calcsInThisPass = passes[passToRun] || [];
|
|
49
49
|
|
|
50
|
+
const manifestWeightMap = new Map(computationManifest.map(c => [normalizeName(c.name), c.weight || 1.0]));
|
|
51
|
+
|
|
50
52
|
if (!calcsInThisPass.length) {
|
|
51
53
|
logger.log('WARN', `[Dispatcher] 🛑 No calculations found for Pass ${passToRun}.`);
|
|
52
54
|
return { status: 'MOVE_TO_NEXT_PASS', dispatched: 0 };
|
|
@@ -117,21 +119,27 @@ async function dispatchComputationPass(config, dependencies, computationManifest
|
|
|
117
119
|
return { status: 'MOVE_TO_NEXT_PASS', dispatched: 0, etaSeconds: 0 };
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
const totalweight = selectedTasks.reduce((sum, t) => {
|
|
123
|
+
const weight = manifestWeightMap.get(normalizeName(t.name)) || 1.0;
|
|
124
|
+
return sum + weight;
|
|
125
|
+
}, 0);
|
|
126
|
+
|
|
120
127
|
// 2. Prepare Payload and Telemetry
|
|
121
128
|
const currentDispatchId = crypto.randomUUID();
|
|
122
|
-
const etaSeconds = Math.max(20,
|
|
129
|
+
const etaSeconds = Math.max(20, Math.ceil(totalweight * BASE_SECONDS_PER_WEIGHT_UNIT));
|
|
123
130
|
const remainingDatesCount = Math.max(0, dirtyDates.length - targetCursorN);
|
|
124
131
|
|
|
125
132
|
const computationNames = selectedTasks.map(t => t.name);
|
|
126
133
|
|
|
127
134
|
logger.log('INFO', `[Dispatcher] ✅ Dispatching ${selectedTasks.length} tasks for ${selectedDate}. ETA: ${etaSeconds}s. [Mode: ${isSweep ? 'RECOVERY' : 'NORMAL'}]`, {
|
|
128
|
-
date:
|
|
129
|
-
pass:
|
|
130
|
-
dispatchedCount:
|
|
135
|
+
date: selectedDate,
|
|
136
|
+
pass: passToRun,
|
|
137
|
+
dispatchedCount: selectedTasks.length,
|
|
131
138
|
remainingCursorDates: remainingDatesCount,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
totalweight: totalweight,
|
|
140
|
+
etaSeconds: etaSeconds,
|
|
141
|
+
dispatchId: currentDispatchId,
|
|
142
|
+
tasks: computationNames
|
|
135
143
|
});
|
|
136
144
|
|
|
137
145
|
const mapToTaskPayload = (t) => ({
|