Skip to content

Commit 982ba21

Browse files
committed
SERVER-36697 Utilize TickSource for transactions timing metrics
This patch converts the existing transactions diagnostics timing related metrics to use a TickSource to record transaction durations. The TickSource is a high precision, mock-able time source for measuring the passage of time. This patch also converts the existing unit tests to use a mock TickSource, which allows the tests to fully virtualize time, making them much faster and less flaky.
1 parent 23d7c89 commit 982ba21

18 files changed

+633
-529
lines changed

src/mongo/db/SConscript

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1950,6 +1950,7 @@ env.CppUnitTest(
19501950
'$BUILD_DIR/mongo/client/read_preference',
19511951
'$BUILD_DIR/mongo/db/auth/authmocks',
19521952
'$BUILD_DIR/mongo/db/repl/mock_repl_coord_server_fixture',
1953+
'$BUILD_DIR/mongo/util/clock_source_mock',
19531954
'query_exec',
19541955
],
19551956
)

src/mongo/db/ftdc/ftdc_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ FTDCTest::FTDCTest() {
142142
auto service = getServiceContext();
143143
service->setFastClockSource(stdx::make_unique<ClockSourceMock>());
144144
service->setPreciseClockSource(stdx::make_unique<ClockSourceMock>());
145-
service->setTickSource(stdx::make_unique<TickSourceMock>());
145+
service->setTickSource(stdx::make_unique<TickSourceMock<>>());
146146
}
147147

148148
} // namespace mongo

src/mongo/db/operation_context_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ class OperationDeadlineTests : public unittest::Test {
225225
service = ServiceContext::make();
226226
service->setFastClockSource(stdx::make_unique<SharedClockSourceAdapter>(mockClock));
227227
service->setPreciseClockSource(stdx::make_unique<SharedClockSourceAdapter>(mockClock));
228-
service->setTickSource(stdx::make_unique<TickSourceMock>());
228+
service->setTickSource(stdx::make_unique<TickSourceMock<>>());
229229
client = service->makeClient("OperationDeadlineTest");
230230
}
231231

src/mongo/db/s/namespace_metadata_change_notifications_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const NamespaceString kNss("foo.bar");
4545
class NamespaceMetadataChangeNotificationsTest : public ServiceContextMongoDTest {
4646
protected:
4747
NamespaceMetadataChangeNotificationsTest() {
48-
getServiceContext()->setTickSource(stdx::make_unique<TickSourceMock>());
48+
getServiceContext()->setTickSource(stdx::make_unique<TickSourceMock<>>());
4949
}
5050
};
5151

src/mongo/db/single_transaction_stats.cpp

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,68 +32,67 @@
3232

