Skip to content

Commit a24c919

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 4b11cca commit a24c919

3 files changed

Lines changed: 24 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 <functional>
89
#include <memory>
@@ -92,6 +93,10 @@ class HttpRequest : public WebSocketConnectionCallbacks,
9293
};
9394
LastHeaderState _last_header_state;
9495

96+
// Sys.time()-compatible timestamp, which can later be used to instrument
97+
// roundtrip latency in R.
98+
std::chrono::time_point<std::chrono::system_clock> _timestamp;
99+
95100
public:
96101
HttpRequest(uv_loop_t* pLoop,
97102
std::shared_ptr<WebApplication> pWebApplication,
@@ -106,7 +111,8 @@ class HttpRequest : public WebSocketConnectionCallbacks,
106111
_is_upgrade(false),
107112
_response_scheduled(false),
108113
_handling_request(false),
109-
_background_queue(backgroundQueue)
114+
_background_queue(backgroundQueue),
115+
_timestamp(std::chrono::system_clock::now())
110116
{
111117
ASSERT_BACKGROUND_THREAD()
112118
uv_tcp_init(pLoop, &_handle.tcp);
@@ -166,6 +172,9 @@ class HttpRequest : public WebSocketConnectionCallbacks,
166172
// pipelined HTTP requests.
167173
void requestCompleted();
168174

175+
// Returns timestamp compatible with R/Def.h's currentTime().
176+
double timestamp() const;
177+
169178
void _call_r_on_ws_open();
170179
void _schedule_on_headers_complete_complete(std::shared_ptr<HttpResponse> pResponse);
171180
void _on_headers_complete_complete(std::shared_ptr<HttpResponse> pResponse);

src/webapplication.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ void requestToEnv(std::shared_ptr<HttpRequest> pRequest, Rcpp::Environment* pEnv
150150
env["rook.version"] = CharacterVector("1.1-0");
151151
env["rook.url_scheme"] = CharacterVector("http");
152152

153+
NumericVector timestamp = NumericVector::create(pRequest->timestamp());
154+
timestamp.attr("class") = CharacterVector({"POSIXct", "POSIXt"});
155+
env["httpuv.timestamp"] = timestamp;
156+
153157
Address addr = pRequest->serverAddress();
154158
env["SERVER_NAME"] = CharacterVector(addr.host);
155159
std::ostringstream portstr;

0 commit comments

Comments
 (0)