SERVER-117870 Fix aggregation view resolution concurrent to viewless timeseries upgrade (#47026)

GitOrigin-RevId: 7a9e77287614a2b91db9b6e1700761883c724f42
This commit is contained in:
Joan Bruguera Micó (at MongoDB) 2026-01-29 07:56:08 +00:00 committed by MongoDB Bot
parent f74364871c
commit c0ba541999
5 changed files with 105 additions and 0 deletions

3
.github/CODEOWNERS vendored
View File

@ -1198,6 +1198,9 @@ WORKSPACE.bazel @10gen/devprod-build @svc-auto-approve-bot
# The following patterns are parsed from ./jstests/multiVersion/genericSetFCVUsage/index_builds/OWNERS.yml
/jstests/multiVersion/genericSetFCVUsage/index_builds/**/* @10gen/server-index-builds @svc-auto-approve-bot
# The following patterns are parsed from ./jstests/multiVersion/genericSetFCVUsage/query-optimization/OWNERS.yml
/jstests/multiVersion/genericSetFCVUsage/query-optimization/**/* @10gen/query-optimization @svc-auto-approve-bot
# The following patterns are parsed from ./jstests/multiVersion/genericSetFCVUsage/shard_catalog/OWNERS.yml
/jstests/multiVersion/genericSetFCVUsage/shard_catalog/**/* @10gen/server-catalog-and-routing-shard-catalog @svc-auto-approve-bot

View File

@ -0,0 +1,12 @@
load("//bazel:mongo_js_rules.bzl", "all_subpackage_javascript_files", "mongo_js_library")
package(default_visibility = ["//visibility:public"])
mongo_js_library(
name = "all_javascript_files",
srcs = glob([
"*.js",
]),
)
all_subpackage_javascript_files()

View File

@ -0,0 +1,5 @@
version: 1.0.0
filters:
- "*":
approvers:
- 10gen/query-optimization

View File

@ -0,0 +1,61 @@
/**
* Tests that aggregations return correct results when a timeseries collection is upgraded from
* viewful to viewless format mid-query. In particular, if the aggregation resolves the timeseries
* view but the buckets collection is transformed to viewless timeseries before it can be acquired.
*
* @tags: [
* requires_timeseries,
* featureFlagCreateViewlessTimeseriesCollections,
* ]
*/
import {configureFailPoint} from "jstests/libs/fail_point_util.js";
import {Thread} from "jstests/libs/parallelTester.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
// TODO(SERVER-114573): Remove this test once 9.0 becomes lastLTS.
if (lastLTSFCV != "8.0") {
quit();
}
const rst = new ReplSetTest({nodes: 1});
rst.startSet();
rst.initiate();
const primary = rst.getPrimary();
const dbName = jsTestName();
const collName = jsTestName();
const testDB = primary.getDB(dbName);
const coll = testDB[collName];
// Create timeseries in viewful format.
assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV, confirm: true}));
assert.commandWorked(testDB.createCollection(collName, {timeseries: {timeField: "t"}}));
assert.commandWorked(coll.insertOne({t: ISODate()}));
// Pause aggregation after resolving the view but before acquiring buckets.
const fp = configureFailPoint(primary, "hangAfterAcquiringCollectionCatalog", {collection: collName});
const aggThread = new Thread(
function (host, dbName, collName) {
const conn = new Mongo(host);
const result = conn.getDB(dbName).runCommand({aggregate: collName, pipeline: [], cursor: {}});
assert.commandWorked(result);
assert.eq(1, result.cursor.firstBatch.length);
},
primary.host,
dbName,
collName,
);
aggThread.start();
fp.wait();
// Upgrade FCV while aggregation is paused - converts viewful to viewless.
assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: latestFCV, confirm: true}));
// Release aggregation. We expect non-empty results.
fp.off();
aggThread.join();
rst.stopSet();

View File

@ -242,6 +242,30 @@ private:
[&](const BSONObj& data) {
return _aggExState.getExecutionNss().coll() == data["collection"].valueStringData();
});
// TODO SERVER-111172: Remove this test once view-ful timeseries are removed and 9.0 is LTS.
if (_aggExState.isView() && !_mainAcq->collectionExists() &&
executionNss.isTimeseriesBucketsCollection()) {
// We resolved a timeseries view, but didn't later find the targeted buckets collection.
// If we continue execution, we will return no documents.
//
// This can happen in multiple ways, such as:
// 1. Because the timeseries collection is being converted to viewless timeseries.
// 2. Due to the timeseries collection (buckets + view) being concurrently dropped.
// 3. Due to a 'orphaned' timeseries view (e.g. because of a stepdown during a drop).
//
// In scenario (1), it is incorrect to return empty results. Throw CollectionBecameView
// to re-resolve the aggregation over the now viewless timeseries collection.
//
// In scenarios like (2) or (3), preserve the existing (v8.0) behavior of returning
// empty results on queries over dropped or incomplete viewful timeseries collections.
auto originalNssColl = CollectionCatalog::get(opCtx)->establishConsistentCollection(
opCtx, _aggExState.getOriginalNss(), boost::none /* readTimestamp */);
if (originalNssColl && originalNssColl->isTimeseriesCollection()) {
uasserted(ErrorCodes::CollectionBecameView,
"Timeseries collection upgraded to viewless format while resolving view");
}
}
}