bulltrackers-module 1.0.126 → 1.0.128

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.
@@ -1,8 +1,3 @@
1
- /*
2
- * FILENAME: CloudFunctions/NpmWrappers/bulltrackers-module/functions/orchestrator/helpers/discovery_helpers.js
3
- * (FIXED: Publishes 'discover' tasks in the correct batch format { tasks: [...] })
4
- */
5
-
6
1
  /**
7
2
  * @fileoverview Orchestrator's discovery sub-pipes.
8
3
  * REFACTORED: All functions are now stateless and receive dependencies.
@@ -18,46 +13,17 @@
18
13
  async function checkDiscoveryNeed(userType, config, dependencies) {
19
14
  const { logger, firestoreUtils } = dependencies;
20
15
  logger.log('INFO', `[Orchestrator Helpers] Checking discovery need for ${userType}...`);
21
-
22
- const {
23
- targetUsersPerBlock,
24
- speculatorBlockCountsDocPath,
25
- normalBlockCountsDocPath,
26
- speculatorInstruments,
27
- allHighValueBlocks
28
- } = config;
29
-
16
+ const { targetUsersPerBlock, speculatorBlockCountsDocPath, normalBlockCountsDocPath,speculatorInstruments,allHighValueBlocks} = config;
30
17
  const isSpeculator = userType === 'speculator';
31
-
32
- // Pass dependencies and config to core util
33
- const blockCounts = await firestoreUtils.getBlockCapacities(
34
- dependencies,
35
- { speculatorBlockCountsDocPath, normalBlockCountsDocPath },
36
- userType
37
- );
38
-
18
+ const blockCounts = await firestoreUtils.getBlockCapacities(dependencies, { speculatorBlockCountsDocPath, normalBlockCountsDocPath }, userType);
39
19
  let underPopulatedBlocks = [];
40
- if (isSpeculator) {
41
- for (const instrument of speculatorInstruments) {
42
- for (const block of allHighValueBlocks) {
43
- const instrumentBlockKey = `${instrument}_${block.startId}`;
44
- if ((blockCounts[instrumentBlockKey] || 0) < targetUsersPerBlock) {
45
- underPopulatedBlocks.push({ ...block, instrument });
46
- }
47
- }
48
- }
49
- } else {
50
- underPopulatedBlocks = allHighValueBlocks.filter(
51
- block => (blockCounts[block.startId] || 0) < targetUsersPerBlock
52
- );
53
- }
54
-
55
- const needsDiscovery = underPopulatedBlocks.length > 0;
56
- if (!needsDiscovery) {
57
- logger.log('SUCCESS', `✅ All blocks are at target capacity for ${userType} users.`);
58
- } else {
59
- logger.log('INFO', `Found ${underPopulatedBlocks.length} underpopulated blocks/instruments for ${userType}.`);
60
- }
20
+ if (isSpeculator) {for (const instrument of speculatorInstruments) { for (const block of allHighValueBlocks)
21
+ {const instrumentBlockKey = `${instrument}_${block.startId}`;
22
+ if ((blockCounts[instrumentBlockKey] || 0) < targetUsersPerBlock) {underPopulatedBlocks.push({ ...block, instrument });}}}}
23
+ else {underPopulatedBlocks = allHighValueBlocks.filter(block => (blockCounts[block.startId] || 0) < targetUsersPerBlock);}
24
+ const needsDiscovery = underPopulatedBlocks.length > 0;
25
+ if (!needsDiscovery) {logger.log('SUCCESS', `✅ All blocks are at target capacity for ${userType} users.`);}
26
+ else {logger.log('INFO', `Found ${underPopulatedBlocks.length} underpopulated blocks/instruments for ${userType}.`);}
61
27
  return { needsDiscovery, blocksToFill: underPopulatedBlocks };
62
28
  }
63
29
 
@@ -72,89 +38,29 @@ async function checkDiscoveryNeed(userType, config, dependencies) {
72
38
  async function getDiscoveryCandidates(userType, blocksToFill, config, dependencies) {
73
39
  const { logger, firestoreUtils } = dependencies;
74
40
  logger.log('INFO', `[Orchestrator Helpers] Getting discovery candidates for ${userType}...`);
75
-
76
- const {
77
- maxTasksPerRun,
78
- discoveryBatchSize,
79
- maxRandomCidsToDiscover,
80
- specBlocksCollection,
81
- pendingSpecCollection,
82
- normalUserCollection,
83
- invalidSpecCollection,
84
- speculatorInstrumentSet,
85
- snapshotsSubCollectionName,
86
- partsSubCollectionName
87
- } = config;
88
-
41
+ const {maxTasksPerRun,discoveryBatchSize,maxRandomCidsToDiscover,specBlocksCollection,pendingSpecCollection,normalUserCollection,invalidSpecCollection,speculatorInstrumentSet,snapshotsSubCollectionName,partsSubCollectionName} = config;
89
42
  const isSpeculator = userType === 'speculator';
90
43
  const dispatchedCids = new Set();
91
-
92
- // Pass dependencies and config to core util
93
- const latestNormalPortfolios = await firestoreUtils.getLatestNormalUserPortfolios(
94
- dependencies,
95
- { normalUserCollectionName: normalUserCollection, snapshotsSubCollectionName, partsSubCollectionName }
96
- );
44
+ const latestNormalPortfolios = await firestoreUtils.getLatestNormalUserPortfolios(dependencies,{ normalUserCollectionName: normalUserCollection, snapshotsSubCollectionName, partsSubCollectionName });
97
45
  const existingNormalUserIds = new Set(Object.keys(latestNormalPortfolios));
98
-
99
- // Pass dependencies and config to core util
100
- const exclusionIds = await firestoreUtils.getExclusionIds(dependencies, {
101
- specBlocksCollection,
102
- pendingSpecCollection,
103
- normalUserCollection,
104
- invalidSpecCollection,
105
- existingNormalUserIds
106
- }, userType);
107
-
108
- if (isSpeculator) {
109
- // Pass dependencies to core util
110
- const prioritizedCandidates = await firestoreUtils.getPrioritizedSpeculators(
111
- dependencies,
112
- exclusionIds,
113
- speculatorInstrumentSet,
114
- latestNormalPortfolios
115
- );
46
+ const exclusionIds = await firestoreUtils.getExclusionIds(dependencies, {specBlocksCollection, pendingSpecCollection, normalUserCollection,invalidSpecCollection,existingNormalUserIds}, userType);
47
+ if (isSpeculator) {const prioritizedCandidates = await firestoreUtils.getPrioritizedSpeculators(dependencies,exclusionIds, speculatorInstrumentSet,latestNormalPortfolios);
116
48
  logger.log('INFO', `Found ${prioritizedCandidates.length} potential new speculators from existing user pool.`);
117
-
118
- prioritizedCandidates.forEach(cidStr => {
119
- if (dispatchedCids.size < maxTasksPerRun) {
120
- dispatchedCids.add(cidStr);
121
- }
122
- });
123
- logger.log('INFO', `Added ${dispatchedCids.size} prioritized speculators.`);
124
- }
125
-
126
- // --- Random Generation ---
49
+ prioritizedCandidates.forEach(cidStr => {if (dispatchedCids.size < maxTasksPerRun) {dispatchedCids.add(cidStr);}});
50
+ logger.log('INFO', `Added ${dispatchedCids.size} prioritized speculators.`);}
127
51
  let dispatchedRandomCidCount = 0;
128
52
  const initialDispatchedCount = dispatchedCids.size;
129
-
130
- while ((dispatchedRandomCidCount + initialDispatchedCount) < maxRandomCidsToDiscover &&
131
- dispatchedCids.size < maxTasksPerRun && blocksToFill.length > 0)
132
- {
53
+ while ((dispatchedRandomCidCount + initialDispatchedCount) < maxRandomCidsToDiscover &&dispatchedCids.size < maxTasksPerRun && blocksToFill.length > 0){
133
54
  const blockIndex = dispatchedCids.size % blocksToFill.length;
134
55
  const block = blocksToFill[blockIndex];
135
56
  if (!block) break;
136
-
137
57
  for (let j = 0; j < discoveryBatchSize; j++) {
138
- if ((dispatchedRandomCidCount + initialDispatchedCount) >= maxRandomCidsToDiscover || dispatchedCids.size >= maxTasksPerRun) break;
139
-
140
- let randomId;
141
- let retryCount = 0;
142
- const MAX_RETRIES = 50;
143
- do {
144
- if (++retryCount > MAX_RETRIES) break;
145
- randomId = String(Math.floor(Math.random() * 1000000) + block.startId);
146
- } while (
147
- exclusionIds.has(randomId) ||
148
- dispatchedCids.has(randomId)
149
- );
150
-
151
- if (retryCount <= MAX_RETRIES) {
152
- dispatchedCids.add(randomId);
153
- dispatchedRandomCidCount++;
154
- }
155
- }
156
- }
157
-
58
+ if ((dispatchedRandomCidCount + initialDispatchedCount) >= maxRandomCidsToDiscover || dispatchedCids.size >= maxTasksPerRun) break;
59
+ let randomId;
60
+ let retryCount = 0;
61
+ const MAX_RETRIES = 50;
62
+ do {if (++retryCount > MAX_RETRIES) break;randomId = String(Math.floor(Math.random() * 1000000) + block.startId);} while (exclusionIds.has(randomId) ||dispatchedCids.has(randomId));
63
+ if (retryCount <= MAX_RETRIES) {dispatchedCids.add(randomId);dispatchedRandomCidCount++;}}}
158
64
  logger.log('INFO', `Generated ${dispatchedRandomCidCount} random CIDs for ${userType} discovery.`);
159
65
  logger.log('INFO', `Total candidates for dispatch: ${dispatchedCids.size}`);
160
66
  return dispatchedCids;
@@ -169,90 +75,36 @@ async function getDiscoveryCandidates(userType, blocksToFill, config, dependenci
169
75
  * @returns {Promise<void>}
170
76
  */
