bulltrackers-module 1.0.660 → 1.0.661
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.
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
const { FieldValue } = require('@google-cloud/firestore');
|
|
7
7
|
const zlib = require('zlib');
|
|
8
|
+
const { Storage } = require('@google-cloud/storage');
|
|
8
9
|
const { getAlertTypeByComputation, generateAlertMessage } = require('./alert_type_registry');
|
|
9
10
|
// Migration helpers removed - write directly to new path
|
|
10
11
|
|
|
12
|
+
const storage = new Storage(); // Singleton GCS Client
|
|
13
|
+
|
|
11
14
|
/**
|
|
12
15
|
* Process alerts for a specific PI from computation results
|
|
13
16
|
*/
|
|
@@ -474,10 +477,48 @@ function readComputationResults(docData) {
|
|
|
474
477
|
}
|
|
475
478
|
|
|
476
479
|
/**
|
|
477
|
-
* Read computation results, handling sharded data
|
|
480
|
+
* Read computation results, handling GCS pointers, sharded data, and compressed data
|
|
481
|
+
* UPDATED: Added GCS pointer support to read from GCS when data is offloaded
|
|
478
482
|
*/
|
|
479
|
-
async function readComputationResultsWithShards(db, docData, docRef) {
|
|
483
|
+
async function readComputationResultsWithShards(db, docData, docRef, logger = null) {
|
|
480
484
|
try {
|
|
485
|
+
// -------------------------------------------------------------------------
|
|
486
|
+
// 1. GCS POINTER HANDLER (Check first - highest priority)
|
|
487
|
+
// -------------------------------------------------------------------------
|
|
488
|
+
if (docData.gcsUri || (docData._gcs && docData.gcsBucket && docData.gcsPath)) {
|
|
489
|
+
try {
|
|
490
|
+
const bucketName = docData.gcsBucket || docData.gcsUri.split('/')[2];
|
|
491
|
+
const fileName = docData.gcsPath || docData.gcsUri.split('/').slice(3).join('/');
|
|
492
|
+
|
|
493
|
+
if (logger) {
|
|
494
|
+
logger.log('INFO', `[AlertSystem] Reading computation results from GCS: ${fileName}`);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Stream download is memory efficient for large files
|
|
498
|
+
const [fileContent] = await storage.bucket(bucketName).file(fileName).download();
|
|
499
|
+
|
|
500
|
+
// Assume Gzip (as writer does it), if fails try plain
|
|
501
|
+
let decompressedData;
|
|
502
|
+
try {
|
|
503
|
+
decompressedData = JSON.parse(zlib.gunzipSync(fileContent).toString('utf8'));
|
|
504
|
+
} catch (gzipErr) {
|
|
505
|
+
// Fallback for uncompressed GCS files
|
|
506
|
+
decompressedData = JSON.parse(fileContent.toString('utf8'));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Process the decompressed data through readComputationResults
|
|
510
|
+
return readComputationResults(decompressedData);
|
|
511
|
+
} catch (gcsErr) {
|
|
512
|
+
if (logger) {
|
|
513
|
+
logger.log('ERROR', `[AlertSystem] GCS fetch failed, falling back to Firestore: ${gcsErr.message}`);
|
|
514
|
+
}
|
|
515
|
+
// Fall through to Firestore logic below
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// -------------------------------------------------------------------------
|
|
520
|
+
// 2. FIRESTORE SHARDED HANDLER
|
|
521
|
+
// -------------------------------------------------------------------------
|
|
481
522
|
if (docData._sharded === true && docData._shardCount) {
|
|
482
523
|
const shardsCol = docRef.collection('_shards');
|
|
483
524
|
const shardsSnapshot = await shardsCol.get();
|
|
@@ -492,9 +533,17 @@ async function readComputationResultsWithShards(db, docData, docRef) {
|
|
|
492
533
|
return readComputationResults(mergedData);
|
|
493
534
|
}
|
|
494
535
|
}
|
|
536
|
+
|
|
537
|
+
// -------------------------------------------------------------------------
|
|
538
|
+
// 3. FIRESTORE COMPRESSED OR DIRECT DATA HANDLER
|
|
539
|
+
// -------------------------------------------------------------------------
|
|
495
540
|
return readComputationResults(docData);
|
|
496
541
|
} catch (error) {
|
|
497
|
-
|
|
542
|
+
if (logger) {
|
|
543
|
+
logger.log('ERROR', `[AlertSystem] Error reading computation results: ${error.message}`);
|
|
544
|
+
} else {
|
|
545
|
+
console.error('[readComputationResultsWithShards] Error reading sharded results', error);
|
|
546
|
+
}
|
|
498
547
|
return { cids: [], metadata: {}, perUserData: {} };
|
|
499
548
|
}
|
|
500
549
|
}
|
|
@@ -72,9 +72,9 @@ async function handleAlertTrigger(message, context, config, dependencies) {
|
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// 3. Read and decompress computation results
|
|
75
|
+
// 3. Read and decompress computation results (handling GCS, shards, and compression)
|
|
76
76
|
const docData = docSnapshot.data();
|
|
77
|
-
const results =
|
|
77
|
+
const results = await readComputationResultsWithShards(db, docData, docRef, logger);
|
|
78
78
|
|
|
79
79
|
if (!results.cids || results.cids.length === 0) {
|
|
80
80
|
logger.log('INFO', `[AlertTrigger] No PIs found in computation results for ${computationName}`);
|
|
@@ -188,7 +188,7 @@ async function handleComputationResultWrite(change, context, config, dependencie
|
|
|
188
188
|
// If it's PopularInvestorProfileMetrics, check for all-clear notifications only
|
|
189
189
|
if (isProfileMetrics) {
|
|
190
190
|
const docData = change.after.data();
|
|
191
|
-
const results = await readComputationResultsWithShards(db, docData, change.after.ref);
|
|
191
|
+
const results = await readComputationResultsWithShards(db, docData, change.after.ref, logger);
|
|
192
192
|
if (results.cids && results.cids.length > 0) {
|
|
193
193
|
await checkAndSendAllClearNotifications(db, logger, results.cids, date, config, dependencies);
|
|
194
194
|
}
|
|
@@ -203,9 +203,9 @@ async function handleComputationResultWrite(change, context, config, dependencie
|
|
|
203
203
|
|
|
204
204
|
logger.log('INFO', `[AlertTrigger] Processing alert computation: ${computationName} for date ${date}`);
|
|
205
205
|
|
|
206
|
-
// 2. Read and decompress computation results (handling shards)
|
|
206
|
+
// 2. Read and decompress computation results (handling GCS, shards, and compression)
|
|
207
207
|
const docData = change.after.data();
|
|
208
|
-
const results = await readComputationResultsWithShards(db, docData, change.after.ref);
|
|
208
|
+
const results = await readComputationResultsWithShards(db, docData, change.after.ref, logger);
|
|
209
209
|
|
|
210
210
|
if (!results.cids || results.cids.length === 0) {
|
|
211
211
|
logger.log('INFO', `[AlertTrigger] No PIs found in computation results for ${computationName}`);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// Firestore helper functions for fetching data from collections
|
|
2
2
|
const { FieldValue, Timestamp } = require('@google-cloud/firestore');
|
|
3
|
+
const { Storage } = require('@google-cloud/storage');
|
|
3
4
|
const { dispatchSyncRequest } = require('../task_engine_helper.js');
|
|
4
5
|
const { sanitizeCid, sanitizeDocId } = require('../security_utils.js');
|
|
5
6
|
const crypto = require('crypto');
|
|
6
7
|
const zlib = require('zlib');
|
|
7
8
|
|
|
9
|
+
const storage = new Storage(); // Singleton GCS Client
|
|
10
|
+
|
|
8
11
|
// 1. Fetch latest stored snapshots of user data from a user-centric collection
|
|
9
12
|
|
|
10
13
|
// Examples
|
|
@@ -1210,14 +1213,40 @@ const getComputationResults = async (db, computationName, dateStr, userId = null
|
|
|
1210
1213
|
|
|
1211
1214
|
const pointerData = pointerSnap.data();
|
|
1212
1215
|
|
|
1213
|
-
// 2. Strategy:
|
|
1216
|
+
// 2. Strategy: GCS Pointer (Check first - highest priority)
|
|
1217
|
+
// If _gcs is true or gcsUri exists, the data is stored in GCS
|
|
1218
|
+
// Note: Page mode is exempt from GCS logic (handled separately below)
|
|
1219
|
+
if (pointerData._isPageMode !== true && (pointerData.gcsUri || (pointerData._gcs && pointerData.gcsBucket && pointerData.gcsPath))) {
|
|
1220
|
+
try {
|
|
1221
|
+
const bucketName = pointerData.gcsBucket || pointerData.gcsUri.split('/')[2];
|
|
1222
|
+
const fileName = pointerData.gcsPath || pointerData.gcsUri.split('/').slice(3).join('/');
|
|
1223
|
+
|
|
1224
|
+
console.log(`[Computation] Reading from GCS: ${fileName} for ${computationName}`);
|
|
1225
|
+
|
|
1226
|
+
// Stream download is memory efficient for large files
|
|
1227
|
+
const [fileContent] = await storage.bucket(bucketName).file(fileName).download();
|
|
1228
|
+
|
|
1229
|
+
// Assume Gzip (as writer does it), if fails try plain
|
|
1230
|
+
try {
|
|
1231
|
+
return JSON.parse(zlib.gunzipSync(fileContent).toString('utf8'));
|
|
1232
|
+
} catch (gzipErr) {
|
|
1233
|
+
// Fallback for uncompressed GCS files
|
|
1234
|
+
return JSON.parse(fileContent.toString('utf8'));
|
|
1235
|
+
}
|
|
1236
|
+
} catch (gcsErr) {
|
|
1237
|
+
console.error(`[Computation] GCS fetch failed for ${computationName}, falling back to Firestore: ${gcsErr.message}`);
|
|
1238
|
+
// Fall through to Firestore strategies below
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// 3. Strategy: Compressed Data
|
|
1214
1243
|
// If _compressed is true, the data is inside the payload field, just zipped.
|
|
1215
1244
|
if (pointerData._compressed === true) {
|
|
1216
1245
|
console.log(`[Computation] Reading compressed data for ${computationName}`);
|
|
1217
1246
|
return tryDecompress(pointerData);
|
|
1218
1247
|
}
|
|
1219
1248
|
|
|
1220
|
-
//
|
|
1249
|
+
// 4. Strategy: Sharded Data
|
|
1221
1250
|
// If _sharded is true, we must fetch N documents from the _shards subcollection.
|
|
1222
1251
|
if (pointerData._sharded === true) {
|
|
1223
1252
|
const shardCount = pointerData._shardCount || 0;
|
|
@@ -1260,14 +1289,15 @@ const getComputationResults = async (db, computationName, dateStr, userId = null
|
|
|
1260
1289
|
return reassembledData;
|
|
1261
1290
|
}
|
|
1262
1291
|
|
|
1263
|
-
//
|
|
1292
|
+
// 5. Strategy: Page Mode (User Centric)
|
|
1264
1293
|
// If _isPageMode is true, we delegate to the pageCollection helper.
|
|
1294
|
+
// Note: Page mode is exempt from GCS logic (uses individual user documents)
|
|
1265
1295
|
if (pointerData._isPageMode === true) {
|
|
1266
1296
|
console.log(`[Computation] Fetching page mode data for ${computationName} / User: ${userId}`);
|
|
1267
1297
|
return await pageCollection(db, dateStr, computationName, userId);
|
|
1268
1298
|
}
|
|
1269
1299
|
|
|
1270
|
-
//
|
|
1300
|
+
// 6. Strategy: Standard (Direct Read)
|
|
1271
1301
|
// If no flags are set, the data is in the pointer document itself.
|
|
1272
1302
|
console.log(`[Computation] Returning direct pointer data for ${computationName}`);
|
|
1273
1303
|
return pointerData;
|