Time-series write path ignores extended-range data when selecting bucket-level predicate generator

XMLWordPrintableJSON

    • Type: Bug
    • Resolution: Fixed
    • Priority: Major - P3
    • 9.1.0-rc0
    • 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).

            Assignee:
            Chris Wolff
            Reporter:
            Chris Wolff
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: