bulltrackers-module 1.0.284 → 1.0.285

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.
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @fileoverview Monitor helper for Cloud Workflows.
3
+ * Checks the state of the Audit Ledger to determine if a pass is complete.
4
+ * This function is stateless and receives dependencies via injection.
5
+ */
6
+
7
+ /**
8
+ * Checks the status of a specific computation pass.
9
+ * @param {object} req - Express request object (query: date, pass).
10
+ * @param {object} res - Express response object.
11
+ * @param {object} dependencies - Contains db (Firestore), logger.
12
+ */
13
+ async function checkPassStatus(req, res, dependencies) {
14
+ const { db, logger } = dependencies;
15
+ const { date, pass } = req.query;
16
+
17
+ if (!date || !pass) {
18
+ return res.status(400).json({ error: "Missing 'date' or 'pass' query parameters." });
19
+ }
20
+
21
+ const ledgerPath = `computation_audit_ledger/${date}/passes/${pass}/tasks`;
22
+ logger.log('INFO', `[Monitor] Checking status for ${date} Pass ${pass} at ${ledgerPath}`);
23
+
24
+ try {
25
+ const tasksRef = db.collection(ledgerPath);
26
+
27
+ // 1. Check for Active Tasks (Blocking)
28
+ // If anything is PENDING or IN_PROGRESS, the system is still working.
29
+ const runningSnap = await tasksRef.where('status', 'in', ['PENDING', 'IN_PROGRESS']).get();
30
+
31
+ if (!runningSnap.empty) {
32
+ logger.log('INFO', `[Monitor] Pass ${pass} is RUNNING. Active tasks: ${runningSnap.size}`);
33
+ return res.status(200).json({
34
+ state: 'RUNNING',
35
+ activeCount: runningSnap.size
36
+ });
37
+ }
38
+
39
+ // 2. Check for Failures (Retry Condition)
40
+ // If nothing is running, we check if anything ended in FAILED state.
41
+ // We consider these "retryable" by re-triggering the dispatcher.
42
+ const failedSnap = await tasksRef.where('status', '==', 'FAILED').get();
43
+
44
+ if (!failedSnap.empty) {
45
+ logger.log('WARN', `[Monitor] Pass ${pass} finished with FAILURES. Count: ${failedSnap.size}`);
46
+ return res.status(200).json({
47
+ state: 'HAS_FAILURES',
48
+ failureCount: failedSnap.size
49
+ });
50
+ }
51
+
52
+ // 3. Clean Success
53
+ // No running tasks, no failed tasks.
54
+ logger.log('INFO', `[Monitor] Pass ${pass} COMPLETED successfully.`);
55
+ return res.status(200).json({ state: 'SUCCESS' });
56
+
57
+ } catch (error) {
58
+ logger.log('ERROR', `[Monitor] Failed to check status: ${error.message}`);
59
+ return res.status(500).json({ error: error.message });
60
+ }
61
+ }
62
+
63
+ module.exports = { checkPassStatus };
@@ -0,0 +1,143 @@
1
+ # Cloud Workflows Definition for BullTrackers Computation Pipeline
2
+ # Orchestrates 5 sequential passes with Self-Healing (Retry) logic.
3
+
4
+ main:
5
+ params: [input]
6
+ steps:
7
+ - init:
8
+ assign:
9
+ - project: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
10
+ - location: "europe-west1"
11
+ # If 'date' is provided in input, use it. Otherwise default to today (YYYY-MM-DD).
12
+ - date_to_run: ${default(map.get(input, "date"), text.substring(time.format(sys.now()), 0, 10))}
13
+ - passes: ["1", "2", "3", "4", "5"]
14
+ - max_retries: 3
15
+ - propagation_wait_seconds: 300 # 5 Minutes
16
+ # URL of the new Monitor Function
17
+ - monitor_url: ${"https://europe-west1-" + project + ".cloudfunctions.net/computation-monitor"}
18
+
19
+ # ======================================================
20
+ # MAIN LOOP: Iterate through Passes 1 to 5
21
+ # ======================================================
22
+ - run_passes:
23
+ for:
24
+ value: pass_id
25
+ in: ${passes}
26
+ steps:
27
+ - init_pass_vars:
28
+ assign:
29
+ - attempt_count: 0
30
+ - pass_success: false
31
+ # Construct URL for the specific pass function (e.g. computation-pass-1)
32
+ - dispatcher_url: ${"https://europe-west1-" + project + ".cloudfunctions.net/computation-pass-" + pass_id}
33
+
34
+ # -----------------------------------------------
35
+ # RETRY LOOP: Try to complete the pass up to 3 times
36
+ # -----------------------------------------------
37
+ - pass_retry_loop:
38
+ switch:
39
+ - condition: ${attempt_count < max_retries and not pass_success}
40
+ steps:
41
+ - increment_attempt:
42
+ assign:
43
+ - attempt_count: ${attempt_count + 1}
44
+
45
+ - log_start:
46
+ call: sys.log
47
+ args:
48
+ text: ${"Starting Pass " + pass_id + " (Attempt " + attempt_count + ") for " + date_to_run}
49
+ severity: "INFO"
50
+
51
+ # 1. TRIGGER DISPATCHER (Fire and Forget mechanism via HTTP)
52
+ # The dispatcher analyzes missing data and queues tasks.
53
+ - trigger_dispatcher:
54
+ call: http.get
55
+ args:
56
+ url: ${dispatcher_url}
57
+ query:
58
+ date: ${date_to_run}
59
+ auth:
60
+ type: OIDC
61
+ timeout: 1800 # 30 mins max for dispatch analysis
62
+ result: dispatch_response
63
+
64
+ # 2. PROPAGATION WAIT
65
+ # Wait for dispatcher to queue tasks and workers to start/finish
66
+ - wait_for_propagation:
67
+ call: sys.log
68
+ args:
69
+ text: ${"Pass " + pass_id + " dispatched. Waiting " + propagation_wait_seconds + "s for propagation..."}
70
+ next: sleep_propagation
71
+
72
+ - sleep_propagation:
73
+ call: sys.sleep
74
+ args:
75
+ seconds: ${propagation_wait_seconds}
76
+
77
+ # 3. MONITORING LOOP
78
+ # Poll until RUNNING state clears
79
+ - monitor_loop:
80
+ call: http.get
81
+ args:
82
+ url: ${monitor_url}
83
+ query:
84
+ date: ${date_to_run}
85
+ pass: ${pass_id}
86
+ auth:
87
+ type: OIDC
88
+ result: status_resp
89
+
90
+ - evaluate_status:
91
+ switch:
92
+ # CASE A: Still Running -> Sleep and Poll Again
93
+ - condition: ${status_resp.body.state == "RUNNING"}
94
+ steps:
95
+ - log_running:
96
+ call: sys.log
97
+ args:
98
+ text: ${"Pass " + pass_id + " is RUNNING (" + status_resp.body.activeCount + " active). Waiting..."}
99
+ - sleep_polling:
100
+ call: sys.sleep
101
+ args:
102
+ seconds: 60
103
+ - next: monitor_loop
104
+
105
+ # CASE B: Clean Success -> Mark done, Break Retry Loop
106
+ - condition: ${status_resp.body.state == "SUCCESS"}
107
+ steps:
108
+ - log_success:
109
+ call: sys.log
110
+ args:
111
+ text: ${"Pass " + pass_id + " COMPLETED successfully."}
112
+ severity: "INFO"
113
+ - mark_success:
114
+ assign:
115
+ - pass_success: true
116
+ - next: pass_retry_loop # Will exit loop due to pass_success=true
117
+
118
+ # CASE C: Failures Found -> Continue Retry Loop (will trigger dispatcher again)
119
+ - condition: ${status_resp.body.state == "HAS_FAILURES"}
120
+ steps:
121
+ - log_failure:
122
+ call: sys.log
123
+ args:
124
+ text: ${"Pass " + pass_id + " has " + status_resp.body.failureCount + " FAILURES. Attempting Retry."}
125
+ severity: "WARNING"
126
+ - next: pass_retry_loop
127
+
128
+ # -----------------------------------------------
129
+ # END RETRY LOOP
130
+ # -----------------------------------------------
131
+
132
+ - check_final_status:
133
+ switch:
134
+ - condition: ${not pass_success}
135
+ steps:
136
+ - log_giving_up:
137
+ call: sys.log
138
+ args:
139
+ text: ${"Pass " + pass_id + " failed after " + max_retries + " attempts. Proceeding to next pass with potential gaps."}
140
+ severity: "ERROR"
141
+
142
+ - finish:
143
+ return: "Pipeline Execution Complete"
package/index.js CHANGED
@@ -29,8 +29,9 @@ const { handleUpdate } = require('./functions
29
29
  const { build: buildManifest } = require('./functions/computation-system/context/ManifestBuilder');
30
30
  const { dispatchComputationPass } = require('./functions/computation-system/helpers/computation_dispatcher');
31
31
  const { handleComputationTask } = require('./functions/computation-system/helpers/computation_worker');
32
- // [NEW] Import Report Tools
33
32
  const { ensureBuildReport, generateBuildReport } = require('./functions/computation-system/tools/BuildReporter');
33
+ // [NEW] Import Monitor
34
+ const { checkPassStatus } = require('./functions/computation-system/helpers/monitor');
34
35
 
35
36
  const dataLoader = require('./functions/computation-system/utils/data_loader');
36
37
  const computationUtils = require('./functions/computation-system/utils/utils');
@@ -51,8 +52,7 @@ const { runBackfillAssetPrices } = require('./functions
51
52
  // Proxy
52
53
  const { handlePost } = require('./functions/appscript-api/index');
53
54
 
54
- // NEW
55
-
55
+ // Root Indexer
56
56
  const { runRootDataIndexer } = require('./functions/root-data-indexer/index');
57
57
 
58
58
  const core = {
@@ -92,9 +92,10 @@ const computationSystem = {
92
92
  dataLoader,
93
93
  computationUtils,
94
94
  buildManifest,
95
- // [NEW] Export Tools
96
95
  ensureBuildReport,
97
96
  generateBuildReport,
97
+ // [NEW] Export Monitor Pipe
98
+ checkPassStatus
98
99
  };
99
100
 
100
101
  const api = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.284",
3
+ "version": "1.0.285",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [