in mcrouter/routes/LatencyInjectionRoute.h [108:194]
ReplyT<Request> route(const Request& req) const {
auto& proxy = fiber_local<RouterInfo>::getSharedCtx()->proxy();
const auto before_ms = getCurrentTimeInMs();
SCOPE_EXIT {
if (totalLatency_.count() > 0) {
auto elapsed =
std::chrono::milliseconds(getCurrentTimeInMs() - before_ms);
// Pad latency out to total latency configured.
if (totalLatency_ > elapsed) {
proxy.stats().increment(mcrouter::total_latency_injected_stat);
folly::fibers::Baton totalBaton;
totalBaton.try_wait_for(totalLatency_ - elapsed);
}
}
};
// Fixed latency added before request is sent.
if (beforeLatency_.count() > 0) {
proxy.stats().increment(mcrouter::before_latency_injected_stat);
folly::fibers::Baton beforeBaton;
beforeBaton.try_wait_for(beforeLatency_);
}
std::chrono::microseconds beforeReqLatency{0};
std::chrono::microseconds afterReqLatency{0};
std::optional<Request> newReq;
static folly::ThreadWheelTimekeeperHighRes timeKeeper;
// Both beforeLatencyUs and afterLatencyUs fields have to be defined
// for request specific latency injection to happen.
if constexpr (
HasBeforeLatencyUsTrait<Request>::value &&
HasAfterLatencyUsTrait<Request>::value) {
if (requestLatency_) {
if (req.beforeLatencyUs_ref().has_value()) {
beforeReqLatency =
std::chrono::microseconds(*req.beforeLatencyUs_ref());
}
if (req.afterLatencyUs_ref().has_value()) {
afterReqLatency =
std::chrono::microseconds(*req.afterLatencyUs_ref());
}
// Reset request latencies to avoid downstream latency injection
if (beforeReqLatency.count() > 0 || afterReqLatency.count() > 0) {
newReq.emplace(req);
newReq->afterLatencyUs_ref() = 0;
newReq->beforeLatencyUs_ref() = 0;
}
// Inject per request latency if enabled
if (beforeReqLatency.count() > 0 &&
((maxRequestLatency_.count() == 0) ||
(beforeReqLatency.count() <= maxRequestLatency_.count()))) {
proxy.stats().increment(
mcrouter::before_request_latency_injected_stat);
fiber_local<RouterInfo>::accumulateBeforeReqInjectedLatencyUs(
beforeReqLatency.count());
folly::futures::sleep(beforeReqLatency, &timeKeeper).get();
}
}
}
auto& reqToSend = newReq ? *newReq : req;
auto reply = rh_->route(reqToSend);
// Optimize out the logic that is related to request level latency injection
// for protocols that does not support it
if constexpr (HasAfterLatencyUsTrait<Request>::value) {
if (afterReqLatency.count() > 0 &&
((maxRequestLatency_.count() == 0) ||
(afterReqLatency.count() <= maxRequestLatency_.count()))) {
proxy.stats().increment(mcrouter::after_request_latency_injected_stat);
fiber_local<RouterInfo>::accumulateAfterReqInjectedLatencyUs(
afterReqLatency.count());
folly::futures::sleep(afterReqLatency, &timeKeeper).get();
}
}
// Fixed latency added after reply is received.
if (afterLatency_.count() > 0) {
proxy.stats().increment(mcrouter::after_latency_injected_stat);
folly::fibers::Baton afterBaton;
afterBaton.try_wait_for(afterLatency_);
}
return reply;
}