3333
namespace mongo {
3434

35-
unsigned long long SingleTransactionStats::getStartTime() const {
36-
invariant(_startTime > 0);
37-
38-
return _startTime;
39-
}
40-
41-
void SingleTransactionStats::setStartTime(unsigned long long time) {
35+
void SingleTransactionStats::setStartTime(TickSource::Tick curTick, Date_t curWallClockTime) {
4236
invariant(_startTime == 0);
4337

44-
_startTime = time;
38+
_startTime = curTick;
39+
_startWallClockTime = curWallClockTime;
4540
}
4641

47-
unsigned long long SingleTransactionStats::getDuration(unsigned long long curTime) const {
42+
Microseconds SingleTransactionStats::getDuration(TickSource* tickSource,
43+
TickSource::Tick curTick) const {
4844
invariant(_startTime > 0);
4945

5046
// The transaction hasn't ended yet, so we return how long it has currently been running for.
5147
if (_endTime == 0) {
52-
return curTime - _startTime;
48+
return tickSource->ticksTo<Microseconds>(curTick - _startTime);
5349
}
54-
return _endTime - _startTime;
50+
return tickSource->ticksTo<Microseconds>(_endTime - _startTime);
5551
}
5652

57-
void SingleTransactionStats::setEndTime(unsigned long long time) {
53+
void SingleTransactionStats::setEndTime(TickSource::Tick time) {
5854
invariant(_startTime > 0);
5955

6056
_endTime = time;
6157
}
6258

63-
Microseconds SingleTransactionStats::getTimeActiveMicros(unsigned long long curTime) const {
59+
Microseconds SingleTransactionStats::getTimeActiveMicros(TickSource* tickSource,
60+
TickSource::Tick curTick) const {
6461
invariant(_startTime > 0);
6562

6663
// The transaction is currently active, so we return the recorded active time so far plus the
6764
// time since _timeActiveStart.
6865
if (isActive()) {
6966
return _timeActiveMicros +
70-
Microseconds{static_cast<long long>(curTime - _lastTimeActiveStart)};
67+
tickSource->ticksTo<Microseconds>(curTick - _lastTimeActiveStart);
7168
}
7269
return _timeActiveMicros;
7370
}
7471

75-
Microseconds SingleTransactionStats::getTimeInactiveMicros(unsigned long long curTime) const {
72+
Microseconds SingleTransactionStats::getTimeInactiveMicros(TickSource* tickSource,
73+
TickSource::Tick curTick) const {
7674
invariant(_startTime > 0);
7775

78-
return Microseconds{static_cast<long long>(getDuration(curTime))} -
79-
getTimeActiveMicros(curTime);
76+
return getDuration(tickSource, curTick) - getTimeActiveMicros(tickSource, curTick);
8077
}
8178

82-
void SingleTransactionStats::setActive(unsigned long long time) {
79+
void SingleTransactionStats::setActive(TickSource::Tick curTick) {
8380
invariant(!isActive());
8481

85-
_lastTimeActiveStart = time;
82+
_lastTimeActiveStart = curTick;
8683
}
8784

88-
void SingleTransactionStats::setInactive(unsigned long long time) {
85+
void SingleTransactionStats::setInactive(TickSource* tickSource, TickSource::Tick curTick) {
8986
invariant(isActive());
9087

91-
_timeActiveMicros += Microseconds{static_cast<long long>(time - _lastTimeActiveStart)};
88+
_timeActiveMicros += tickSource->ticksTo<Microseconds>(curTick - _lastTimeActiveStart);
9289
_lastTimeActiveStart = 0;
9390
}
9491

9592
void SingleTransactionStats::report(BSONObjBuilder* builder,
96-
const repl::ReadConcernArgs& readConcernArgs) const {
93+
const repl::ReadConcernArgs& readConcernArgs,
94+
TickSource* tickSource,
95+
TickSource::Tick curTick) const {
9796
BSONObjBuilder parametersBuilder(builder->subobjStart("parameters"));
9897
parametersBuilder.append("txnNumber", _txnNumber);
9998

@@ -108,15 +107,15 @@ void SingleTransactionStats::report(BSONObjBuilder* builder,
108107
parametersBuilder.done();
109108

110109
builder->append("readTimestamp", _readTimestamp);
111-
builder->append("startWallClockTime",
112-
dateToISOStringLocal(Date_t::fromMillisSinceEpoch(getStartTime() / 1000)));
110+
builder->append("startWallClockTime", dateToISOStringLocal(_startWallClockTime));
113111

114-
// We use the same "now" time so that the following time metrics are consistent with each other.
115-
auto curTime = curTimeMicros64();
116-
builder->append("timeOpenMicros", static_cast<long long>(getDuration(curTime)));
112+
// The same "now" time must be used so that the following time metrics are consistent with each
113+
// other.
114+
builder->append("timeOpenMicros",
115+
durationCount<Microseconds>(getDuration(tickSource, curTick)));
117116

118-
auto timeActive = durationCount<Microseconds>(getTimeActiveMicros(curTime));
119-
auto timeInactive = durationCount<Microseconds>(getTimeInactiveMicros(curTime));
117+
auto timeActive = durationCount<Microseconds>(getTimeActiveMicros(tickSource, curTick));
118+
auto timeInactive = durationCount<Microseconds>(getTimeInactiveMicros(tickSource, curTick));
120119

121120
builder->append("timeActiveMicros", timeActive);
122121
builder->append("timeInactiveMicros", timeInactive);

src/mongo/db/single_transaction_stats.h

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ namespace mongo {
3636

3737
/**
3838
* Tracks metrics for a single multi-document transaction.
39+
*
40+
* For all timing related stats, a TickSource with at least microsecond resolution must be used.
3941
*/
4042
class SingleTransactionStats {
4143
public:
@@ -64,19 +66,19 @@ class SingleTransactionStats {
6466
SingleTransactionStats() : _txnNumber(kUninitializedTxnNumber){};
6567
SingleTransactionStats(TxnNumber txnNumber) : _txnNumber(txnNumber){};
6668

67-
/**
68-
* Returns the start time of the transaction in microseconds.
69-
*
70-
* This method cannot be called until setStartTime() has been called.
71-
*/
72-
unsigned long long getStartTime() const;
73-
7469
/**
7570
* Sets the transaction's start time, only if it hasn't already been set.
7671
*
72+
* This method takes two sources of input for the current time. The 'curTick' argument should be
73+
* the current time as measured by a TickSource, which is a high precision interface for
74+
* measuring the passage of time that should provide at least microsecond resolution. The given
75+
* 'curWallClockTime' need only be a millisecond resolution time point that serves as a close
76+
* approximation to the true start time. This millisecond resolution time point is not used for
77+
* measuring transaction durations. The given tick value, however, is.
78+
*
7779
* This method must only be called once.
7880
*/
79-
void setStartTime(unsigned long long time);
81+
void setStartTime(TickSource::Tick curTick, Date_t curWallClockTime);
8082

8183
/**
8284
* If the transaction is currently in progress, this method returns the duration
@@ -87,42 +89,42 @@ class SingleTransactionStats {
8789
*
8890
* This method cannot be called until setStartTime() has been called.
8991
*/
90-
unsigned long long getDuration(unsigned long long curTime) const;
92+
Microseconds getDuration(TickSource* tickSource, TickSource::Tick curTick) const;
9193

9294
/**
9395
* Sets the transaction's end time, only if the start time has already been set.
9496
*
9597
* This method cannot be called until setStartTime() has been called.
9698
*/
97-
void setEndTime(unsigned long long time);
99+
void setEndTime(TickSource::Tick time);
98100

99101
/**
100102
* Returns the total active time of the transaction, given the current time value. A transaction
101103
* is active when there is a running operation that is part of the transaction.
102104
*/
103-
Microseconds getTimeActiveMicros(unsigned long long curTime) const;
105+
Microseconds getTimeActiveMicros(TickSource* tickSource, TickSource::Tick curTick) const;
104106

105107
/**
106108
* Returns the total inactive time of the transaction, given the current time value. A
107109
* transaction is inactive when it is idly waiting for a new operation to occur.
108110
*/
109-
Microseconds getTimeInactiveMicros(unsigned long long curTime) const;
111+
Microseconds getTimeInactiveMicros(TickSource* tickSource, TickSource::Tick curTick) const;
110112

111113
/**
112114
* Marks the transaction as active and sets the start of the transaction's active time.
113115
*
114116
* This method cannot be called if the transaction is currently active. A call to setActive()
115117
* must be followed by a call to setInactive() before calling setActive() again.
116118
*/
117-
void setActive(unsigned long long time);
119+
void setActive(TickSource::Tick curTick);
118120

119121
/**
120122
* Marks the transaction as inactive and sets the total active time of the transaction. The
121123
* total active time will only be set if the transaction was active prior to this call.
122124
*
123125
* This method cannot be called if the transaction is currently not active.
124126
*/
125-
void setInactive(unsigned long long time);
127+
void setInactive(TickSource* tickSource, TickSource::Tick curTick);
126128

127129
/**
128130
* Returns whether or not the transaction is currently active.
@@ -192,7 +194,10 @@ class SingleTransactionStats {
192194
/**
193195
* Append the stats to the builder.
194196
*/
195-
void report(BSONObjBuilder* builder, const repl::ReadConcernArgs& readConcernArgs) const;
197+
void report(BSONObjBuilder* builder,
198+
const repl::ReadConcernArgs& readConcernArgs,
199+
TickSource* tickSource,
200+
TickSource::Tick curTick) const;
196201

197202
private:
198203
// The transaction number of the transaction.
@@ -202,18 +207,23 @@ class SingleTransactionStats {
202207
// for future use.
203208
boost::optional<bool> _autoCommit;
204209

205-
// The start time of the transaction in microseconds.
206-
unsigned long long _startTime{0};
210+
// The start time of the transaction in millisecond resolution. Used only for diagnostics
211+
// reporting. Not used for measuring transaction durations.
212+
Date_t _startWallClockTime;
213+
214+
// The start time of the transaction. Note that tick values should only ever be used to measure
215+
// distance from other tick values, not for reporting absolute wall clock time.
216+
TickSource::Tick _startTime{0};
207217

208-
// The end time of the transaction in microseconds.
209-
unsigned long long _endTime{0};
218+
// The end time of the transaction.
219+
TickSource::Tick _endTime{0};
210220

211221
// The total amount of active time spent by the transaction.
212222
Microseconds _timeActiveMicros = Microseconds{0};
213223

214-
// The time at which the transaction was last marked as active in microseconds. The transaction
215-
// is considered active if this value is not equal to 0.
216-
unsigned long long _lastTimeActiveStart{0};
224+
// The time at which the transaction was last marked as active. The transaction is considered
225+
// active if this value is not equal to 0.
226+
TickSource::Tick _lastTimeActiveStart{0};
217227

218228
// The expiration date of the transaction.
219229
Date_t _expireDate = Date_t::max();

0 commit comments

Comments
 (0)