-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: None
-
None
-
Query Integration
-
Fully Compatible
-
ALL
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Overview
On the time-series write path (deletes and updates), bucket-level predicate generation can select the fixed-bucket predicate generator even when the collection contains extended-range data. The code comment at the selection point explicitly states that fixed-bucket optimizations are "not compatible with extended range data", so this can produce incorrect bucket-level predicates and therefore wrong or missed documents modified by deletes/updates on fixed-bucket collections that hold dates outside the 1970-2038 range.
This was found while working on SERVER-131033 (consolidating the usesExtendedRange flag). It is the same "flag divergence" theme, but a distinct location: here the extended-range fact lives on the ExpressionContext and in the catalog, but is never propagated into the BucketSpec that drives write-path predicate generation.
Mechanism
BucketSpec::getPushdownPredicates() (src/mongo/db/query/timeseries/bucket_spec.cpp, ~line 437) constructs a default BucketSpec whose usesExtendedRange() is always false:
BucketSpec bucketSpec{
std::string{tsOptions.getTimeField\(\)},
metaField.map\(...\),
// default values for the rest of the arguments
};
It passes that spec to createPredicatesOnBucketLevelField(), which reaches BucketLevelComparisonPredicateGenerator::getBuilder() (src/mongo/db/query/timeseries/bucket_level_comparison_predicate_generator.cpp:586):
if \(\!params.bucketSpec.usesExtendedRange\(\) && params.fixedBuckets\) {
// Fixed bucket optimizations are not compatible with extended range data
return std::make\_unique\(std::move\(params\)\);
}
Because params.bucketSpec.usesExtendedRange() is always false here, a fixed-bucket collection (fixedBuckets == true) with extended-range data always gets the fixed-bucket generator.
The write path is reached via timeseries::getMatchExprsForWrites() (src/mongo/db/timeseries/timeseries_update_delete_util.cpp:329), used by both CanonicalDelete and CanonicalUpdate.
Contrast with the read path
The read path passes the real spec with the flag set correctly, so it selects the generator correctly:
// DocumentSourceInternalUnpackBucket::createPredicatesOnBucketLevelField \(line \~1210\) return BucketSpec::createPredicatesOnBucketLevelField\( matchExpr, \_sharedState\->\_bucketUnpacker.bucketSpec\(\), // real usesExtendedRange ...\);
Note: the separate _id-predicate generation inside getPushdownPredicates (gated on expCtx->getRequiresTimeseriesExtendedRangeSupport()) is correct, because the write paths do set the expCtx flag (canonical_delete.cpp:54 and the update expCtx builders in write_ops_exec.cpp). The bug is specifically the generator-selection input.
Proposed fix
Set the local spec's flag from the source of truth when constructing it in getPushdownPredicates:
BucketSpec bucketSpec{...};
bucketSpec.setUsesExtendedRange\(expCtx\->getRequiresTimeseriesExtendedRangeSupport\(\)\);
This makes the generator selection read a correct, consistent value (single source of truth = BucketSpec), matching the read path.
Testing
- Write a repro first: a fixed-bucket time-series collection with extended-range data (dates outside 1970-2038), then a delete and an update with a time predicate; assert the correct documents are modified. Confirm it fails before the fix and passes after.
- Reuse/extend existing extended-range and timeseries write suites where possible (e.g. jstests/core/timeseries/write/timeseries_update_delete_extended_range.js).
- is related to
-
SERVER-96993 Reenable time-series fixed bucket optimizations
-
- Closed
-
- related to
-
SERVER-131033 Consolidate _usesExtendedRange from BucketSpec and DocumentSource
-
- Closed
-