171
77
  async function dispatchDiscovery(userType, candidates, config, dependencies) {
172
- const { logger, firestoreUtils, pubsubUtils, pubsub } = dependencies; // <-- Add pubsub
173
- const {
174
- topicName,
175
- dispatchBatchSize,
176
- pubsubBatchSize, // This is no longer used by this function
177
- pendingSpecCollection,
178
- pendingMaxFieldsPerDoc,
179
- pendingMaxWritesPerBatch
180
- } = config;
181
-
182
- if (candidates.size === 0) {
183
- logger.log('INFO', `[Orchestrator Helpers] No ${userType} candidates to dispatch.`);
184
- return;
185
- }
78
+ const { logger, firestoreUtils, pubsubUtils, pubsub } = dependencies;
79
+ const {topicName,dispatchBatchSize,pubsubBatchSize, pendingSpecCollection, pendingMaxFieldsPerDoc,pendingMaxWritesPerBatch} = config;
80
+ if (candidates.size === 0) {logger.log('INFO', `[Orchestrator Helpers] No ${userType} candidates to dispatch.`);return;}
186
81
  logger.log('INFO', `[Orchestrator Helpers] Dispatching ${candidates.size} discovery tasks for ${userType}...`);
187
-
188
82
  const isSpeculator = userType === 'speculator';
189
83
  const cidsArray = Array.from(candidates);
190
-
191
- if (isSpeculator) {
192
- await firestoreUtils.clearCollection(dependencies, pendingSpecCollection);
193
- await firestoreUtils.batchWriteShardedIds(dependencies, {
194
- collectionPath: pendingSpecCollection,
195
- items: cidsArray,
196
- timestamp: new Date(),
197
- maxFieldsPerDoc: pendingMaxFieldsPerDoc,
198
- maxWritesPerBatch: pendingMaxWritesPerBatch
199
- });
200
- }
201
-
84
+ if (isSpeculator)
85
+ {await firestoreUtils.clearCollection(dependencies, pendingSpecCollection);
86
+ await firestoreUtils.batchWriteShardedIds(dependencies, {collectionPath: pendingSpecCollection,items: cidsArray, timestamp: new Date(),maxFieldsPerDoc: pendingMaxFieldsPerDoc,maxWritesPerBatch: pendingMaxWritesPerBatch});}
202
87
  const tasks = [];
203
- for (let i = 0; i < cidsArray.length; i += dispatchBatchSize) {
204
- const batchCids = cidsArray.slice(i, i + dispatchBatchSize).map(cid => parseInt(cid));
205
- if (batchCids.length > 0) {
206
- const blockId = Math.floor(batchCids[0] / 1000000) * 1000000;
207
- tasks.push({
208
- type: 'discover',
209
- cids: batchCids,
210
- blockId,
211
- userType
212
- });
213
- }
214
- }
215
-
216
- // --- START FIX: RE-IMPLEMENT BATCHING LOGIC ---
88
+ for (let i = 0; i < cidsArray.length; i += dispatchBatchSize) {const batchCids = cidsArray.slice(i, i + dispatchBatchSize).map(cid => parseInt(cid));
89
+ if (batchCids.length > 0) {
90
+ const blockId = Math.floor(batchCids[0] / 1000000) * 1000000;
91
+ tasks.push({type: 'discover',cids: batchCids,blockId, userType});}}
217
92
  const topic = pubsub.topic(topicName);
218
93
  let totalCidsPublished = 0;
219
94
  let messagesPublished = 0;
220
-
221
- // 'tasks' is an array of 'discover' task objects. We group them into
222
- // batches and publish each batch as a single message.
223
- // We use `dispatchBatchSize` here to determine how many 'discover' tasks go into one message.
224
- // This value should probably be small, like 1-10.
225
-
226
- // Let's re-use `dispatchBatchSize` as the number of *tasks* per message.
227
- // If `tasks.length` is 200 and `dispatchBatchSize` is 50, this will create 4 messages.
228
- // This seems correct.
229
-
230
95
  for (let i = 0; i < tasks.length; i += dispatchBatchSize) {
231
- // This batch contains multiple 'discover' tasks
232
96
  const batchOfTasks = tasks.slice(i, i + dispatchBatchSize);
233
-
234
- // Wrap this batch in the new payload format
235
97
  const messagePayload = { tasks: batchOfTasks };
236
-
237
- try {
238
- await topic.publishMessage({ json: messagePayload });
239
-
240
- // Log the number of CIDs in this message
241
- const cidsInThisMessage = batchOfTasks.reduce((acc, task) => acc + task.cids.length, 0);
242
- totalCidsPublished += cidsInThisMessage;
243
- messagesPublished++;
244
-
245
- logger.log('INFO', `[Orchestrator Helpers] Dispatched batch ${messagesPublished} with ${batchOfTasks.length} discover tasks (${cidsInThisMessage} CIDs) as 1 Pub/Sub message.`);
246
- } catch (publishError) {
247
- logger.log('ERROR', `[Orchestrator Helpers] Failed to publish discover batch ${messagesPublished + 1}.`, { error: publishError.message });
248
- }
249
- }
250
-
251
- logger.log('SUCCESS', `[Orchestrator Helpers] Dispatched ${totalCidsPublished} CIDs in ${tasks.length} tasks, grouped into ${messagesPublished} Pub/Sub messages for ${userType} discovery.`);
252
- // --- END FIX ---
98
+ try {await topic.publishMessage({ json: messagePayload });
99
+ const cidsInThisMessage = batchOfTasks.reduce((acc, task) => acc + task.cids.length, 0);
100
+ totalCidsPublished += cidsInThisMessage;
101
+ messagesPublished++;
102
+ logger.log('INFO', `[Orchestrator Helpers] Dispatched batch ${messagesPublished} with ${batchOfTasks.length} discover tasks (${cidsInThisMessage} CIDs) as 1 Pub/Sub message.`);}
103
+ catch (publishError) {
104
+ logger.log('ERROR', `[Orchestrator Helpers] Failed to publish discover batch ${messagesPublished + 1}.`, { error: publishError.message });}}
105
+ logger.log('SUCCESS', `[Orchestrator Helpers] Dispatched ${totalCidsPublished} CIDs in ${tasks.length} tasks, grouped into ${messagesPublished} Pub/Sub messages for ${userType} discovery.`);
253
106
  }
254
107
 
255
-
256
108
  module.exports = {
257
109
  checkDiscoveryNeed,
258
110
  getDiscoveryCandidates,
@@ -15,29 +15,19 @@ async function getUpdateTargets(userType, thresholds, config, dependencies) {
15
15
  const { logger, firestoreUtils } = dependencies;
16
16
  logger.log('INFO', `[Orchestrator Helpers] Getting update targets for ${userType}...`);
17
17
  let targets = [];
18
-
19
18
  try {
20
- if (userType === 'normal') {
21
- // Pass dependencies and config to core util
22
- targets = await firestoreUtils.getNormalUsersToUpdate(dependencies, {
23
- dateThreshold: thresholds.dateThreshold,
24
- normalUserCollectionName: config.normalUserCollectionName,
25
- normalUserTimestampsDocId: config.normalUserTimestampsDocId
26
- });
27
- } else if (userType === 'speculator') {
28
- // Pass dependencies and config to core util
29
- targets = await firestoreUtils.getSpeculatorsToUpdate(dependencies, {
30
- dateThreshold: thresholds.dateThreshold,
31
- gracePeriodThreshold: thresholds.gracePeriodThreshold,
32
- speculatorBlocksCollectionName: config.speculatorBlocksCollectionName
33
- });
34
- }
35
- logger.log('SUCCESS', `[Orchestrator Helpers] Found ${targets.length} targets for ${userType} update.`);
36
- return targets;
37
- } catch (error) {
38
- logger.log('ERROR', `[Orchestrator Helpers] Error getting update targets for ${userType}`, { errorMessage: error.message });
39
- throw error;
40
- }
19
+ if (userType === 'normal') { targets = await firestoreUtils.getNormalUsersToUpdate(dependencies,{
20
+ dateThreshold: thresholds.dateThreshold,
21
+ normalUserCollectionName: config.normalUserCollectionName,
22
+ normalUserTimestampsDocId: config.normalUserTimestampsDocId
23
+ }); }
24
+ else if (userType === 'speculator') { targets = await firestoreUtils.getSpeculatorsToUpdate(dependencies,{
25
+ dateThreshold: thresholds.dateThreshold,
26
+ gracePeriodThreshold: thresholds.gracePeriodThreshold,
27
+ speculatorBlocksCollectionName: config.speculatorBlocksCollectionName}); }
28
+ logger.log('SUCCESS', `[Orchestrator Helpers] Found ${targets.length} targets for ${userType} update.`);
29
+ return targets;}
30
+ catch (error) { logger.log('ERROR', `[Orchestrator Helpers] Error getting update targets for ${userType}`, { errorMessage: error.message }); throw error; }
41
31
  }
