@@ -10,6 +10,10 @@ const { BAD_REQUEST } = require('../../utils/status-codes');
1010
1111const router = Router ( { mergeParams : true } ) ;
1212
13+ // In-memory cache for the GridFS size aggregation (expensive full-collection scan)
14+ const CACHE_TTL_MS = 60 * 60 * 1000 * 24 ; // 24 hour
15+ let gridFsSizeCache = new Map ( ) ;
16+
1317// Root -> display available PDBs
1418router . route ( '/' ) . get (
1519 handler ( {
@@ -27,10 +31,48 @@ router.route('/').get(
2731 const database = await getDatabase ( request ) ;
2832 // Get database statistics
2933 const dbStats = await database . db . command ( { dbStats : 1 , scale : 1e6 } ) ; // Results in KB
34+ // Compute the real stored size of GridFS files for the queried projects
35+ // Without a query filter this would require aggregating all files in the DB,
36+ // so we skip it and fall back to the dbStats figure instead
37+ const query = request . query . query ;
38+ let realDataSizeInTB ;
39+ if ( ! query ) {
40+ realDataSizeInTB = + ( dbStats . dataSize / 1e6 ) ;
41+ } else {
42+ // This is expensive so we cache it with a TTL, keyed by query string
43+ if ( ! gridFsSizeCache . has ( query )
44+ || Date . now ( ) - gridFsSizeCache . get ( query ) . timestamp > CACHE_TTL_MS ) {
45+ const projectsFinder = database . getBaseFilter ( ) ;
46+ const processedQuery = await database . processProjectsQuery ( query ) ;
47+ if ( processedQuery . error ) return processedQuery ;
48+ if ( ! projectsFinder . $and ) projectsFinder . $and = processedQuery ;
49+ else projectsFinder . $and = projectsFinder . $and . concat ( processedQuery ) ;
50+ // Get all file IDs, including those in MDs
51+ const projectsCursor = await database . projects . find (
52+ projectsFinder ,
53+ { projection : { files : 1 , 'mds.files' : 1 } } ,
54+ ) ;
55+ const allProjects = await projectsCursor . toArray ( ) ;
56+ // Flatten all file IDs
57+ const fileIds = allProjects . flatMap ( project => [
58+ ...( project . files ?? [ ] ) . map ( f => f . id ) ,
59+ ...( project . mds ?? [ ] ) . flatMap ( md => ( md . files ?? [ ] ) . map ( f => f . id ) ) ,
60+ ] ) ;
61+ // Sum 'length' from fs.files for all those IDs
62+ const sizeAgg = await database . db . collection ( 'fs.files' ) . aggregate ( [
63+ { $match : { _id : { $in : fileIds } } } ,
64+ { $group : { _id : null , totalBytes : { $sum : '$length' } } } ,
65+ ] ) . toArray ( ) ;
66+ const totalBytes = sizeAgg . length > 0 ? sizeAgg [ 0 ] . totalBytes : 0 ;
67+ gridFsSizeCache . set ( query , { totalBytes, timestamp : Date . now ( ) } ) ;
68+ }
69+ realDataSizeInTB = + ( gridFsSizeCache . get ( query ) . totalBytes / 1e12 ) ;
70+ }
3071 // Create a formatted response with values in TB
3172 const storageStats = {
3273 // databaseName: dbStats.db,
3374 dataSizeInTB : + ( dbStats . dataSize / 1e6 ) . toFixed ( 2 ) ,
75+ realDataSizeInTB, // Decimals are not fixed because we may have to change the unit (TB vs GB) depending on the value
3476 storageUsedInTB : + ( dbStats . storageSize / 1e6 ) . toFixed ( 2 ) ,
3577 indexSizeInMB : + ( dbStats . indexSize ) . toFixed ( 2 ) ,
3678 usedDiskInTB : + ( dbStats . fsUsedSize / 1e6 ) . toFixed ( 2 ) ,
0 commit comments