bulltrackers-module 1.0.127 → 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.
- package/functions/computation-system/helpers/computation_pass_runner.js +20 -773
- package/functions/computation-system/helpers/orchestration_helpers.js +88 -867
- package/functions/computation-system/utils/data_loader.js +58 -147
- package/functions/computation-system/utils/utils.js +55 -98
- package/functions/orchestrator/helpers/discovery_helpers.js +40 -188
- package/functions/orchestrator/helpers/update_helpers.js +21 -61
- package/functions/orchestrator/index.js +42 -121
- package/functions/task-engine/handler_creator.js +22 -143
- package/functions/task-engine/helpers/discover_helpers.js +20 -90
- package/functions/task-engine/helpers/update_helpers.js +90 -185
- package/functions/task-engine/helpers/verify_helpers.js +43 -159
- package/functions/task-engine/utils/firestore_batch_manager.js +97 -290
- package/functions/task-engine/utils/task_engine_utils.js +99 -0
- package/package.json +1 -1
- package/functions/task-engine/utils/api_calls.js +0 -0
- package/functions/task-engine/utils/firestore_ops.js +0 -0
|
@@ -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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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;
|
|
173
|
-
const {
|
|
174
|
-
|
|
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
|
-
|
|
192
|
-
await firestoreUtils.
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
//
|
|
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.
|
|
91
|
-
await dispatchDiscovery(
|
|
92
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
};
|
|
65
|
+
runDiscoveryOrchestrator,
|
|
66
|
+
runUpdateOrchestrator,
|
|
67
|
+
runDiscovery,
|
|
68
|
+
runUpdates
|
|
69
|
+
};
|