42
32
 
43
33
  /**
@@ -51,42 +41,12 @@ async function getUpdateTargets(userType, thresholds, config, dependencies) {
51
41
  async function dispatchUpdates(targets, userType, config, dependencies) {
52
42
  const { logger, pubsubUtils } = dependencies;
53
43
  const { dispatcherTopicName, taskBatchSize, pubsubBatchSize } = config;
54
-
55
- if (targets.length === 0) {
56
- logger.log('INFO', `[Orchestrator Helpers] No ${userType} update targets to dispatch.`);
57
- return;
58
- }
59
- logger.log('INFO', `[Orchestrator Helpers] Dispatching ${targets.length} update tasks for ${userType} to ${dispatcherTopicName}...`);
60
-
61
- const individualTasks = targets.map(target => ({
62
- type: 'update',
63
- userId: userType === 'normal' ? target : target.userId,
64
- instrumentId: userType ==='speculator' ? target.instrumentId : undefined,
65
- userType,
66
- }));
67
-
68
- const dispatcherMessages = [];
69
- for (let i = 0; i < individualTasks.length; i += taskBatchSize) {
70
- const batch = individualTasks.slice(i, i + taskBatchSize);
71
- dispatcherMessages.push({ tasks: batch }); // Wrap the batch
72
- }
73
-
74
- try {
75
- // Pass dependencies and config to core util
76
- await pubsubUtils.batchPublishTasks(dependencies, {
77
- topicName: dispatcherTopicName,
78
- tasks: dispatcherMessages,
79
- taskType: `${userType} update batch`,
80
- maxPubsubBatchSize: pubsubBatchSize
81
- });
82
- logger.log('SUCCESS', `[Orchestrator Helpers] Published ${dispatcherMessages.length} messages (${individualTasks.length} tasks) for ${userType} updates to the dispatcher.`);
83
- } catch (error) {
84
- logger.log('ERROR', `[Orchestrator Helpers] Error dispatching update tasks for ${userType}`, { errorMessage: error.message });
85
- throw error;
86
- }
87
- }
88
-
89
- module.exports = {
90
- getUpdateTargets,
91
- dispatchUpdates,
92
- };
44
+ if (targets.length === 0) { logger.log('INFO', `[Orchestrator Helpers] No ${userType} update targets to dispatch.`); return; }
45
+ logger.log('INFO', `[Orchestrator Helpers] Dispatching ${targets.length} update tasks for ${userType} to ${dispatcherTopicName}...`);
46
+ const individualTasks = targets.map(target => ({ type: 'update', userId: userType === 'normal' ? target : target.userId, instrumentId: userType === 'speculator' ? target.instrumentId : undefined, userType }));
47
+ const dispatcherMessages = [];
48
+ for (let i = 0; i < individualTasks.length; i += taskBatchSize) { dispatcherMessages.push({ tasks: individualTasks.slice(i, i + taskBatchSize) }); }
49
+ try { await pubsubUtils.batchPublishTasks(dependencies,{ topicName: dispatcherTopicName, tasks: dispatcherMessages, taskType: `${userType} update batch`, maxPubsubBatchSize: pubsubBatchSize }); logger.log('SUCCESS', `[Orchestrator Helpers] Published ${dispatcherMessages.length} messages (${individualTasks.length} tasks) for ${userType} updates to the dispatcher.`); }
50
+ catch (error) { logger.log('ERROR', `[Orchestrator Helpers] Error dispatching update tasks for ${userType}`, { errorMessage: error.message }); throw error; }}
51
+
52
+ module.exports = { getUpdateTargets, dispatchUpdates };
@@ -4,145 +4,66 @@
4
4
  * that are called by the Cloud Function entry points.
5
5
  * They receive all dependencies.
6
6
  */
