bulltrackers-module 1.0.207 → 1.0.208
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.
|
@@ -12,18 +12,30 @@
|
|
|
12
12
|
*/
|
|
13
13
|
async function checkDiscoveryNeed(userType, config, dependencies) {
|
|
14
14
|
const { logger, firestoreUtils } = dependencies;
|
|
15
|
+
const { targetUsersPerBlock, normalBlockCountsDocPath, allHighValueBlocks } = config;
|
|
16
|
+
|
|
17
|
+
// --- REFACTOR: ORGANIC SPECULATOR DISCOVERY ---
|
|
18
|
+
// We no longer "hunt" for speculators using block targets.
|
|
19
|
+
// Speculators are now discovered organically via the Normal User Update Loop (Feedback Loop).
|
|
20
|
+
if (userType === 'speculator') {
|
|
21
|
+
logger.log('INFO', '[Orchestrator Helpers] Speculator discovery is Organic/Feedback-Driven. Skipping active discovery checks.');
|
|
22
|
+
return { needsDiscovery: false, blocksToFill: [] };
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
logger.log('INFO', `[Orchestrator Helpers] Checking discovery need for ${userType}...`);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const blockCounts = await firestoreUtils.getBlockCapacities(dependencies, {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
// Logic for Normal Users remains (fill blocks to target)
|
|
28
|
+
const blockCounts = await firestoreUtils.getBlockCapacities(dependencies, { normalBlockCountsDocPath }, userType);
|
|
29
|
+
|
|
30
|
+
const underPopulatedBlocks = allHighValueBlocks.filter(block => (blockCounts[block.startId] || 0) < targetUsersPerBlock);
|
|
31
|
+
const needsDiscovery = underPopulatedBlocks.length > 0;
|
|
32
|
+
|
|
33
|
+
if (!needsDiscovery) {
|
|
34
|
+
logger.log('SUCCESS', `✅ All blocks are at target capacity for ${userType} users.`);
|
|
35
|
+
} else {
|
|
36
|
+
logger.log('INFO', `Found ${underPopulatedBlocks.length} underpopulated blocks for ${userType}.`);
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
return { needsDiscovery, blocksToFill: underPopulatedBlocks };
|
|
28
40
|
}
|
|
29
41
|
|
|
@@ -38,29 +50,70 @@ async function checkDiscoveryNeed(userType, config, dependencies) {
|
|
|
38
50
|
async function getDiscoveryCandidates(userType, blocksToFill, config, dependencies) {
|
|
39
51
|
const { logger, firestoreUtils } = dependencies;
|
|
40
52
|
logger.log('INFO', `[Orchestrator Helpers] Getting discovery candidates for ${userType}...`);
|
|
41
|
-
|
|
42
|
-
const
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
maxTasksPerRun,
|
|
56
|
+
discoveryBatchSize,
|
|
57
|
+
maxRandomCidsToDiscover,
|
|
58
|
+
specBlocksCollection,
|
|
59
|
+
pendingSpecCollection,
|
|
60
|
+
normalUserCollection,
|
|
61
|
+
invalidSpecCollection,
|
|
62
|
+
snapshotsSubCollectionName,
|
|
63
|
+
partsSubCollectionName
|
|
64
|
+
} = config;
|
|
65
|
+
|
|
43
66
|
const dispatchedCids = new Set();
|
|
44
|
-
|
|
67
|
+
|
|
68
|
+
// Fetch exclusion list to avoid re-discovering existing users
|
|
69
|
+
// Note: For Normal users, we check against existing Normal users.
|
|
70
|
+
const latestNormalPortfolios = await firestoreUtils.getLatestNormalUserPortfolios(dependencies, {
|
|
71
|
+
normalUserCollectionName: normalUserCollection,
|
|
72
|
+
snapshotsSubCollectionName,
|
|
73
|
+
partsSubCollectionName
|
|
74
|
+
});
|
|
75
|
+
|
|
45
76
|
const existingNormalUserIds = new Set(Object.keys(latestNormalPortfolios));
|
|
46
|
-
const exclusionIds = await firestoreUtils.getExclusionIds(dependencies, {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
77
|
+
const exclusionIds = await firestoreUtils.getExclusionIds(dependencies, {
|
|
78
|
+
specBlocksCollection,
|
|
79
|
+
pendingSpecCollection,
|
|
80
|
+
normalUserCollection,
|
|
81
|
+
invalidSpecCollection,
|
|
82
|
+
existingNormalUserIds
|
|
83
|
+
}, userType);
|
|
84
|
+
|
|
85
|
+
// --- RANDOM DISCOVERY LOOP (For Normal Users) ---
|
|
51
86
|
let dispatchedRandomCidCount = 0;
|
|
52
|
-
|
|
53
|
-
while ((dispatchedRandomCidCount +
|
|
87
|
+
|
|
88
|
+
while ((dispatchedRandomCidCount + dispatchedCids.size) < maxRandomCidsToDiscover &&
|
|
89
|
+
dispatchedCids.size < maxTasksPerRun &&
|
|
90
|
+
blocksToFill.length > 0) {
|
|
91
|
+
|
|
54
92
|
const blockIndex = dispatchedCids.size % blocksToFill.length;
|
|
55
93
|
const block = blocksToFill[blockIndex];
|
|
56
94
|
if (!block) break;
|
|
95
|
+
|
|
57
96
|
for (let j = 0; j < discoveryBatchSize; j++) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
97
|
+
if ((dispatchedRandomCidCount + dispatchedCids.size) >= maxRandomCidsToDiscover ||
|
|
98
|
+
dispatchedCids.size >= maxTasksPerRun) break;
|
|
99
|
+
|
|
100
|
+
let randomId;
|
|
101
|
+
let retryCount = 0;
|
|
102
|
+
const MAX_RETRIES = 50;
|
|
103
|
+
|
|
104
|
+
do {
|
|
105
|
+
if (++retryCount > MAX_RETRIES) break;
|
|
106
|
+
// Generate random CID within the block range
|
|
107
|
+
randomId = String(Math.floor(Math.random() * 1000000) + block.startId);
|
|
108
|
+
} while (exclusionIds.has(randomId) || dispatchedCids.has(randomId));
|
|
109
|
+
|
|
110
|
+
if (retryCount <= MAX_RETRIES) {
|
|
111
|
+
dispatchedCids.add(randomId);
|
|
112
|
+
dispatchedRandomCidCount++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
64
117
|
logger.log('INFO', `Generated ${dispatchedRandomCidCount} random CIDs for ${userType} discovery.`);
|
|
65
118
|
logger.log('INFO', `Total candidates for dispatch: ${dispatchedCids.size}`);
|
|
66
119
|
return dispatchedCids;
|
|
@@ -75,34 +128,54 @@ async function getDiscoveryCandidates(userType, blocksToFill, config, dependenci
|
|
|
75
128
|
* @returns {Promise<void>}
|
|
76
129
|
*/
|
|
77
130
|
async function dispatchDiscovery(userType, candidates, config, dependencies) {
|
|
78
|
-
const { logger,
|
|
79
|
-
const {topicName,dispatchBatchSize
|
|
80
|
-
|
|
131
|
+
const { logger, pubsub } = dependencies;
|
|
132
|
+
const { topicName, dispatchBatchSize } = config;
|
|
133
|
+
|
|
134
|
+
if (candidates.size === 0) {
|
|
135
|
+
logger.log('INFO', `[Orchestrator Helpers] No ${userType} candidates to dispatch.`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
81
139
|
logger.log('INFO', `[Orchestrator Helpers] Dispatching ${candidates.size} discovery tasks for ${userType}...`);
|
|
82
|
-
|
|
140
|
+
|
|
83
141
|
const cidsArray = Array.from(candidates);
|
|
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});}
|
|
87
142
|
const tasks = [];
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
143
|
+
|
|
144
|
+
for (let i = 0; i < cidsArray.length; i += dispatchBatchSize) {
|
|
145
|
+
const batchCids = cidsArray.slice(i, i + dispatchBatchSize).map(cid => parseInt(cid));
|
|
146
|
+
if (batchCids.length > 0) {
|
|
147
|
+
const blockId = Math.floor(batchCids[0] / 1000000) * 1000000;
|
|
148
|
+
tasks.push({
|
|
149
|
+
type: 'discover',
|
|
150
|
+
cids: batchCids,
|
|
151
|
+
blockId,
|
|
152
|
+
userType
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
92
157
|
const topic = pubsub.topic(topicName);
|
|
93
158
|
let totalCidsPublished = 0;
|
|
94
159
|
let messagesPublished = 0;
|
|
160
|
+
|
|
95
161
|
for (let i = 0; i < tasks.length; i += dispatchBatchSize) {
|
|
96
162
|
const batchOfTasks = tasks.slice(i, i + dispatchBatchSize);
|
|
97
163
|
const messagePayload = { tasks: batchOfTasks };
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
await topic.publishMessage({ json: messagePayload });
|
|
167
|
+
|
|
168
|
+
const cidsInThisMessage = batchOfTasks.reduce((acc, task) => acc + task.cids.length, 0);
|
|
169
|
+
totalCidsPublished += cidsInThisMessage;
|
|
170
|
+
messagesPublished++;
|
|
171
|
+
|
|
172
|
+
logger.log('INFO', `[Orchestrator Helpers] Dispatched batch ${messagesPublished} with ${batchOfTasks.length} discover tasks (${cidsInThisMessage} CIDs) as 1 Pub/Sub message.`);
|
|
173
|
+
} catch (publishError) {
|
|
174
|
+
logger.log('ERROR', `[Orchestrator Helpers] Failed to publish discover batch ${messagesPublished + 1}.`, { error: publishError.message });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
logger.log('SUCCESS', `[Orchestrator Helpers] Dispatched ${totalCidsPublished} CIDs in ${tasks.length} tasks for ${userType} discovery.`);
|
|
106
179
|
}
|
|
107
180
|
|
|
108
181
|
module.exports = { checkDiscoveryNeed, getDiscoveryCandidates, dispatchDiscovery };
|