Files
mongo/buildscripts/antithesis/test_composer/basic_js_commands/js/commands.js
Trevor Guidry 61d4116416 SERVER-116370 Small antithesis test composer fixes (#46030)
GitOrigin-RevId: 374cd03d285d01ddbe03a297edf5fff452cb1b8d
2026-01-09 16:37:59 +00:00

149 lines
5.3 KiB
JavaScript
Executable File

const MONGOD_URI = connection_string;
const MAX_RETRIES = 180; // 3 minutes
// Executes the given function, retrying if it fails
// due to the network being unreachable (MongoNetworkError)
// or cluster unavailable (MongoServerSelectionError)
// or retryable write error.
function retryOnFailure(func) {
let retries = 0;
while (true) {
try {
const result = func();
return result;
} catch (err) {
if (retries++ >= MAX_RETRIES) {
print(`failed (exhausted all retries): ${JSON.stringify(err)}`);
throw err;
}
if (
err.name === "MongoServerSelectionError" ||
err.name === "MongoNetworkError" ||
err.name === "MongoNetworkTimeoutError" ||
err.name === "MongoPoolClearedError" ||
err.name === "PoolClearedOnNetworkError" ||
err.name === "InterruptedDueToReplStateChange" ||
err.message === "read ECONNRESET" ||
err.message === "read ETIMEDOUT" ||
err.message === "Shutting down" ||
err.message.startsWith("network error while attempting to run command")
) {
print(`Attempt ${retries} failed due to ${err.name}, retrying in 1 second...`);
sleep(1000);
continue;
}
if (
Array.isArray(err.errorResponse?.errorLabels) &&
err.errorResponse.errorLabels.includes("RetryableWriteError")
) {
print(`Attempt ${retries} failed due to ${err.codeName}, retrying in 1 second...`);
sleep(1000);
continue;
}
// Some other non-retryable issue, re-throw
print(`failed due to unretryable error: ${JSON.stringify(err)}`);
throw err;
}
}
}
// Exclusive upper bound (0 to max-1)
function randomInt(max) {
return Math.floor(Math.random() * max);
}
// Establishes a conenction
function getDB() {
return retryOnFailure(() => new Mongo(MONGOD_URI).getDB("test"));
}
function find() {
const db = getDB();
print("executing find");
for (var i = 0; i < 100; i++) {
const val = Math.floor(Math.random() * 10000) + 1;
retryOnFailure(() => db.test.findOne({x: {$gt: val}}));
}
print("done executing find");
}
function fsync() {
const db = getDB();
print("executing fsync");
const result = retryOnFailure(() => db.adminCommand({fsync: 1}));
print("done executing fsync. result:", JSON.stringify(result));
assert(result.ok);
}
function insert() {
const db = getDB();
for (var i = 0; i < 100; i++) {
const val = Math.floor(Math.random() * 10000) + 1;
retryOnFailure(() => db.test.insertOne({x: val}));
}
}
// Executes a write, then a point-in-time read
// to fetch the older state of the doc prior to the write.
function pitRead() {
// Set up initial state, a doc with field "value":0
const db = getDB();
const _id = ObjectId();
retryOnFailure(() => {
db.createCollection("test_snapshots");
db.test_snapshots.updateOne({_id}, {$set: {value: 0}}, {upsert: true, writeConcern: {w: "majority"}});
});
// Execute an update (set value=1) and capture its timestamp as t1
const t1 = retryOnFailure(() => {
const ses1 = db.getMongo().startSession();
const ses1Coll = ses1.getDatabase(db.getName()).test_snapshots;
ses1Coll.updateOne({_id}, {$set: {value: 1}}, {writeConcern: {w: "majority"}});
const t1 = ses1.getOperationTime();
db.test_snapshots.updateOne({_id}, {$set: {value: 2}}, {writeConcern: {w: "majority"}});
return t1;
});
// Sleep for a random period between 0 and 30 seconds.
// The snapshot history window is configured to 10 seconds,
// so sometimes this will attempt a read outside the window.
sleep(randomInt(30) * 1000);
let snapshotReadResult;
try {
snapshotReadResult = retryOnFailure(() => {
// Do a snapshot read to get the document's state as of t1
return db.runCommand({
find: "test_snapshots",
filter: {_id},
readConcern: {level: "snapshot", atClusterTime: t1},
});
});
} catch (e) {
if (e.codeName === "SnapshotTooOld") {
// This case is expected sometimes, so exit cleanly.
return;
}
throw e;
}
// Assert that the snapshot read actually returned the version of the
// doc at the older value (1), not the newer one (2).
assert(JSON.stringify(snapshotReadResult.cursor.firstBatch) == JSON.stringify([{_id, value: 1}]));
}
function validateCollections() {
const db = getDB();
const dbs = retryOnFailure(() => db.adminCommand("listDatabases").databases.map((x) => x.name));
dbs.forEach((dbName) => {
const collectionNames = retryOnFailure(() => db.getSiblingDB(dbName).getCollectionNames());
collectionNames.forEach((coll) => {
print(`validating ${dbName}.${coll}...`);
const validateResult = retryOnFailure(() => db.getSiblingDB(dbName).getCollection(coll).validate());
assert(validateResult.valid, "collection is not valid");
});
});
}