Skip to content

Commit b16a5f4

Browse files
committed
Add a timestamp to the Rook request object.
Requests objects now have a `httpuv.timestamp` member containing a timestamp compatible with Sys.time(). This timestamp is captured when the request is received at the C++ level in the background thread, which has two advantages: 1. We can much more accurately capture the total time the client actually waits for the request to get processed. 2. We can instrument the time it takes before the R-level callback actually runs. More broadly, the timestamp makes it easier to log measures of request latency in httpuv-based applications, and reduces the overhead of common ways to do so -- e.g. using Plumber hooks. Signed-off-by: Aaron Jacobs <aaron.jacobs@crescendotechnology.com>
1 parent 622c76a commit b16a5f4

3 files changed

Lines changed: 21 additions & 1 deletion

File tree

src/httprequest.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,16 @@ std::string HttpRequest::url() const {
176176
return _url;
177177
}
178178

179+
double HttpRequest::timestamp() const {
180+
// According to the std::chrono docs, we need at least 55 bit here.
181+
long long since_epoch = std::chrono::duration_cast<std::chrono::microseconds>(
182+
_timestamp.time_since_epoch()
183+
).count();
184+
// R's currentTime() returns a Unix timestamp with microseconds (or
185+
// nanoseconds on supported platforms) tacked on.
186+
return ((double) since_epoch) / 1e6;
187+
}
188+
179189
const RequestHeaders& HttpRequest::headers() const {
180190
return _headers;
181191
}

src/httprequest.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <map>
55
#include <iostream>
6+
#include <chrono>
67

78
#include <boost/function.hpp>
89
#include <boost/bind.hpp>
@@ -82,6 +83,10 @@ class HttpRequest : public WebSocketConnectionCallbacks,
8283
// to schedule callbacks to run on the background thread.
8384
CallbackQueue* _background_queue;
8485

86+
// Sys.time()-compatible timestamp, which can later be used to instrument
87+
// roundtrip latency in R.
88+
std::chrono::time_point<std::chrono::system_clock> _timestamp;
89+
8590
public:
8691
HttpRequest(uv_loop_t* pLoop,
8792
boost::shared_ptr<WebApplication> pWebApplication,
@@ -96,7 +101,8 @@ class HttpRequest : public WebSocketConnectionCallbacks,
96101
_is_upgrade(false),
97102
_response_scheduled(false),
98103
_handling_request(false),
99-
_background_queue(backgroundQueue)
104+
_background_queue(backgroundQueue),
105+
_timestamp(std::chrono::system_clock::now())
100106
{
101107
ASSERT_BACKGROUND_THREAD()
102108
uv_tcp_init(pLoop, &_handle.tcp);
@@ -154,6 +160,9 @@ class HttpRequest : public WebSocketConnectionCallbacks,
154160
// pipelined HTTP requests.
155161
void requestCompleted();
156162

163+
// Returns timestamp compatible with R/Def.h's currentTime().
164+
double timestamp() const;
165+
157166
void _call_r_on_ws_open();
158167
void _schedule_on_headers_complete_complete(boost::shared_ptr<HttpResponse> pResponse);
159168
void _on_headers_complete_complete(boost::shared_ptr<HttpResponse> pResponse);

src/webapplication.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ void requestToEnv(boost::shared_ptr<HttpRequest> pRequest, Rcpp::Environment* pE
150150

151151
env["rook.version"] = CharacterVector("1.1-0");
152152
env["rook.url_scheme"] = CharacterVector("http");
153+
env["httpuv.timestamp"] = NumericVector::create(pRequest->timestamp());
153154

154155
Address addr = pRequest->serverAddress();
155156
env["SERVER_NAME"] = CharacterVector(addr.host);

0 commit comments

Comments
 (0)