@@ -593,6 +593,7 @@ export abstract class MongoSyncBucketStorage
593593 {
594594 $facet : {
595595 sampledOps : [ { $count : 'count' } ] ,
596+ rowOps : [ { $match : { op : { $in : [ 'PUT' , 'REMOVE' ] } } } , { $count : 'count' } ] ,
596597 distinctRows : [
597598 { $match : { op : { $in : [ 'PUT' , 'REMOVE' ] } } } ,
598599 { $group : { _id : { table : '$table' , row_id : '$row_id' } } } ,
@@ -601,34 +602,46 @@ export abstract class MongoSyncBucketStorage
601602 }
602603 }
603604 ] ;
604- type FacetResult = { sampledOps : { count : number } [ ] ; distinctRows : { count : number } [ ] } ;
605+ type FacetResult = {
606+ sampledOps : { count : number } [ ] ;
607+ rowOps : { count : number } [ ] ;
608+ distinctRows : { count : number } [ ] ;
609+ } ;
605610 const [ result ] = await collection
606611 . aggregate < FacetResult > ( pipeline , { allowDiskUse : false , maxTimeMS : storage . BUCKET_REPORT_TIMEOUT_MS } )
607612 . toArray ( ) ;
608613 return {
609614 sampledOps : result ?. sampledOps [ 0 ] ?. count ?? 0 ,
615+ rowOps : result ?. rowOps [ 0 ] ?. count ?? 0 ,
610616 distinctRows : result ?. distinctRows [ 0 ] ?. count ?? 0
611617 } ;
612618 } ;
613619
614- let { sampledOps , distinctRows } = await runCounts ( sampled ) ;
615- if ( sampled && sampledOps == 0 ) {
620+ let counts = await runCounts ( sampled ) ;
621+ if ( sampled && counts . sampledOps == 0 ) {
616622 // A document-level `$sampleRate` can select nothing when a bucket spans very few storage documents
617623 // (v3 batches operations into a document). Fall back to an exact read so the bucket is not reported as
618624 // zero rows. This reads the whole bucket only in the rare empty-sample case, which cannot happen for a
619625 // bucket large enough to span many documents.
620- distinctRows = ( await runCounts ( false ) ) . distinctRows ;
621- return { rows : distinctRows , estimated : false } ;
626+ return { rows : ( await runCounts ( false ) ) . distinctRows , estimated : false } ;
622627 }
623- if ( distinctRows == 0 ) {
628+ if ( counts . distinctRows == 0 ) {
624629 // Nothing row-bearing was found (e.g. a bucket of only MOVE/CLEAR ops): treat as fully fragmented.
625630 return { rows : 0 , estimated : sampled } ;
626631 }
627632 if ( ! sampled ) {
628633 // Read in full: the distinct row count is exact.
629- return { rows : distinctRows , estimated : false } ;
634+ return { rows : counts . distinctRows , estimated : false } ;
630635 }
631- return { rows : storage . estimateDistinctRows ( operations , sampledOps , distinctRows ) , estimated : true } ;
636+ // Only PUT/REMOVE operations carry a row identity; MOVE/CLEAR (produced by compaction) do not. Run the
637+ // estimator over the row-bearing operations only, scaling the bucket's operation count by the row-bearing
638+ // share observed in the sample. Including identity-less operations in the model under-counts rows on
639+ // compacted buckets. For uncompacted buckets rowOps equals sampledOps and this changes nothing.
640+ const rowBearingOperations = Math . round ( operations * ( counts . rowOps / counts . sampledOps ) ) ;
641+ return {
642+ rows : storage . estimateDistinctRows ( rowBearingOperations , counts . rowOps , counts . distinctRows ) ,
643+ estimated : true
644+ } ;
632645 }
633646
634647 /**
0 commit comments