mirror of https://github.com/mongodb/mongo
SERVER-110919 Ban hint on '_id_' for timeseries collections (#41961)
GitOrigin-RevId: 4d73214d504aa32e91911ae0fe40fbe87af9a039
This commit is contained in:
parent
0c4afc24f0
commit
911c390cb7
|
|
@ -44,6 +44,7 @@ suites:
|
|||
- jstests/core/timeseries/write/timeseries_delete_with_meta_concurrent.js
|
||||
- jstests/core/timeseries/write/timeseries_findAndModify_deletes_hints.js
|
||||
- jstests/core/timeseries/write/timeseries_findAndModify_updates_hints.js
|
||||
- jstests/core/timeseries/write/timeseries_hint_id.js
|
||||
- jstests/core/timeseries/write/timeseries_insert.js
|
||||
- jstests/core/timeseries/write/timeseries_insert_after_delete.js
|
||||
- jstests/core/timeseries/write/timeseries_insert_after_update.js
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ suites:
|
|||
- jstests/core/timeseries/write/timeseries_delete_with_meta_concurrent.js
|
||||
- jstests/core/timeseries/write/timeseries_findAndModify_deletes_hints.js
|
||||
- jstests/core/timeseries/write/timeseries_findAndModify_updates_hints.js
|
||||
- jstests/core/timeseries/write/timeseries_hint_id.js
|
||||
- jstests/core/timeseries/write/timeseries_insert.js
|
||||
- jstests/core/timeseries/write/timeseries_insert_after_delete.js
|
||||
- jstests/core/timeseries/write/timeseries_insert_after_update.js
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
* Tests running the find and delete commands with a hint on the _id index on a timeseries
|
||||
* collection. Verifies that commands specifying a hint on the _id index via "_id_" always fail.
|
||||
* @tags: [
|
||||
* # This is a backwards-breaking change.
|
||||
* requires_fcv_83,
|
||||
* does_not_support_stepdowns,
|
||||
* # Retryable deletes, for example, not supported on timeseries collections
|
||||
* does_not_support_retryable_writes,
|
||||
* requires_non_retryable_commands,
|
||||
* requires_non_retryable_writes,
|
||||
* # We need a timeseries collection.
|
||||
* requires_timeseries,
|
||||
* ]
|
||||
*/
|
||||
const timeFieldName = "time";
|
||||
const metaFieldName = "tag";
|
||||
const collName = jsTestName();
|
||||
const dbName = jsTestName();
|
||||
|
||||
const testDB = db.getSiblingDB(dbName);
|
||||
const coll = testDB.getCollection(collName);
|
||||
|
||||
assert.commandWorked(
|
||||
testDB.createCollection(coll.getName(), {timeseries: {timeField: timeFieldName, metaField: metaFieldName}}),
|
||||
);
|
||||
|
||||
const objA = {
|
||||
[timeFieldName]: ISODate(),
|
||||
"measurement": {"A": "cpu"},
|
||||
[metaFieldName]: {a: "A"},
|
||||
};
|
||||
const objB = {
|
||||
[timeFieldName]: ISODate(),
|
||||
"measurement": {"A": "cpu"},
|
||||
[metaFieldName]: {b: "B"},
|
||||
};
|
||||
const objC = {
|
||||
[timeFieldName]: ISODate(),
|
||||
"measurement": {"A": "cpu"},
|
||||
[metaFieldName]: {c: "C"},
|
||||
};
|
||||
|
||||
assert.commandWorked(coll.insert([objA, objB, objC]));
|
||||
|
||||
function runCommandOnTSCollection(commandWithHintObjectValue, commandWithHintStringValue) {
|
||||
// No user-facing index exists on the _id field. It only exists on the underlying buckets
|
||||
// collection.
|
||||
assert.commandFailedWithCode(testDB.runCommand(commandWithHintObjectValue), ErrorCodes.BadValue);
|
||||
|
||||
assert.commandFailedWithCode(testDB.runCommand(commandWithHintStringValue), ErrorCodes.BadValue);
|
||||
|
||||
// Create an index on the _id field of the user-facing view collection.
|
||||
assert.commandWorked(coll.createIndex({_id: 1}));
|
||||
|
||||
// This hint will be rewritten as the key pattern (ex:
|
||||
// "hint":{"control.min._id":1,"control.max._id":1}) for the buckets collection.
|
||||
assert.commandWorked(testDB.runCommand(commandWithHintObjectValue));
|
||||
|
||||
assert.commandFailedWithCode(testDB.runCommand(commandWithHintStringValue), ErrorCodes.BadValue);
|
||||
|
||||
// Drop the index on the _id field of the user-facing view collection.
|
||||
assert.commandWorked(coll.dropIndex({_id: 1}));
|
||||
}
|
||||
|
||||
(function runFindCommand() {
|
||||
runCommandOnTSCollection({find: collName, hint: {_id: 1}}, {find: collName, hint: "_id_"});
|
||||
})();
|
||||
|
||||
(function runDeleteCommand() {
|
||||
runCommandOnTSCollection(
|
||||
{delete: collName, deletes: [{q: {metaFieldName: "b"}, limit: 0, hint: {_id: 1}}]},
|
||||
{delete: collName, deletes: [{q: {metaFieldName: "c"}, limit: 0, hint: "_id_"}]},
|
||||
);
|
||||
assert.commandWorked(coll.insert([objB, objC]));
|
||||
})();
|
||||
|
||||
(function runAggregateCommand() {
|
||||
assert.commandWorked(coll.insert([objA]));
|
||||
const matchPipeline = [{$match: {metaFieldName: "a"}}, {$sort: {_id: 1}}, {$project: {_id: 0, time: 0}}];
|
||||
runCommandOnTSCollection(
|
||||
{
|
||||
aggregate: collName,
|
||||
pipeline: matchPipeline,
|
||||
cursor: {},
|
||||
allowDiskUse: true,
|
||||
hint: {_id: 1},
|
||||
},
|
||||
{
|
||||
aggregate: collName,
|
||||
pipeline: matchPipeline,
|
||||
cursor: {},
|
||||
allowDiskUse: true,
|
||||
hint: "_id_",
|
||||
},
|
||||
);
|
||||
})();
|
||||
|
||||
(function runUpdateCommand() {
|
||||
runCommandOnTSCollection(
|
||||
{
|
||||
update: collName,
|
||||
updates: [
|
||||
{
|
||||
q: {tag: "b"},
|
||||
u: {$set: {tag: {b: "RAM"}}},
|
||||
hint: {_id: 1},
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
update: collName,
|
||||
updates: [
|
||||
{
|
||||
q: {tag: "c"},
|
||||
u: {$set: {tag: {c: "L1"}}},
|
||||
hint: "_id_",
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
})();
|
||||
|
||||
(function runCountCommand() {
|
||||
runCommandOnTSCollection(
|
||||
{count: collName, query: {metaFieldName: "a"}, hint: {_id: 1}},
|
||||
{count: collName, query: {metaFieldName: "a"}, hint: "_id_"},
|
||||
);
|
||||
})();
|
||||
|
|
@ -44,6 +44,7 @@
|
|||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/bson/bsontypes.h"
|
||||
#include "mongo/db/index/index_constants.h"
|
||||
#include "mongo/db/index_names.h"
|
||||
#include "mongo/db/local_catalog/clustered_collection_options_gen.h"
|
||||
#include "mongo/db/matcher/expression_algo.h"
|
||||
|
|
@ -978,9 +979,18 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan(
|
|||
boost::optional<BSONObj> hintedIndexBson = boost::none;
|
||||
if (!params.indexFiltersApplied && !params.querySettingsApplied) {
|
||||
if (auto hintObj = query.getFindCommandRequest().getHint(); !hintObj.isEmpty()) {
|
||||
if (params.mainCollectionInfo.stats.isTimeseries &&
|
||||
hintObj.firstElement().valueStringDataSafe() == IndexConstants::kIdIndexName) {
|
||||
// The index name '_id_' is reserved for the _id index on the underlying buckets
|
||||
// collection of a timeseries collection. The user will never be able to specify the
|
||||
// _id index name '_id_' in the hint.
|
||||
return Status(ErrorCodes::BadValue,
|
||||
"cannot hint on '_id_' for timeseries collections");
|
||||
} else {
|
||||
hintedIndexBson = hintObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// geoNear and text queries *require* an index.
|
||||
// Also, if a hint is specified it indicates that we MUST use it.
|
||||
|
|
|
|||
Loading…
Reference in New Issue