summaryrefslogtreecommitdiff
path: root/src/mongo/db/transaction_participant.cpp
diff options
context:
space:
mode:
authorA. Jesse Jiryu Davis <jesse@mongodb.com>2019-03-07 17:11:12 -0500
committerA. Jesse Jiryu Davis <jesse@mongodb.com>2019-03-21 21:22:24 -0400
commit78eaa3cf538764d5aa5a09c5997532a4c3b2bca8 (patch)
tree1b5fcc32ad4b9cc2369b9fcc7ae95be2b09da3f7 /src/mongo/db/transaction_participant.cpp
parent9fa4a356cc1d89adc1edd4321117503ce90e2d4b (diff)
downloadmongo-78eaa3cf538764d5aa5a09c5997532a4c3b2bca8.tar.gz
SERVER-39679 Get oldest transaction time when snapshotting
Diffstat (limited to 'src/mongo/db/transaction_participant.cpp')
-rw-r--r--src/mongo/db/transaction_participant.cpp69
1 files changed, 57 insertions, 12 deletions
diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp
index 7e5d3334b3d..16cec7c5e7a 100644
--- a/src/mongo/db/transaction_participant.cpp
+++ b/src/mongo/db/transaction_participant.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/transaction_participant.h"
+#include "mongo/db/catalog/database_holder.h"
#include "mongo/db/catalog/index_catalog.h"
#include "mongo/db/catalog_raii.h"
#include "mongo/db/commands/test_commands_enabled.h"
@@ -332,19 +333,63 @@ void TransactionParticipant::performNoopWrite(OperationContext* opCtx, StringDat
}
}
-boost::optional<Timestamp> TransactionParticipant::getOldestActiveTimestamp(
- OperationContext* opCtx) {
- DBDirectClient client(opCtx);
- Query q(BSON(SessionTxnRecord::kStateFieldName << "prepared"));
- q.sort(SessionTxnRecord::kStartOpTimeFieldName.toString());
- auto result = client.findOne(NamespaceString::kSessionTransactionsTableNamespace.ns(), q);
- if (result.isEmpty()) {
- return boost::none;
- }
+StorageEngine::OldestActiveTransactionTimestampResult
+TransactionParticipant::getOldestActiveTimestamp(Timestamp stableTimestamp) {
+ // Read from config.transactions at the stable timestamp for the oldest active transaction
+ // timestamp. Use a short timeout: another thread might have the global lock e.g. to shut down
+ // the server, and it both blocks this thread from querying config.transactions and waits for
+ // this thread to terminate.
+ auto client = getGlobalServiceContext()->makeClient("OldestActiveTxnTimestamp");
+ AlternativeClientRegion acr(client);
+
+ try {
+ auto opCtx = cc().makeOperationContext();
+ auto nss = NamespaceString::kSessionTransactionsTableNamespace;
+ auto deadline = Date_t::now() + Milliseconds(100);
+ Lock::DBLock dbLock(opCtx.get(), nss.db(), MODE_IS, deadline);
+ Lock::CollectionLock collLock(opCtx.get()->lockState(), nss.toString(), MODE_IS, deadline);
+
+ auto databaseHolder = DatabaseHolder::get(opCtx.get());
+ auto db = databaseHolder->getDb(opCtx.get(), nss.db());
+ if (!db) {
+ // There is no config database, so there cannot be any active transactions.
+ return boost::none;
+ }
+
+ auto collection = db->getCollection(opCtx.get(), nss);
+ if (!collection) {
+ return boost::none;
+ }
- auto txnRecord =
- SessionTxnRecord::parse(IDLParserErrorContext("parse oldest active txn record"), result);
- return txnRecord.getStartOpTime()->getTimestamp();
+ if (!stableTimestamp.isNull()) {
+ opCtx->recoveryUnit()->setTimestampReadSource(RecoveryUnit::ReadSource::kProvided,
+ stableTimestamp);
+ }
+
+ // Scan. We guess that occasional scans are cheaper than the write overhead of an index.
+ boost::optional<Timestamp> oldestTxnTimestamp;
+ auto cursor = collection->getCursor(opCtx.get());
+ while (auto record = cursor->next()) {
+ auto doc = record.get().data.toBson();
+ auto txnRecord = SessionTxnRecord::parse(
+ IDLParserErrorContext("parse oldest active txn record"), doc);
+ if (txnRecord.getState() != DurableTxnStateEnum::kPrepared) {
+ continue;
+ }
+
+ // A prepared transaction must have a start timestamp.
+ // TODO(SERVER-40013): Handle entries with state "prepared" and no "startTimestamp".
+ invariant(txnRecord.getStartOpTime());
+ auto ts = txnRecord.getStartOpTime()->getTimestamp();
+ if (!oldestTxnTimestamp || ts < oldestTxnTimestamp.value()) {
+ oldestTxnTimestamp = ts;
+ }
+ }
+
+ return oldestTxnTimestamp;
+ } catch (const DBException&) {
+ return exceptionToStatus();
+ }
}
const LogicalSessionId& TransactionParticipant::Observer::_sessionId() const {