bulltrackers-module 1.0.105 → 1.0.106
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/README.MD +222 -222
- package/functions/appscript-api/helpers/errors.js +19 -19
- package/functions/appscript-api/index.js +58 -58
- package/functions/computation-system/helpers/orchestration_helpers.js +647 -113
- package/functions/computation-system/utils/data_loader.js +191 -191
- package/functions/computation-system/utils/utils.js +149 -254
- package/functions/core/utils/firestore_utils.js +433 -433
- package/functions/core/utils/pubsub_utils.js +53 -53
- package/functions/dispatcher/helpers/dispatch_helpers.js +47 -47
- package/functions/dispatcher/index.js +52 -52
- package/functions/etoro-price-fetcher/helpers/handler_helpers.js +124 -124
- package/functions/fetch-insights/helpers/handler_helpers.js +91 -91
- package/functions/generic-api/helpers/api_helpers.js +379 -379
- package/functions/generic-api/index.js +150 -150
- package/functions/invalid-speculator-handler/helpers/handler_helpers.js +75 -75
- package/functions/orchestrator/helpers/discovery_helpers.js +226 -226
- package/functions/orchestrator/helpers/update_helpers.js +92 -92
- package/functions/orchestrator/index.js +147 -147
- package/functions/price-backfill/helpers/handler_helpers.js +116 -123
- package/functions/social-orchestrator/helpers/orchestrator_helpers.js +61 -61
- package/functions/social-task-handler/helpers/handler_helpers.js +288 -288
- package/functions/task-engine/handler_creator.js +78 -78
- package/functions/task-engine/helpers/discover_helpers.js +125 -125
- package/functions/task-engine/helpers/update_helpers.js +118 -118
- package/functions/task-engine/helpers/verify_helpers.js +162 -162
- package/functions/task-engine/utils/firestore_batch_manager.js +258 -258
- package/index.js +105 -113
- package/package.json +45 -45
- package/functions/computation-system/computation_dependencies.json +0 -120
- package/functions/computation-system/helpers/worker_helpers.js +0 -340
- package/functions/computation-system/utils/computation_state_manager.js +0 -178
- package/functions/computation-system/utils/dependency_graph.js +0 -191
- package/functions/speculator-cleanup-orchestrator/helpers/cleanup_helpers.js +0 -160
|
@@ -1,226 +1,226 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Orchestrator's discovery sub-pipes.
|
|
3
|
-
* REFACTORED: All functions are now stateless and receive dependencies.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Sub-pipe: pipe.orchestrator.checkDiscoveryNeed
|
|
8
|
-
* @param {string} userType - 'normal' or 'speculator'.
|
|
9
|
-
* @param {object} config - The user type specific config (e.g., config.discoveryConfig.normal).
|
|
10
|
-
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
|
|
11
|
-
* @returns {Promise<{needsDiscovery: boolean, blocksToFill: Array<object>}>}
|
|
12
|
-
*/
|
|
13
|
-
async function checkDiscoveryNeed(userType, config, dependencies) {
|
|
14
|
-
const { logger, firestoreUtils } = dependencies;
|
|
15
|
-
logger.log('INFO', `[Orchestrator Helpers] Checking discovery need for ${userType}...`);
|
|
16
|
-
|
|
17
|
-
const {
|
|
18
|
-
targetUsersPerBlock,
|
|
19
|
-
speculatorBlockCountsDocPath,
|
|
20
|
-
normalBlockCountsDocPath,
|
|
21
|
-
speculatorInstruments,
|
|
22
|
-
allHighValueBlocks
|
|
23
|
-
} = config;
|
|
24
|
-
|
|
25
|
-
const isSpeculator = userType === 'speculator';
|
|
26
|
-
|
|
27
|
-
// Pass dependencies and config to core util
|
|
28
|
-
const blockCounts = await firestoreUtils.getBlockCapacities(
|
|
29
|
-
dependencies,
|
|
30
|
-
{ speculatorBlockCountsDocPath, normalBlockCountsDocPath },
|
|
31
|
-
userType
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
let underPopulatedBlocks = [];
|
|
35
|
-
if (isSpeculator) {
|
|
36
|
-
for (const instrument of speculatorInstruments) {
|
|
37
|
-
for (const block of allHighValueBlocks) {
|
|
38
|
-
const instrumentBlockKey = `${instrument}_${block.startId}`;
|
|
39
|
-
if ((blockCounts[instrumentBlockKey] || 0) < targetUsersPerBlock) {
|
|
40
|
-
underPopulatedBlocks.push({ ...block, instrument });
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
underPopulatedBlocks = allHighValueBlocks.filter(
|
|
46
|
-
block => (blockCounts[block.startId] || 0) < targetUsersPerBlock
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const needsDiscovery = underPopulatedBlocks.length > 0;
|
|
51
|
-
if (!needsDiscovery) {
|
|
52
|
-
logger.log('SUCCESS', `✅ All blocks are at target capacity for ${userType} users.`);
|
|
53
|
-
} else {
|
|
54
|
-
logger.log('INFO', `Found ${underPopulatedBlocks.length} underpopulated blocks/instruments for ${userType}.`);
|
|
55
|
-
}
|
|
56
|
-
return { needsDiscovery, blocksToFill: underPopulatedBlocks };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Sub-pipe: pipe.orchestrator.getDiscoveryCandidates
|
|
61
|
-
* @param {string} userType - 'normal' or 'speculator'.
|
|
62
|
-
* @param {Array<object>} blocksToFill - From checkDiscoveryNeed.
|
|
63
|
-
* @param {object} config - The user type specific config.
|
|
64
|
-
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
|
|
65
|
-
* @returns {Promise<Set<string>>} Set of candidate CIDs.
|
|
66
|
-
*/
|
|
67
|
-
async function getDiscoveryCandidates(userType, blocksToFill, config, dependencies) {
|
|
68
|
-
const { logger, firestoreUtils } = dependencies;
|
|
69
|
-
logger.log('INFO', `[Orchestrator Helpers] Getting discovery candidates for ${userType}...`);
|
|
70
|
-
|
|
71
|
-
const {
|
|
72
|
-
maxTasksPerRun,
|
|
73
|
-
discoveryBatchSize,
|
|
74
|
-
maxRandomCidsToDiscover,
|
|
75
|
-
specBlocksCollection,
|
|
76
|
-
pendingSpecCollection,
|
|
77
|
-
normalUserCollection,
|
|
78
|
-
invalidSpecCollection,
|
|
79
|
-
speculatorInstrumentSet,
|
|
80
|
-
snapshotsSubCollectionName,
|
|
81
|
-
partsSubCollectionName
|
|
82
|
-
} = config;
|
|
83
|
-
|
|
84
|
-
const isSpeculator = userType === 'speculator';
|
|
85
|
-
const dispatchedCids = new Set();
|
|
86
|
-
|
|
87
|
-
// Pass dependencies and config to core util
|
|
88
|
-
const latestNormalPortfolios = await firestoreUtils.getLatestNormalUserPortfolios(
|
|
89
|
-
dependencies,
|
|
90
|
-
{ normalUserCollectionName: normalUserCollection, snapshotsSubCollectionName, partsSubCollectionName }
|
|
91
|
-
);
|
|
92
|
-
const existingNormalUserIds = new Set(Object.keys(latestNormalPortfolios));
|
|
93
|
-
|
|
94
|
-
// Pass dependencies and config to core util
|
|
95
|
-
const exclusionIds = await firestoreUtils.getExclusionIds(dependencies, {
|
|
96
|
-
specBlocksCollection,
|
|
97
|
-
pendingSpecCollection,
|
|
98
|
-
normalUserCollection,
|
|
99
|
-
invalidSpecCollection,
|
|
100
|
-
existingNormalUserIds
|
|
101
|
-
}, userType);
|
|
102
|
-
|
|
103
|
-
if (isSpeculator) {
|
|
104
|
-
// Pass dependencies to core util
|
|
105
|
-
const prioritizedCandidates = await firestoreUtils.getPrioritizedSpeculators(
|
|
106
|
-
dependencies,
|
|
107
|
-
exclusionIds,
|
|
108
|
-
speculatorInstrumentSet,
|
|
109
|
-
latestNormalPortfolios
|
|
110
|
-
);
|
|
111
|
-
logger.log('INFO', `Found ${prioritizedCandidates.length} potential new speculators from existing user pool.`);
|
|
112
|
-
|
|
113
|
-
prioritizedCandidates.forEach(cidStr => {
|
|
114
|
-
if (dispatchedCids.size < maxTasksPerRun) {
|
|
115
|
-
dispatchedCids.add(cidStr);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
logger.log('INFO', `Added ${dispatchedCids.size} prioritized speculators.`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// --- Random Generation ---
|
|
122
|
-
let dispatchedRandomCidCount = 0;
|
|
123
|
-
const initialDispatchedCount = dispatchedCids.size;
|
|
124
|
-
|
|
125
|
-
while ((dispatchedRandomCidCount + initialDispatchedCount) < maxRandomCidsToDiscover &&
|
|
126
|
-
dispatchedCids.size < maxTasksPerRun && blocksToFill.length > 0)
|
|
127
|
-
{
|
|
128
|
-
const blockIndex = dispatchedCids.size % blocksToFill.length;
|
|
129
|
-
const block = blocksToFill[blockIndex];
|
|
130
|
-
if (!block) break;
|
|
131
|
-
|
|
132
|
-
for (let j = 0; j < discoveryBatchSize; j++) {
|
|
133
|
-
if ((dispatchedRandomCidCount + initialDispatchedCount) >= maxRandomCidsToDiscover || dispatchedCids.size >= maxTasksPerRun) break;
|
|
134
|
-
|
|
135
|
-
let randomId;
|
|
136
|
-
let retryCount = 0;
|
|
137
|
-
const MAX_RETRIES = 50;
|
|
138
|
-
do {
|
|
139
|
-
if (++retryCount > MAX_RETRIES) break;
|
|
140
|
-
randomId = String(Math.floor(Math.random() * 1000000) + block.startId);
|
|
141
|
-
} while (
|
|
142
|
-
exclusionIds.has(randomId) ||
|
|
143
|
-
dispatchedCids.has(randomId)
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
if (retryCount <= MAX_RETRIES) {
|
|
147
|
-
dispatchedCids.add(randomId);
|
|
148
|
-
dispatchedRandomCidCount++;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
logger.log('INFO', `Generated ${dispatchedRandomCidCount} random CIDs for ${userType} discovery.`);
|
|
154
|
-
logger.log('INFO', `Total candidates for dispatch: ${dispatchedCids.size}`);
|
|
155
|
-
return dispatchedCids;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Sub-pipe: pipe.orchestrator.dispatchDiscovery
|
|
160
|
-
* @param {string} userType - 'normal' or 'speculator'.
|
|
161
|
-
* @param {Set<string>} candidates - Set of candidate CIDs.
|
|
162
|
-
* @param {object} config - The user type specific config.
|
|
163
|
-
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
|
|
164
|
-
* @returns {Promise<void>}
|
|
165
|
-
*/
|
|
166
|
-
async function dispatchDiscovery(userType, candidates, config, dependencies) {
|
|
167
|
-
const { logger, firestoreUtils, pubsubUtils } = dependencies;
|
|
168
|
-
const {
|
|
169
|
-
topicName,
|
|
170
|
-
dispatchBatchSize,
|
|
171
|
-
pubsubBatchSize,
|
|
172
|
-
pendingSpecCollection,
|
|
173
|
-
pendingMaxFieldsPerDoc,
|
|
174
|
-
pendingMaxWritesPerBatch
|
|
175
|
-
} = config;
|
|
176
|
-
|
|
177
|
-
if (candidates.size === 0) {
|
|
178
|
-
logger.log('INFO', `[Orchestrator Helpers] No ${userType} candidates to dispatch.`);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
logger.log('INFO', `[Orchestrator Helpers] Dispatching ${candidates.size} discovery tasks for ${userType}...`);
|
|
182
|
-
|
|
183
|
-
const isSpeculator = userType === 'speculator';
|
|
184
|
-
const cidsArray = Array.from(candidates);
|
|
185
|
-
|
|
186
|
-
if (isSpeculator) {
|
|
187
|
-
await firestoreUtils.clearCollection(dependencies, pendingSpecCollection);
|
|
188
|
-
await firestoreUtils.batchWriteShardedIds(dependencies, {
|
|
189
|
-
collectionPath: pendingSpecCollection,
|
|
190
|
-
items: cidsArray,
|
|
191
|
-
timestamp: new Date(),
|
|
192
|
-
maxFieldsPerDoc: pendingMaxFieldsPerDoc,
|
|
193
|
-
maxWritesPerBatch: pendingMaxWritesPerBatch
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const tasks = [];
|
|
198
|
-
for (let i = 0; i < cidsArray.length; i += dispatchBatchSize) {
|
|
199
|
-
const batchCids = cidsArray.slice(i, i + dispatchBatchSize).map(cid => parseInt(cid));
|
|
200
|
-
if (batchCids.length > 0) {
|
|
201
|
-
const blockId = Math.floor(batchCids[0] / 1000000) * 1000000;
|
|
202
|
-
tasks.push({
|
|
203
|
-
type: 'discover',
|
|
204
|
-
cids: batchCids,
|
|
205
|
-
blockId,
|
|
206
|
-
userType
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Pass dependencies and config to core util
|
|
212
|
-
await pubsubUtils.batchPublishTasks(dependencies, {
|
|
213
|
-
topicName,
|
|
214
|
-
tasks,
|
|
215
|
-
taskType: `${userType} discovery`,
|
|
216
|
-
maxPubsubBatchSize: pubsubBatchSize
|
|
217
|
-
});
|
|
218
|
-
logger.log('SUCCESS', `[Orchestrator Helpers] Dispatched ${tasks.length} task messages (${candidates.size} CIDs) for ${userType} discovery.`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
module.exports = {
|
|
223
|
-
checkDiscoveryNeed,
|
|
224
|
-
getDiscoveryCandidates,
|
|
225
|
-
dispatchDiscovery,
|
|
226
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Orchestrator's discovery sub-pipes.
|
|
3
|
+
* REFACTORED: All functions are now stateless and receive dependencies.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sub-pipe: pipe.orchestrator.checkDiscoveryNeed
|
|
8
|
+
* @param {string} userType - 'normal' or 'speculator'.
|
|
9
|
+
* @param {object} config - The user type specific config (e.g., config.discoveryConfig.normal).
|
|
10
|
+
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
|
|
11
|
+
* @returns {Promise<{needsDiscovery: boolean, blocksToFill: Array<object>}>}
|
|
12
|
+
*/
|
|
13
|
+
async function checkDiscoveryNeed(userType, config, dependencies) {
|
|
14
|
+
const { logger, firestoreUtils } = dependencies;
|
|
15
|
+
logger.log('INFO', `[Orchestrator Helpers] Checking discovery need for ${userType}...`);
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
targetUsersPerBlock,
|
|
19
|
+
speculatorBlockCountsDocPath,
|
|
20
|
+
normalBlockCountsDocPath,
|
|
21
|
+
speculatorInstruments,
|
|
22
|
+
allHighValueBlocks
|
|
23
|
+
} = config;
|
|
24
|
+
|
|
25
|
+
const isSpeculator = userType === 'speculator';
|
|
26
|
+
|
|
27
|
+
// Pass dependencies and config to core util
|
|
28
|
+
const blockCounts = await firestoreUtils.getBlockCapacities(
|
|
29
|
+
dependencies,
|
|
30
|
+
{ speculatorBlockCountsDocPath, normalBlockCountsDocPath },
|
|
31
|
+
userType
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
let underPopulatedBlocks = [];
|
|
35
|
+
if (isSpeculator) {
|
|
36
|
+
for (const instrument of speculatorInstruments) {
|
|
37
|
+
for (const block of allHighValueBlocks) {
|
|
38
|
+
const instrumentBlockKey = `${instrument}_${block.startId}`;
|
|
39
|
+
if ((blockCounts[instrumentBlockKey] || 0) < targetUsersPerBlock) {
|
|
40
|
+
underPopulatedBlocks.push({ ...block, instrument });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
underPopulatedBlocks = allHighValueBlocks.filter(
|
|
46
|
+
block => (blockCounts[block.startId] || 0) < targetUsersPerBlock
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const needsDiscovery = underPopulatedBlocks.length > 0;
|
|
51
|
+
if (!needsDiscovery) {
|
|
52
|
+
logger.log('SUCCESS', `✅ All blocks are at target capacity for ${userType} users.`);
|
|
53
|
+
} else {
|
|
54
|
+
logger.log('INFO', `Found ${underPopulatedBlocks.length} underpopulated blocks/instruments for ${userType}.`);
|
|
55
|
+
}
|
|
56
|
+
return { needsDiscovery, blocksToFill: underPopulatedBlocks };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Sub-pipe: pipe.orchestrator.getDiscoveryCandidates
|
|
61
|
+
* @param {string} userType - 'normal' or 'speculator'.
|
|
62
|
+
* @param {Array<object>} blocksToFill - From checkDiscoveryNeed.
|
|
63
|
+
* @param {object} config - The user type specific config.
|
|
64
|
+
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
|
|
65
|
+
* @returns {Promise<Set<string>>} Set of candidate CIDs.
|
|
66
|
+
*/
|
|
67
|
+
async function getDiscoveryCandidates(userType, blocksToFill, config, dependencies) {
|
|
68
|
+
const { logger, firestoreUtils } = dependencies;
|
|
69
|
+
logger.log('INFO', `[Orchestrator Helpers] Getting discovery candidates for ${userType}...`);
|
|
70
|
+
|
|
71
|
+
const {
|
|
72
|
+
maxTasksPerRun,
|
|
73
|
+
discoveryBatchSize,
|
|
74
|
+
maxRandomCidsToDiscover,
|
|
75
|
+
specBlocksCollection,
|
|
76
|
+
pendingSpecCollection,
|
|
77
|
+
normalUserCollection,
|
|
78
|
+
invalidSpecCollection,
|
|
79
|
+
speculatorInstrumentSet,
|
|
80
|
+
snapshotsSubCollectionName,
|
|
81
|
+
partsSubCollectionName
|
|
82
|
+
} = config;
|
|
83
|
+
|
|
84
|
+
const isSpeculator = userType === 'speculator';
|
|
85
|
+
const dispatchedCids = new Set();
|
|
86
|
+
|
|
87
|
+
// Pass dependencies and config to core util
|
|
88
|
+
const latestNormalPortfolios = await firestoreUtils.getLatestNormalUserPortfolios(
|
|
89
|
+
dependencies,
|
|
90
|
+
{ normalUserCollectionName: normalUserCollection, snapshotsSubCollectionName, partsSubCollectionName }
|
|
91
|
+
);
|
|
92
|
+
const existingNormalUserIds = new Set(Object.keys(latestNormalPortfolios));
|
|
93
|
+
|
|
94
|
+
// Pass dependencies and config to core util
|
|
95
|
+
const exclusionIds = await firestoreUtils.getExclusionIds(dependencies, {
|
|
96
|
+
specBlocksCollection,
|
|
97
|
+
pendingSpecCollection,
|
|
98
|
+
normalUserCollection,
|
|
99
|
+
invalidSpecCollection,
|
|
100
|
+
existingNormalUserIds
|
|
101
|
+
}, userType);
|
|
102
|
+
|
|
103
|
+
if (isSpeculator) {
|
|
104
|
+
// Pass dependencies to core util
|
|
105
|
+
const prioritizedCandidates = await firestoreUtils.getPrioritizedSpeculators(
|
|
106
|
+
dependencies,
|
|
107
|
+
exclusionIds,
|
|
108
|
+
speculatorInstrumentSet,
|
|
109
|
+
latestNormalPortfolios
|
|
110
|
+
);
|
|
111
|
+
logger.log('INFO', `Found ${prioritizedCandidates.length} potential new speculators from existing user pool.`);
|
|
112
|
+
|
|
113
|
+
prioritizedCandidates.forEach(cidStr => {
|
|
114
|
+
if (dispatchedCids.size < maxTasksPerRun) {
|
|
115
|
+
dispatchedCids.add(cidStr);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
logger.log('INFO', `Added ${dispatchedCids.size} prioritized speculators.`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// --- Random Generation ---
|
|
122
|
+
let dispatchedRandomCidCount = 0;
|
|
123
|
+
const initialDispatchedCount = dispatchedCids.size;
|
|
124
|
+
|
|
125
|
+
while ((dispatchedRandomCidCount + initialDispatchedCount) < maxRandomCidsToDiscover &&
|
|
126
|
+
dispatchedCids.size < maxTasksPerRun && blocksToFill.length > 0)
|
|
127
|
+
{
|
|
128
|
+
const blockIndex = dispatchedCids.size % blocksToFill.length;
|
|
129
|
+
const block = blocksToFill[blockIndex];
|
|
130
|
+
if (!block) break;
|
|
131
|
+
|
|
132
|
+
for (let j = 0; j < discoveryBatchSize; j++) {
|
|
133
|
+
if ((dispatchedRandomCidCount + initialDispatchedCount) >= maxRandomCidsToDiscover || dispatchedCids.size >= maxTasksPerRun) break;
|
|
134
|
+
|
|
135
|
+
let randomId;
|
|
136
|
+
let retryCount = 0;
|
|
137
|
+
const MAX_RETRIES = 50;
|
|
138
|
+
do {
|
|
139
|
+
if (++retryCount > MAX_RETRIES) break;
|
|
140
|
+
randomId = String(Math.floor(Math.random() * 1000000) + block.startId);
|
|
141
|
+
} while (
|
|
142
|
+
exclusionIds.has(randomId) ||
|
|
143
|
+
dispatchedCids.has(randomId)
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
if (retryCount <= MAX_RETRIES) {
|
|
147
|
+
dispatchedCids.add(randomId);
|
|
148
|
+
dispatchedRandomCidCount++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
logger.log('INFO', `Generated ${dispatchedRandomCidCount} random CIDs for ${userType} discovery.`);
|
|
154
|
+
logger.log('INFO', `Total candidates for dispatch: ${dispatchedCids.size}`);
|
|
155
|
+
return dispatchedCids;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Sub-pipe: pipe.orchestrator.dispatchDiscovery
|
|
160
|
+
* @param {string} userType - 'normal' or 'speculator'.
|
|
161
|
+
* @param {Set<string>} candidates - Set of candidate CIDs.
|
|
162
|
+
* @param {object} config - The user type specific config.
|
|
163
|
+
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils, pubsubUtils.
|
|
164
|
+
* @returns {Promise<void>}
|
|
165
|
+
*/
|
|
166
|
+
async function dispatchDiscovery(userType, candidates, config, dependencies) {
|
|
167
|
+
const { logger, firestoreUtils, pubsubUtils } = dependencies;
|
|
168
|
+
const {
|
|
169
|
+
topicName,
|
|
170
|
+
dispatchBatchSize,
|
|
171
|
+
pubsubBatchSize,
|
|
172
|
+
pendingSpecCollection,
|
|
173
|
+
pendingMaxFieldsPerDoc,
|
|
174
|
+
pendingMaxWritesPerBatch
|
|
175
|
+
} = config;
|
|
176
|
+
|
|
177
|
+
if (candidates.size === 0) {
|
|
178
|
+
logger.log('INFO', `[Orchestrator Helpers] No ${userType} candidates to dispatch.`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
logger.log('INFO', `[Orchestrator Helpers] Dispatching ${candidates.size} discovery tasks for ${userType}...`);
|
|
182
|
+
|
|
183
|
+
const isSpeculator = userType === 'speculator';
|
|
184
|
+
const cidsArray = Array.from(candidates);
|
|
185
|
+
|
|
186
|
+
if (isSpeculator) {
|
|
187
|
+
await firestoreUtils.clearCollection(dependencies, pendingSpecCollection);
|
|
188
|
+
await firestoreUtils.batchWriteShardedIds(dependencies, {
|
|
189
|
+
collectionPath: pendingSpecCollection,
|
|
190
|
+
items: cidsArray,
|
|
191
|
+
timestamp: new Date(),
|
|
192
|
+
maxFieldsPerDoc: pendingMaxFieldsPerDoc,
|
|
193
|
+
maxWritesPerBatch: pendingMaxWritesPerBatch
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const tasks = [];
|
|
198
|
+
for (let i = 0; i < cidsArray.length; i += dispatchBatchSize) {
|
|
199
|
+
const batchCids = cidsArray.slice(i, i + dispatchBatchSize).map(cid => parseInt(cid));
|
|
200
|
+
if (batchCids.length > 0) {
|
|
201
|
+
const blockId = Math.floor(batchCids[0] / 1000000) * 1000000;
|
|
202
|
+
tasks.push({
|
|
203
|
+
type: 'discover',
|
|
204
|
+
cids: batchCids,
|
|
205
|
+
blockId,
|
|
206
|
+
userType
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Pass dependencies and config to core util
|
|
212
|
+
await pubsubUtils.batchPublishTasks(dependencies, {
|
|
213
|
+
topicName,
|
|
214
|
+
tasks,
|
|
215
|
+
taskType: `${userType} discovery`,
|
|
216
|
+
maxPubsubBatchSize: pubsubBatchSize
|
|
217
|
+
});
|
|
218
|
+
logger.log('SUCCESS', `[Orchestrator Helpers] Dispatched ${tasks.length} task messages (${candidates.size} CIDs) for ${userType} discovery.`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
module.exports = {
|
|
223
|
+
checkDiscoveryNeed,
|
|
224
|
+
getDiscoveryCandidates,
|
|
225
|
+
dispatchDiscovery,
|
|
226
|
+
};
|
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Orchestrator's update sub-pipes.
|
|
3
|
-
* REFACTORED: All functions are now stateless and receive dependencies.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Sub-pipe: pipe.orchestrator.getUpdateTargets
|
|
8
|
-
* @param {string} userType - 'normal' or 'speculator'.
|
|
9
|
-
* @param {object} thresholds - Contains date thresholds.
|
|
10
|
-
* @param {object} config - The *global* updateConfig object.
|
|
11
|
-
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
|
|
12
|
-
* @returns {Promise<Array<any>>} Array of targets.
|
|
13
|
-
*/
|
|
14
|
-
async function getUpdateTargets(userType, thresholds, config, dependencies) {
|
|
15
|
-
const { logger, firestoreUtils } = dependencies;
|
|
16
|
-
logger.log('INFO', `[Orchestrator Helpers] Getting update targets for ${userType}...`);
|
|
17
|
-
let targets = [];
|
|
18
|
-
|
|
19
|
-
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
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Sub-pipe: pipe.orchestrator.dispatchUpdates
|
|
45
|
-
* @param {Array<any>} targets - Array of targets from getUpdateTargets.
|
|
46
|
-
* @param {string} userType - 'normal' or 'speculator'.
|
|
47
|
-
* @param {object} config - The *global* updateConfig object.
|
|
48
|
-
* @param {object} dependencies - Contains db, pubsub, logger, pubsubUtils.
|
|
49
|
-
* @returns {Promise<void>}
|
|
50
|
-
*/
|
|
51
|
-
async function dispatchUpdates(targets, userType, config, dependencies) {
|
|
52
|
-
const { logger, pubsubUtils } = dependencies;
|
|
53
|
-
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
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Orchestrator's update sub-pipes.
|
|
3
|
+
* REFACTORED: All functions are now stateless and receive dependencies.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sub-pipe: pipe.orchestrator.getUpdateTargets
|
|
8
|
+
* @param {string} userType - 'normal' or 'speculator'.
|
|
9
|
+
* @param {object} thresholds - Contains date thresholds.
|
|
10
|
+
* @param {object} config - The *global* updateConfig object.
|
|
11
|
+
* @param {object} dependencies - Contains db, pubsub, logger, firestoreUtils.
|
|
12
|
+
* @returns {Promise<Array<any>>} Array of targets.
|
|
13
|
+
*/
|
|
14
|
+
async function getUpdateTargets(userType, thresholds, config, dependencies) {
|
|
15
|
+
const { logger, firestoreUtils } = dependencies;
|
|
16
|
+
logger.log('INFO', `[Orchestrator Helpers] Getting update targets for ${userType}...`);
|
|
17
|
+
let targets = [];
|
|
18
|
+
|
|
19
|
+
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
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Sub-pipe: pipe.orchestrator.dispatchUpdates
|
|
45
|
+
* @param {Array<any>} targets - Array of targets from getUpdateTargets.
|
|
46
|
+
* @param {string} userType - 'normal' or 'speculator'.
|
|
47
|
+
* @param {object} config - The *global* updateConfig object.
|
|
48
|
+
* @param {object} dependencies - Contains db, pubsub, logger, pubsubUtils.
|
|
49
|
+
* @returns {Promise<void>}
|
|
50
|
+
*/
|
|
51
|
+
async function dispatchUpdates(targets, userType, config, dependencies) {
|
|
52
|
+
const { logger, pubsubUtils } = dependencies;
|
|
53
|
+
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
|
+
};
|