Synchronous I/O for http request/response fixes 99%tile latencies

* Async I/O is being used for reading and writing http request and
response respectively. This causes the worker core to steal new tasks as
when one sandbox blocks on "read" or "write". This causes true
round-robin on timer interrupts, leading to those high latencies.
* Ideally, the request/response should be synchronous I belive because
we are expected to have "low"/near-realtime latencies and the read/writes
are only performed when a client connects, so ideally not block!
Plus, we expect only small data transfers (in KBs) so that should also
support synchronous request/response processing.
main
phani 5 years ago
parent f8cc5edb89
commit 9528f65b32

@ -16,6 +16,7 @@ CFLAGS += -D_GNU_SOURCE
#CFLAGS += -DNOSTDIO #CFLAGS += -DNOSTDIO
#CFLAGS += -DSTANDALONE #CFLAGS += -DSTANDALONE
CFLAGS += -DUSE_UVIO CFLAGS += -DUSE_UVIO
#CFLAGS += -DUSE_HTTP_UVIO -DUSE_HTTP_SYNC
CFLAGS += -DSBOX_SCALE_ALLOC CFLAGS += -DSBOX_SCALE_ALLOC
#CFLAGS += -DUSE_SYSCALL #CFLAGS += -DUSE_SYSCALL
#CFLAGS += -DPREEMPT_DISABLE #CFLAGS += -DPREEMPT_DISABLE

@ -4,6 +4,7 @@
#include <http_parser.h> #include <http_parser.h>
#include <types.h> #include <types.h>
#include <uv.h> #include <uv.h>
#include <sys/uio.h>
/* all in-memory ptrs.. don't mess around with that! */ /* all in-memory ptrs.. don't mess around with that! */
struct http_header { struct http_header {
@ -34,7 +35,11 @@ struct http_response {
int bodylen; int bodylen;
char *status; char *status;
int stlen; int stlen;
#ifdef USE_HTTP_UVIO
uv_buf_t bufs[HTTP_HEADERS_MAX * 2 + 3]; //max headers, one line for status code, remaining for body! uv_buf_t bufs[HTTP_HEADERS_MAX * 2 + 3]; //max headers, one line for status code, remaining for body!
#else
struct iovec bufs[HTTP_HEADERS_MAX * 2 + 3];
#endif
}; };
#endif /* SFRT_HTTP_H */ #endif /* SFRT_HTTP_H */

@ -8,7 +8,7 @@ int http_request_parse_sb(struct sandbox *s, size_t l);
int http_response_header_set_sb(struct sandbox *s, char *h, int len); int http_response_header_set_sb(struct sandbox *s, char *h, int len);
int http_response_body_set_sb(struct sandbox *s, char *body, int len); int http_response_body_set_sb(struct sandbox *s, char *body, int len);
int http_response_status_set_sb(struct sandbox *s, char *status, int len); int http_response_status_set_sb(struct sandbox *s, char *status, int len);
int http_response_uv_sb(struct sandbox *s); int http_response_vector_sb(struct sandbox *s);
void http_init(void); void http_init(void);
@ -37,9 +37,9 @@ http_response_status_set(char *status, int len)
} }
static inline int static inline int
http_response_uv(void) http_response_vector(void)
{ {
return http_response_uv_sb(sandbox_current()); return http_response_vector_sb(sandbox_current());
} }
static inline int static inline int

@ -193,9 +193,12 @@ sandbox_args(void)
//void sandbox_run(struct sandbox *s); //void sandbox_run(struct sandbox *s);
void *sandbox_run_func(void *data); void *sandbox_run_func(void *data);
struct sandbox *sandbox_schedule(void); struct sandbox *sandbox_schedule(int interrupt);
void sandbox_block(void); void sandbox_block(void);
void sandbox_wakeup(sandbox_t *sb); void sandbox_wakeup(sandbox_t *sb);
// called in sandbox_entry() before and after fn() execution
// for http request/response processing using uvio
void sandbox_block_http(void);
void sandbox_response(void); void sandbox_response(void);
// should be the entry-point for each sandbox so it can do per-sandbox mem/etc init. // should be the entry-point for each sandbox so it can do per-sandbox mem/etc init.

@ -116,7 +116,7 @@ typedef enum {
#define JSON_ELE_MAX 16 #define JSON_ELE_MAX 16
// FIXME: some naive work-stealing here.. // FIXME: some naive work-stealing here..
#define SBOX_PULL_MAX 16 #define SBOX_PULL_MAX 1
#define SBOX_MAX_OPEN 32 #define SBOX_MAX_OPEN 32
#define SBOX_PREOPEN_MAGIC (707707707) // reads lol lol lol upside down #define SBOX_PREOPEN_MAGIC (707707707) // reads lol lol lol upside down

@ -163,12 +163,13 @@ http_response_status_set_sb(struct sandbox *c, char *status, int len)
} }
int int
http_response_uv_sb(struct sandbox *c) http_response_vector_sb(struct sandbox *c)
{ {
int nb = 0; int nb = 0;
#ifndef STANDALONE #ifndef STANDALONE
struct http_response *r = &c->rsi; struct http_response *r = &c->rsi;
#ifdef USE_HTTP_UVIO
r->bufs[nb] = uv_buf_init(r->status, r->stlen); r->bufs[nb] = uv_buf_init(r->status, r->stlen);
nb++; nb++;
@ -183,6 +184,27 @@ http_response_uv_sb(struct sandbox *c)
r->bufs[nb] = uv_buf_init(r->status + r->stlen - 2, 2); //for crlf r->bufs[nb] = uv_buf_init(r->status + r->stlen - 2, 2); //for crlf
nb++; nb++;
} }
#else
r->bufs[nb].iov_base = r->status;
r->bufs[nb].iov_len = r->stlen;
nb++;
for (int i = 0; i < r->nheaders; i++) {
r->bufs[nb].iov_base = r->headers[i].hdr;
r->bufs[nb].iov_len = r->headers[i].len;
nb++;
}
if (r->body) {
r->bufs[nb].iov_base = r->body;
r->bufs[nb].iov_len = r->bodylen;
nb++;
r->bufs[nb].iov_base = r->status + r->stlen - 2;
r->bufs[nb].iov_len = 2;
nb++;
}
#endif
#endif #endif
return nb; return nb;