7
-
8
7
  const { checkDiscoveryNeed, getDiscoveryCandidates, dispatchDiscovery } = require('./helpers/discovery_helpers');
9
8
  const { getUpdateTargets, dispatchUpdates } = require('./helpers/update_helpers');
10
9
 
11
-
12
- /**
13
- * Main pipe for running the complete discovery orchestration process.
14
- * This is the function the Cloud Function entry point will call.
15
- * @param {object} config - The full orchestratorConfig object.
16
- * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
17
- * @returns {Promise<void>}
18
- */
19
- async function runDiscoveryOrchestrator(config, dependencies) {
20
- const { logger, firestoreUtils } = dependencies;
21
- logger.log('INFO', '🚀 Discovery Orchestrator triggered via module...');
22
-
23
- // Call sub-pipe from core
24
- await firestoreUtils.resetProxyLocks(dependencies, config);
25
-
26
- // Call sub-pipe for 'normal'
27
- // Pass the directly required sub-pipe functions implicitly via runDiscovery call context
28
- await runDiscovery('normal', config.discoveryConfig.normal, config, dependencies);
29
-
30
- // Call sub-pipe for 'speculator'
31
- await runDiscovery('speculator', config.discoveryConfig.speculator, config, dependencies);
10
+ /** Stage 1: Main discovery orchestrator pipe */
11
+ async function runDiscoveryOrchestrator(config, deps) {
12
+ const { logger, firestoreUtils } = deps;
13
+ logger.log('INFO', '🚀 Discovery Orchestrator triggered...');
14
+ await firestoreUtils.resetProxyLocks(deps, config);
15
+ await runDiscovery('normal', config.discoveryConfig.normal, config, deps);
16
+ await runDiscovery('speculator', config.discoveryConfig.speculator, config, deps);
32
17
  }
33
18
 
34
- /**
35
- * Main pipe for running the complete update orchestration process.
36
- * This is the function the Cloud Function entry point will call.
37
- * @param {object} config - The full orchestratorConfig object.
38
- * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
39
- * @returns {Promise<void>}
40
- */
41
- async function runUpdateOrchestrator(config, dependencies) {
42
- const { logger, firestoreUtils } = dependencies;
43
- logger.log('INFO', '🚀 Update Orchestrator triggered via module...');
44
-
45
- // Call sub-pipe from core
46
- await firestoreUtils.resetProxyLocks(dependencies, config);
47
-
48
- // Call sub-pipe for 'normal'
49
- await runUpdates('normal', config.updateConfig, config, dependencies);
50
-
51
- // Call sub-pipe for 'speculator'
52
- await runUpdates('speculator', config.updateConfig, config, dependencies);
19
+ /** Stage 2: Main update orchestrator pipe */
20
+ async function runUpdateOrchestrator(config, deps) {
21
+ const { logger, firestoreUtils } = deps;
22
+ logger.log('INFO', '🚀 Update Orchestrator triggered...');
23
+ await firestoreUtils.resetProxyLocks(deps, config);
24
+ await runUpdates('normal', config.updateConfig, config, deps);
25
+ await runUpdates('speculator', config.updateConfig, config, deps);
53
26
  }
54
27
 
55
- // --- Internal Helpers (which are also exposed as sub-pipes) ---
56
-
57
- /**
58
- * Runs the discovery process for a *single* user type.
59
- * This is an internal helper, but also maps to pipe.orchestrator.runDiscovery
60
- * @param {string} userType - 'normal' or 'speculator'.
61
- * @param {object} userTypeConfig - The specific config slice (e.g., config.discoveryConfig.normal).
62
- * @param {object} globalConfig - Global config.
63
- * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
64
- */
65
- async function runDiscovery(userType, userTypeConfig, globalConfig, dependencies) {
66
- const { logger } = dependencies;
67
- logger.log('INFO', `[Module Orchestrator] Starting discovery for ${userType} users...`);
68
-
69
- // --- 3. Use the directly required functions ---
70
- // 1. pipe.orchestrator.checkDiscoveryNeed -> becomes checkDiscoveryNeed
71
- const { needsDiscovery, blocksToFill } = await checkDiscoveryNeed(
72
- userType,
73
- userTypeConfig,
74
- dependencies
75
- );
28
+ /** Stage 3: Run discovery for a single user type */
29
+ async function runDiscovery(userType, userConfig, globalConfig, deps) {
30
+ const { logger } = deps;
31
+ logger.log('INFO', `[Orchestrator] Starting discovery for ${userType} users...`);
76
32
 
77
- if (!needsDiscovery) {
78
- logger.log('INFO', `[Module Orchestrator] No discovery needed for ${userType}.`);
79
- return;
80
- }
33
+ // Step 3.1: Check if discovery is needed
34
+ const { needsDiscovery, blocksToFill } = await checkDiscoveryNeed(userType, userConfig, deps);
35
+ if (!needsDiscovery) { logger.log('INFO', `[Orchestrator] No discovery needed for ${userType}.`); return; }
81
36
 
82
- // 2. pipe.orchestrator.getDiscoveryCandidates -> becomes getDiscoveryCandidates
83
- const candidates = await getDiscoveryCandidates(
84
- userType,
85
- blocksToFill,
86
- userTypeConfig,
87
- dependencies
88
- );
37
+ // Step 3.2: Get discovery candidates
38
+ const candidates = await getDiscoveryCandidates(userType, blocksToFill, userConfig, deps);
89
39
 
90
- // 3. pipe.orchestrator.dispatchDiscovery -> becomes dispatchDiscovery
91
- await dispatchDiscovery(
92
- userType,
93
- candidates,
94
- userTypeConfig,
95
- dependencies
96
- );
97
-
98
- logger.log('SUCCESS', `[Module Orchestrator] Dispatched discovery tasks for ${userType}.`);
40
+ // Step 3.3: Dispatch discovery tasks
41
+ await dispatchDiscovery(userType, candidates, userConfig, deps);
42
+ logger.log('SUCCESS', `[Orchestrator] Dispatched discovery tasks for ${userType}.`);
99
43
  }
100
44
 
101
- /**
102
- * Runs the update process for a *single* user type.
103
- * This is an internal helper, but also maps to pipe.orchestrator.runUpdates
104
- * @param {string} userType - 'normal' or 'speculator'.
105
- * @param {object} updateConfig - The config.updateConfig object.
106
- * @param {object} globalConfig - Global config.
107
- * @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
108
- */
109
- async function runUpdates(userType, updateConfig, globalConfig, dependencies) {
110
- const { logger } = dependencies;
111
- logger.log('INFO', `[Module Orchestrator] Collecting users for daily update (${userType})...`);
45
+ /** Stage 4: Run updates for a single user type */
46
+ async function runUpdates(userType, updateConfig, globalConfig, deps) {
47
+ const { logger } = deps;
48
+ logger.log('INFO', `[Orchestrator] Collecting users for daily update (${userType})...`);
112
49
 
50
+ // Step 4.1: Compute thresholds
113
51
  const now = new Date();
114
52
  const startOfTodayUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
115
53
  const DaysAgoUTC = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
54
+ const thresholds = { dateThreshold: startOfTodayUTC, gracePeriodThreshold: DaysAgoUTC };
116
55
 
117
- const thresholds = {
118
- dateThreshold: startOfTodayUTC,
119
- gracePeriodThreshold: DaysAgoUTC
120
- };
121
-
122
- // --- 4. Use the directly required functions ---
123
- // 1. pipe.orchestrator.getUpdateTargets -> becomes getUpdateTargets
124
- const targets = await getUpdateTargets(
125
- userType,
126
- thresholds,
127
- updateConfig,
128
- dependencies
129
- );
130
-
131
- // 2. pipe.orchestrator.dispatchUpdates -> becomes dispatchUpdates
132
- await dispatchUpdates(
133
- targets,
134
- userType,
135
- updateConfig,
136
- dependencies
137
- );
56
+ // Step 4.2: Get update targets
57
+ const targets = await getUpdateTargets(userType, thresholds, updateConfig, deps);
138
58
 
139
- logger.log('SUCCESS', `[Module Orchestrator] Dispatched update tasks for ${userType}.`);
59
+ // Step 4.3: Dispatch update tasks
60
+ await dispatchUpdates(targets, userType, updateConfig, deps);
61
+ logger.log('SUCCESS', `[Orchestrator] Dispatched update tasks for ${userType}.`);
140
62
  }
141
63
 
142
64
  module.exports = {
143
- runDiscoveryOrchestrator,
144
- runUpdateOrchestrator,
145
- // Exporting these internal helpers so the main index.js can map them
146
- runDiscovery,
147
- runUpdates
148
- };
65
+ runDiscoveryOrchestrator,
66
+ runUpdateOrchestrator,
67
+ runDiscovery,
68
+ runUpdates
69
+ };