diff --git a/CHANGES b/CHANGES index f44cc15e..5648234f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ + * delay() can return milliseconds to delay sending next request. + wrk 4.0.0 * The wrk global variable is the only global defined by default. diff --git a/SCRIPTING b/SCRIPTING index 7e9c3f07..c15a03e4 100644 --- a/SCRIPTING +++ b/SCRIPTING @@ -38,6 +38,7 @@ Overview global setup -- called during thread setup global init -- called when the thread is starting + global delay -- called to get the request delay global request -- called to generate the HTTP request global response -- called with HTTP response data global done -- called with results of run @@ -64,6 +65,7 @@ Setup Running function init(args) + function delay() function request() function response(status, headers, body) @@ -73,6 +75,9 @@ Running The init() function receives any extra command line arguments for the script which must be separated from wrk arguments with "--". + delay() returns the number of milliseconds to delay sending the next + request. + request() returns a string containing the HTTP request. Building a new request each time is expensive, when testing a high performance server one solution is to pre-generate all requests in init() and do a quick diff --git a/scripts/delay.lua b/scripts/delay.lua new file mode 100644 index 00000000..a9897cca --- /dev/null +++ b/scripts/delay.lua @@ -0,0 +1,6 @@ +-- example script that demonstrates adding a random +-- 10-50ms delay before each request + +function delay() + return math.random(10, 50) +end diff --git a/src/script.c b/src/script.c index a8a4d571..8c90564c 100644 --- a/src/script.c +++ b/src/script.c @@ -141,6 +141,14 @@ void script_init(lua_State *L, thread *t, int argc, char **argv) { lua_pop(t->L, 1); } +uint64_t script_delay(lua_State *L) { + lua_getglobal(L, "delay"); + lua_call(L, 0, 1); + uint64_t delay = lua_tonumber(L, -1); + lua_pop(L, 1); + return delay; +} + void script_request(lua_State *L, char **buf, size_t *len) { int pop = 1; lua_getglobal(L, "request"); @@ -189,6 +197,10 @@ bool script_want_response(lua_State *L) { return script_is_function(L, "response"); } +bool script_has_delay(lua_State *L) { + return script_is_function(L, "delay"); +} + bool script_has_done(lua_State *L) { return script_is_function(L, "done"); } diff --git a/src/script.h b/src/script.h index f6861d4d..1bf820da 100644 --- a/src/script.h +++ b/src/script.h @@ -16,12 +16,14 @@ void script_setup(lua_State *, thread *); void script_done(lua_State *, stats *, stats *); void script_init(lua_State *, thread *, int, char **); +uint64_t script_delay(lua_State *); void script_request(lua_State *, char **, size_t *); void script_response(lua_State *, int, buffer *, buffer *); size_t script_verify_request(lua_State *L); bool script_is_static(lua_State *); bool script_want_response(lua_State *L); +bool script_has_delay(lua_State *L); bool script_has_done(lua_State *L); void script_summary(lua_State *, uint64_t, uint64_t, uint64_t); void script_errors(lua_State *, errors *); diff --git a/src/wrk.c b/src/wrk.c index cd99fa97..b0208b06 100644 --- a/src/wrk.c +++ b/src/wrk.c @@ -5,13 +5,14 @@ #include "main.h" static struct config { - uint64_t threads; uint64_t connections; uint64_t duration; + uint64_t threads; uint64_t timeout; uint64_t pipeline; - bool latency; + bool delay; bool dynamic; + bool latency; char *script; SSL_CTX *ctx; } cfg; @@ -107,7 +108,8 @@ int main(int argc, char **argv) { if (i == 0) { cfg.pipeline = script_verify_request(t->L); - cfg.dynamic = !script_is_static(t->L); + cfg.dynamic = !script_is_static(t->L); + cfg.delay = script_has_delay(t->L); if (script_want_response(t->L)) { parser_settings.on_header_field = header_field; parser_settings.on_header_value = header_value; @@ -212,6 +214,7 @@ void *thread_main(void *arg) { c->ssl = cfg.ctx ? SSL_new(cfg.ctx) : NULL; c->request = request; c->length = length; + c->delayed = cfg.delay; connect_socket(thread, c); } @@ -282,6 +285,13 @@ static int record_rate(aeEventLoop *loop, long long id, void *data) { return RECORD_INTERVAL_MS; } +static int delay_request(aeEventLoop *loop, long long id, void *data) { + connection *c = data; + c->delayed = false; + aeCreateFileEvent(loop, c->fd, AE_WRITABLE, socket_writeable, c); + return AE_NOMORE; +} + static int header_field(http_parser *parser, const char *at, size_t len) { connection *c = parser->data; if (c->state == VALUE) { @@ -331,6 +341,7 @@ static int response_complete(http_parser *parser) { if (!stats_record(statistics.latency, now - c->start)) { thread->errors.timeout++; } + c->delayed = cfg.delay; aeCreateFileEvent(thread->loop, c->fd, AE_WRITABLE, socket_writeable, c); } @@ -371,6 +382,13 @@ static void socket_writeable(aeEventLoop *loop, int fd, void *data, int mask) { connection *c = data; thread *thread = c->thread; + if (c->delayed) { + uint64_t delay = script_delay(thread->L); + aeDeleteFileEvent(loop, fd, AE_WRITABLE); + aeCreateTimeEvent(loop, delay, delay_request, c, NULL); + return; + } + if (!c->written) { if (cfg.dynamic) { script_request(thread->L, &c->request, &c->length); diff --git a/src/wrk.h b/src/wrk.h index af64ee64..e5036761 100644 --- a/src/wrk.h +++ b/src/wrk.h @@ -51,6 +51,7 @@ typedef struct connection { } state; int fd; SSL *ssl; + bool delayed; uint64_t start; char *request; size_t length;