@ -87,10 +87,12 @@ sandbox_io_nowait(void)
} }
struct sandbox * struct sandbox *
sandbox_schedule(void) sandbox_schedule(int interrupt)
{ {
struct sandbox *s = NULL; struct sandbox *s = NULL;
if (ps_list_head_empty(&runq)) { if (ps_list_head_empty(&runq)) {
// this is in an interrupt context, don't steal work here!
if (interrupt) return NULL;
if (sandbox_pull() == 0) { if (sandbox_pull() == 0) {
//debuglog("[null: null]\n"); //debuglog("[null: null]\n");
return NULL; return NULL;
@ -130,7 +132,7 @@ sandbox_schedule_io(void)
if (!in_callback) sandbox_io_nowait(); if (!in_callback) sandbox_io_nowait();
softint_disable(); softint_disable();
struct sandbox *s = sandbox_schedule(); struct sandbox *s = sandbox_schedule(0);
softint_enable(); softint_enable();
assert(s == NULL || s->state == SANDBOX_RUNNABLE); assert(s == NULL || s->state == SANDBOX_RUNNABLE);
@ -143,11 +145,12 @@ sandbox_wakeup(sandbox_t *s)
#ifndef STANDALONE #ifndef STANDALONE
softint_disable(); softint_disable();
debuglog("[%p: %s]\n", s, s->mod->name); debuglog("[%p: %s]\n", s, s->mod->name);
// perhaps 2 lists in the sandbox to make sure sandbox is either in runlist or waitlist.. if (s->state != SANDBOX_BLOCKED) goto done;
assert(s->state == SANDBOX_BLOCKED); assert(s->state == SANDBOX_BLOCKED);
assert(ps_list_singleton_d(s)); assert(ps_list_singleton_d(s));
s->state = SANDBOX_RUNNABLE; s->state = SANDBOX_RUNNABLE;
ps_list_head_append_d(&runq, s); ps_list_head_append_d(&runq, s);
done:
softint_enable(); softint_enable();
#endif #endif
} }
@ -156,13 +159,12 @@ void
sandbox_block(void) sandbox_block(void)
{ {
#ifndef STANDALONE #ifndef STANDALONE
// perhaps 2 lists in the sandbox to make sure sandbox is either in runlist or waitlist..
assert(in_callback == 0); assert(in_callback == 0);
softint_disable(); softint_disable();
struct sandbox *c = sandbox_current(); struct sandbox *c = sandbox_current();
ps_list_rem_d(c); ps_list_rem_d(c);
c->state = SANDBOX_BLOCKED; c->state = SANDBOX_BLOCKED;
struct sandbox *s = sandbox_schedule(); struct sandbox *s = sandbox_schedule(0);
debuglog("[%p: %s, %p: %s]\n", c, c->mod->name, s, s ? s->mod->name: ""); debuglog("[%p: %s, %p: %s]\n", c, c->mod->name, s, s ? s->mod->name: "");
softint_enable(); softint_enable();
sandbox_switch(s); sandbox_switch(s);
@ -171,6 +173,24 @@ sandbox_block(void)
#endif #endif
} }
void
sandbox_block_http(void)
{
#ifdef USE_HTTP_UVIO
#ifdef USE_HTTP_SYNC
// realistically, we're processing all async I/O on this core when a sandbox blocks on http processing, not great!
// if there is a way (TODO), perhaps RUN_ONCE and check if your I/O is processed, if yes, return
// else do async block!
uv_run(runtime_uvio(), UV_RUN_DEFAULT);
#else
sandbox_block();
#endif
#else
assert(0);
//it should not be called if not using uvio for http
#endif
}
void __attribute__((noinline)) __attribute__((noreturn)) void __attribute__((noinline)) __attribute__((noreturn))
sandbox_switch_preempt(void) sandbox_switch_preempt(void)
{ {
@ -248,7 +268,7 @@ sandbox_exit(void)
sandbox_local_stop(curr); sandbox_local_stop(curr);
curr->state = SANDBOX_RETURNED; curr->state = SANDBOX_RETURNED;
// free resources from "main function execution", as stack still in use. // free resources from "main function execution", as stack still in use.
struct sandbox *n = sandbox_schedule(); struct sandbox *n = sandbox_schedule(0);
assert(n != curr); assert(n != curr);
softint_enable(); softint_enable();
//sandbox_local_end(curr); //sandbox_local_end(curr);

@ -133,11 +133,11 @@ sandbox_client_request_get(void)
struct sandbox *curr = sandbox_current(); struct sandbox *curr = sandbox_current();
curr->rr_data_len = 0; curr->rr_data_len = 0;
#ifndef USE_UVIO #ifndef USE_HTTP_UVIO
int r = 0; int r = 0;
r = recv(curr->csock, (curr->req_resp_data), curr->mod->max_req_sz, 0); r = recv(curr->csock, (curr->req_resp_data), curr->mod->max_req_sz, 0);
if (r <= 0) { if (r <= 0) {
perror("recv"); if (r < 0) perror("recv1");
return r; return r;
} }
while (r > 0) { while (r > 0) {
@ -148,13 +148,13 @@ sandbox_client_request_get(void)
r = recv(curr->csock, (curr->req_resp_data + r), curr->mod->max_req_sz - r, 0); r = recv(curr->csock, (curr->req_resp_data + r), curr->mod->max_req_sz - r, 0);
if (r < 0) { if (r < 0) {
perror("recv"); perror("recv2");
return r; return r;
} }
} }
#else #else
int r = uv_read_start((uv_stream_t *)&curr->cuv, sb_alloc_callback, sb_read_callback); int r = uv_read_start((uv_stream_t *)&curr->cuv, sb_alloc_callback, sb_read_callback);
sandbox_block(); sandbox_block_http();
if (curr->rr_data_len == 0) return 0; if (curr->rr_data_len == 0) return 0;
#endif #endif
@ -170,15 +170,6 @@ sandbox_client_response_set(void)
#ifndef STANDALONE #ifndef STANDALONE
struct sandbox *curr = sandbox_current(); struct sandbox *curr = sandbox_current();
#ifndef USE_UVIO
strcpy(curr->req_resp_data + curr->rr_data_len, "HTTP/1.1 200 OK\r\n");
// TODO: response set in req_resp_data
curr->rr_data_len += strlen("HTTP/1.1 200 OK\r\n");
int r = send(curr->csock, curr->req_resp_data, curr->rr_data_len, 0);
if (r < 0) perror("send");
#else
int bodylen = curr->rr_data_len; int bodylen = curr->rr_data_len;
if (bodylen > 0) { if (bodylen > 0) {
http_response_body_set(curr->req_resp_data, bodylen); http_response_body_set(curr->req_resp_data, bodylen);
@ -217,10 +208,14 @@ sandbox_client_response_set(void)
curr->rr_data_len += strlen("HTTP/1.1 200 OK\r\n"); curr->rr_data_len += strlen("HTTP/1.1 200 OK\r\n");
http_response_status_set(st, strlen("HTTP/1.1 200 OK\r\n")); http_response_status_set(st, strlen("HTTP/1.1 200 OK\r\n"));
int n = http_response_vector();
#ifndef USE_HTTP_UVIO
int r = writev(curr->csock, curr->rsi.bufs, n);
if (r < 0) perror("writev");
#else
uv_write_t req = { .data = curr, }; uv_write_t req = { .data = curr, };
int n = http_response_uv();
int r = uv_write(&req, (uv_stream_t *)&curr->cuv, curr->rsi.bufs, n, sb_write_callback); int r = uv_write(&req, (uv_stream_t *)&curr->cuv, curr->rsi.bufs, n, sb_write_callback);
sandbox_block(); sandbox_block_http();
#endif #endif
return r; return r;
#else #else
@ -253,7 +248,11 @@ sandbox_entry(void)
#ifndef STANDALONE #ifndef STANDALONE
http_parser_init(&curr->hp, HTTP_REQUEST); http_parser_init(&curr->hp, HTTP_REQUEST);
curr->hp.data = curr; curr->hp.data = curr;
#ifdef USE_UVIO #ifdef USE_HTTP_UVIO
#ifndef USE_UVIO
printf("UVIO not enabled!\n");
assert(0);
#endif
int r = uv_tcp_init(runtime_uvio(), (uv_tcp_t *)&curr->cuv); int r = uv_tcp_init(runtime_uvio(), (uv_tcp_t *)&curr->cuv);
assert(r == 0); assert(r == 0);
curr->cuv.data = curr; curr->cuv.data = curr;
@ -277,9 +276,9 @@ sandbox_entry(void)
} }
#ifndef STANDALONE #ifndef STANDALONE
#ifdef USE_UVIO #ifdef USE_HTTP_UVIO
uv_close((uv_handle_t *)&curr->cuv, sb_close_callback); uv_close((uv_handle_t *)&curr->cuv, sb_close_callback);
sandbox_block(); sandbox_block_http();
#else #else
close(curr->csock); close(curr->csock);
#endif #endif

@ -67,7 +67,7 @@ softint_alarm_schedule(void *u)
if (curr == NULL) goto done; if (curr == NULL) goto done;
// find a next sandbox to run.. // find a next sandbox to run..
struct sandbox *next = sandbox_schedule(); struct sandbox *next = sandbox_schedule(1);
if (next == NULL) goto done; if (next == NULL) goto done;
if (next == curr) goto done; // only this sandbox to schedule.. return to it! if (next == curr) goto done; // only this sandbox to schedule.. return to it!
// save the current sandbox, state from uc! // save the current sandbox, state from uc!

Loading…
Cancel
Save