bulltrackers-module 1.0.313 → 1.0.314

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 SECONDS_PER_CALC_MARGIN = 25;
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, selectedTasks.length * SECONDS_PER_CALC_MARGIN);
129
+ const etaSeconds = Math.max(20, Math.cell(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: selectedDate,
129
- pass: passToRun,
130
- dispatchedCount: selectedTasks.length,
135
+ date: selectedDate,
136
+ pass: passToRun,
137
+ dispatchedCount: selectedTasks.length,
131
138
  remainingCursorDates: remainingDatesCount,
132
- etaSeconds: etaSeconds,
133
- dispatchId: currentDispatchId,
134
- tasks: computationNames
139
+ totalweight: totalweight,
140
+ etaSeconds: etaSeconds,
141
+ dispatchId: currentDispatchId,
142
+ tasks: computationNames
135
143
  });
136
144
 
137
145
  const mapToTaskPayload = (t) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.313",
3
+ "version": "1.0.314",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [