mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-27 23:27:11 +01:00
5a975d960a
GitOrigin-RevId: 1a0f3a6919e31e11e3cc1d2b7d8b27b9cfe6cf21
189 lines
7.5 KiB
JavaScript
189 lines
7.5 KiB
JavaScript
/**
|
|
* Tests retrying of time-series delete and update operations that are eligible for retryable writes
|
|
* (specifically single deletes and updates).
|
|
*
|
|
* 'setUpCollection' is a function which performs any necessary set up for the collection after it
|
|
* has already been created.
|
|
*
|
|
* 'checkRetriedCommandsCount' is a function which checks whether the retriedStatementsCount
|
|
* statistic has the expected value and returns the amount by which that statistic was expected to
|
|
* increment.
|
|
*
|
|
* 'retriedStatementsCount' is a function which checks whether the retriedStatementsCount statistic
|
|
* has the expected value and returns the amount by which that statistic was expected to increment.
|
|
*/
|
|
export function runTimeseriesRetryDeleteAndUpdateTest(
|
|
conn, setUpCollection, checkRetriedCommandsCount, checkRetriedStatementsCount) {
|
|
const timeFieldName = 'time';
|
|
const metaFieldName = 'tag';
|
|
const dateTime = ISODate("2021-07-12T16:00:00Z");
|
|
let collCount = 0;
|
|
|
|
let retriedCommandsCount = 0;
|
|
let retriedStatementsCount = 0;
|
|
|
|
/**
|
|
* Verifies that a timeseries delete or update command supports retryable writes. The arguments
|
|
* to this function are an array of documents to insert initially, a command builder function
|
|
* that returns the command object given the collection to run it on, and a validate function
|
|
* that validates the result after the command has been applied to the collection.
|
|
*/
|
|
const runTest = function(
|
|
initialDocs, cmdBuilderFn, validateFn, expectError = false, statementsRetried = 1) {
|
|
const session = conn.startSession({retryWrites: true});
|
|
const testDB = session.getDatabase(jsTestName());
|
|
|
|
const coll = testDB.getCollection('timeseries_retry_delete_and_update_' + collCount++);
|
|
coll.drop();
|
|
if (conn.isMongos()) {
|
|
// TODO (SERVER-86443): Use create instead of createUnsplittableCollection
|
|
// command once any collection becomes tracked upon creation
|
|
assert.commandWorked(testDB.runCommand({
|
|
createUnsplittableCollection: coll.getName(),
|
|
timeseries: {timeField: timeFieldName, metaField: metaFieldName}
|
|
}));
|
|
} else {
|
|
assert.commandWorked(testDB.createCollection(
|
|
coll.getName(),
|
|
{timeseries: {timeField: timeFieldName, metaField: metaFieldName}}));
|
|
}
|
|
setUpCollection(testDB, coll, metaFieldName);
|
|
|
|
assert.commandWorked(testDB.runCommand({
|
|
insert: coll.getName(),
|
|
documents: initialDocs,
|
|
lsid: session.getSessionId(),
|
|
txnNumber: NumberLong(0),
|
|
}));
|
|
|
|
// For retryable writes, the server uses 'txnNumber' as the key to look up previously
|
|
// executed operations in the session.
|
|
let cmdObj = cmdBuilderFn(coll);
|
|
cmdObj["lsid"] = session.getSessionId();
|
|
cmdObj["txnNumber"] = NumberLong(1);
|
|
|
|
if (expectError) {
|
|
assert.commandFailedWithCode(testDB.runCommand(cmdObj), ErrorCodes.InvalidOptions);
|
|
assert.commandFailedWithCode(testDB.runCommand(cmdObj), ErrorCodes.InvalidOptions);
|
|
} else {
|
|
assert.commandWorked(testDB.runCommand(cmdObj),
|
|
'Failed to write bucket on first write');
|
|
assert.commandWorked(testDB.runCommand(cmdObj),
|
|
'Failed to write bucket on retry write');
|
|
}
|
|
|
|
validateFn(coll);
|
|
|
|
retriedCommandsCount +=
|
|
checkRetriedCommandsCount(testDB, retriedCommandsCount, statementsRetried);
|
|
retriedStatementsCount +=
|
|
checkRetriedStatementsCount(testDB, retriedStatementsCount, statementsRetried);
|
|
|
|
session.endSession();
|
|
};
|
|
|
|
const allDocumentsSameBucket = [
|
|
{[timeFieldName]: dateTime, [metaFieldName]: "A", f: 100},
|
|
{[timeFieldName]: dateTime, [metaFieldName]: "A", f: 101},
|
|
{[timeFieldName]: dateTime, [metaFieldName]: "A", f: 102},
|
|
];
|
|
const allDocumentsDifferentBuckets = [
|
|
{[timeFieldName]: dateTime, [metaFieldName]: "A", f: 100},
|
|
{[timeFieldName]: dateTime, [metaFieldName]: "B", f: 101},
|
|
{[timeFieldName]: dateTime, [metaFieldName]: "C", f: 102},
|
|
];
|
|
|
|
function deleteCmdBuilderFn(coll) {
|
|
return {delete: coll.getName(), deletes: [{q: {}, limit: 1}]};
|
|
}
|
|
function deleteValidateFn(coll) {
|
|
assert.eq(coll.countDocuments({}), 2, "Expected exactly one document to be deleted.");
|
|
}
|
|
|
|
(function testPartialBucketDelete() {
|
|
runTest(allDocumentsSameBucket, deleteCmdBuilderFn, deleteValidateFn);
|
|
})();
|
|
(function testFullBucketDelete() {
|
|
runTest(allDocumentsDifferentBuckets, deleteCmdBuilderFn, deleteValidateFn);
|
|
})();
|
|
|
|
function updateCmdBuilderFn(coll) {
|
|
return {
|
|
update: coll.getName(),
|
|
updates: [
|
|
{q: {}, u: {$inc: {updated: 1}}, multi: false},
|
|
{q: {}, u: {$inc: {updated: 1}}, multi: true},
|
|
{q: {}, u: {$inc: {anotherUpdated: 1}}, multi: false},
|
|
],
|
|
};
|
|
}
|
|
function updateCmdUnorderedBuilderFn(coll) {
|
|
let updateCmd = updateCmdBuilderFn(coll);
|
|
updateCmd["ordered"] = false;
|
|
return updateCmd;
|
|
}
|
|
function updateValidateFn(coll) {
|
|
assert.eq(coll.countDocuments({updated: {$exists: true}}),
|
|
1,
|
|
"Expected exactly one document to be updated.");
|
|
assert.eq(
|
|
coll.countDocuments({updated: 1}), 1, "Expected document to be updated only once.");
|
|
}
|
|
function updateUnorderedValidateFn(coll) {
|
|
updateValidateFn(coll);
|
|
assert.eq(coll.countDocuments({anotherUpdated: {$exists: true}}),
|
|
1,
|
|
"Expected exactly one document to be updated.");
|
|
assert.eq(coll.countDocuments({anotherUpdated: 1}),
|
|
1,
|
|
"Expected document to be updated only once.");
|
|
}
|
|
|
|
(function testPartialBucketUpdate() {
|
|
runTest(allDocumentsSameBucket,
|
|
updateCmdBuilderFn,
|
|
updateValidateFn,
|
|
/*expectError=*/ true);
|
|
})();
|
|
(function testFullBucketUpdate() {
|
|
runTest(allDocumentsDifferentBuckets,
|
|
updateCmdBuilderFn,
|
|
updateValidateFn,
|
|
/*expectError=*/ true);
|
|
})();
|
|
(function testPartialBucketUpdateUnordered() {
|
|
runTest(allDocumentsSameBucket,
|
|
updateCmdUnorderedBuilderFn,
|
|
updateUnorderedValidateFn,
|
|
/*expectError=*/ true,
|
|
/*statementRetried=*/ 2);
|
|
})();
|
|
(function testFullBucketUpdateUnordered() {
|
|
runTest(allDocumentsDifferentBuckets,
|
|
updateCmdUnorderedBuilderFn,
|
|
updateUnorderedValidateFn,
|
|
/*expectError=*/ true,
|
|
/*statementRetried=*/ 2);
|
|
})();
|
|
|
|
function upsertCmdBuilderFn(coll) {
|
|
return {
|
|
update: coll.getName(),
|
|
updates: [{
|
|
q: {[timeFieldName]: dateTime, [metaFieldName]: "B"},
|
|
u: {$inc: {updated: 1}},
|
|
multi: false,
|
|
upsert: true,
|
|
}],
|
|
};
|
|
}
|
|
function upsertValidateFn(coll) {
|
|
assert.eq(coll.countDocuments({[metaFieldName]: "B", updated: 1}),
|
|
1,
|
|
"Expected exactly one document to be upserted once.");
|
|
}
|
|
(function testUpsert() {
|
|
runTest(allDocumentsSameBucket, upsertCmdBuilderFn, upsertValidateFn);
|
|
})();
|
|
}
|