summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Noma <gregory.noma@gmail.com>2021-03-25 12:46:26 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-03-25 17:26:18 +0000
commit75d3a439ea77e6f8bd9bed2b4f397803b66c2949 (patch)
tree799eaf1cdeaec65618435981ae41e95ef69e6af7
parent1a2e420059f71f3b486f389fdb1463d927ffac58 (diff)
downloadmongo-75d3a439ea77e6f8bd9bed2b4f397803b66c2949.tar.gz
SERVER-55136 Clean up invalid state on failed time-series insert
-rw-r--r--jstests/noPassthrough/timeseries_insert_after_failed_insert.js51
-rw-r--r--src/mongo/db/commands/write_commands.cpp15
2 files changed, 60 insertions, 6 deletions
diff --git a/jstests/noPassthrough/timeseries_insert_after_failed_insert.js b/jstests/noPassthrough/timeseries_insert_after_failed_insert.js
new file mode 100644
index 00000000000..bc95cc35a8b
--- /dev/null
+++ b/jstests/noPassthrough/timeseries_insert_after_failed_insert.js
@@ -0,0 +1,51 @@
+/**
+ * Tests that a failed time-series insert does not leave behind any invalid state.
+ */
+(function() {
+'use strict';
+
+load('jstests/core/timeseries/libs/timeseries.js');
+load('jstests/libs/fail_point_util.js');
+
+const conn = MongoRunner.runMongod();
+
+if (!TimeseriesTest.timeseriesCollectionsEnabled(conn)) {
+ jsTestLog('Skipping test because the time-series collection feature flag is disabled');
+ MongoRunner.stopMongod(conn);
+ return;
+}
+
+const testDB = conn.getDB(jsTestName());
+
+const coll = testDB.getCollection('t');
+const bucketsColl = testDB.getCollection('system.buckets.' + coll.getName());
+
+const timeFieldName = 'time';
+const metaFieldName = 'meta';
+
+coll.drop();
+assert.commandWorked(testDB.createCollection(
+ coll.getName(), {timeseries: {timeField: timeFieldName, metaField: metaFieldName}}));
+assert.contains(bucketsColl.getName(), testDB.getCollectionNames());
+
+const docs = [
+ {_id: 0, meta: 'fail', [timeFieldName]: ISODate()},
+ {_id: 1, meta: 'fail', [timeFieldName]: ISODate()},
+];
+
+const fp = configureFailPoint(conn, 'failTimeseriesInsert', {metadata: 'fail'});
+assert.commandFailed(coll.insert(docs[0], {ordered: false}));
+fp.off();
+
+// Insert a document that belongs in the same bucket that the failed insert woulld have gone into.
+assert.commandWorked(coll.insert(docs[1], {ordered: false}));
+
+// There should not be any leftover state from the failed insert.
+assert.docEq(coll.find().toArray(), [docs[1]]);
+const buckets = bucketsColl.find().sort({['control.min.' + timeFieldName]: 1}).toArray();
+jsTestLog('Checking buckets: ' + tojson(buckets));
+assert.eq(buckets.length, 1);
+assert.eq(buckets[0].control.min._id, docs[1]._id);
+
+MongoRunner.stopMongod(conn);
+})(); \ No newline at end of file
diff --git a/src/mongo/db/commands/write_commands.cpp b/src/mongo/db/commands/write_commands.cpp
index d2a9c2fa518..ae0f2d0298a 100644
--- a/src/mongo/db/commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands.cpp
@@ -612,16 +612,19 @@ public:
? _performTimeseriesInsert(opCtx, batch, metadata, stmtIds)
: _performTimeseriesUpdate(opCtx, batch, metadata, stmtIds);
- if (batch->numPreviouslyCommittedMeasurements() != 0 && result.isOK() &&
- result.getValue().getNModified() == 0) {
- // No bucket was found to update, meaning that it was manually removed.
+ if (auto error = generateError(opCtx, result, start + index, errors->size())) {
+ errors->push_back(*error);
bucketCatalog.abort(batch);
- updatesToRetry->push_back(index);
return;
}
- if (auto error = generateError(opCtx, result, start + index, errors->size())) {
- errors->push_back(*error);
+ if (batch->numPreviouslyCommittedMeasurements() != 0 &&
+ result.getValue().getNModified() == 0) {
+ // No document in the buckets collection was found to update, meaning that it was
+ // removed.
+ bucketCatalog.abort(batch);
+ updatesToRetry->push_back(index);
+ return;
}
auto* replCoord = repl::ReplicationCoordinator::get(opCtx->getServiceContext());