From 27a674efb84bde8c045b87c9634f123e2f8925dc Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 17 Aug 2009 07:23:33 +0200 Subject: [PATCH] [MEDIUM] make it possible to change the buffer size in the configuration The new tune.bufsize and tune.maxrewrite global directives allow one to change the buffer size and the maxrewrite size. Right now, setting bufsize too low will block stats sockets which will not be able to write at all. An error checking must be added to buffer_write_chunk() so that if it cannot write its message to an empty buffer, it causes the caller to abort. --- doc/configuration.txt | 24 ++++++++++++++++++++++++ include/types/global.h | 2 ++ src/buffers.c | 3 ++- src/cfgparse.c | 26 +++++++++++++++++++++++--- src/client.c | 6 +++--- src/haproxy.c | 14 ++++++++++++-- src/proto_uxst.c | 4 ++-- src/session.c | 2 +- 8 files changed, 69 insertions(+), 12 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 50f82e577..84fc9c921 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -381,8 +381,10 @@ The following keywords are supported in the "global" section : - nosepoll - nosplice - spread-checks + - tune.bufsize - tune.maxaccept - tune.maxpollevents + - tune.maxrewrite * Debugging - debug @@ -557,6 +559,16 @@ spread-checks <0..50, in percent> some randomness in the check interval between 0 and +/- 50%. A value between 2 and 5 seems to show good results. The default value remains at 0. +tune.bufsize + Sets the buffer size to this size (in bytes). Lower values allow more + sessions to coexist in the same amount of RAM, and higher values allow some + applications with very large cookies to work. The default value is 16384 and + can be changed at build time. It is strongly recommended not to change this + from the default value, as very low values will break some services such as + statistics, and values larger than default size will increase memory usage, + possibly causing the system to run out of memory. At least the global maxconn + parameter should be decreased by the same factor as this one is increased. + tune.maxaccept Sets the maximum number of consecutive accepts that a process may perform on a single wake up. High values give higher priority to high connection rates, @@ -574,6 +586,18 @@ tune.maxpollevents latency at the expense of network bandwidth, and increasing it above 200 tends to trade latency for slightly increased bandwidth. +tune.maxrewrite + Sets the reserved buffer space to this size in bytes. The reserved space is + used for header rewriting or appending. The first reads on sockets will never + fill more than bufsize-maxrewrite. Historically it has defaulted to half of + bufsize, though that does not make much sense since there are rarely large + numbers of headers to add. Setting it too high prevents processing of large + requests or responses. Setting it too low prevents addition of new headers + to already large requests or to POST requests. It is generally wise to set it + to about 1024. It is automatically readjusted to half of bufsize if it is + larger than that. This means you don't have to worry about it when changing + bufsize. + 3.3. Debugging -------------- diff --git a/include/types/global.h b/include/types/global.h index aefee3226..4d349c313 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -79,6 +79,8 @@ struct global { int maxaccept; /* max number of consecutive accept() */ int options; /* various tuning options */ int recv_enough; /* how many input bytes at once are "enough" */ + int bufsize; /* buffer size in bytes, defaults to BUFSIZE */ + int maxrewrite; /* buffer max rewrite size in bytes, defaults to MAXREWRITE */ } tune; struct listener stats_sock; /* unix socket listener for statistics */ struct proxy *stats_fe; /* the frontend holding the stats settings */ diff --git a/src/buffers.c b/src/buffers.c index 55b79639b..a20f3f499 100644 --- a/src/buffers.c +++ b/src/buffers.c @@ -17,6 +17,7 @@ #include #include #include +#include struct pool_head *pool2_buffer; @@ -24,7 +25,7 @@ struct pool_head *pool2_buffer; /* perform minimal intializations, report 0 in case of error, 1 if OK. */ int init_buffer() { - pool2_buffer = create_pool("buffer", sizeof(struct buffer) + BUFSIZE, MEM_F_SHARED); + pool2_buffer = create_pool("buffer", sizeof(struct buffer) + global.tune.bufsize, MEM_F_SHARED); return pool2_buffer != NULL; } diff --git a/src/cfgparse.c b/src/cfgparse.c index a6b9f07ce..382881225 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -445,6 +445,26 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) } global.tune.maxaccept = atol(args[1]); } + else if (!strcmp(args[0], "tune.bufsize")) { + if (*(args[1]) == 0) { + Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + global.tune.bufsize = atol(args[1]); + if (global.tune.maxrewrite >= global.tune.bufsize / 2) + global.tune.maxrewrite = global.tune.bufsize / 2; + } + else if (!strcmp(args[0], "tune.maxrewrite")) { + if (*(args[1]) == 0) { + Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + global.tune.maxrewrite = atol(args[1]); + if (global.tune.maxrewrite >= global.tune.bufsize / 2) + global.tune.maxrewrite = global.tune.bufsize / 2; + } else if (!strcmp(args[0], "uid")) { if (global.uid != 0) { Alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum); @@ -3482,13 +3502,13 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) goto out; } - if (stat.st_size <= BUFSIZE) { + if (stat.st_size <= global.tune.bufsize) { errlen = stat.st_size; } else { Warning("parsing [%s:%d] : custom error message file <%s> larger than %d bytes. Truncating.\n", - file, linenum, args[2], BUFSIZE); + file, linenum, args[2], global.tune.bufsize); err_code |= ERR_WARN; - errlen = BUFSIZE; + errlen = global.tune.bufsize; } err = malloc(errlen); /* malloc() must succeed during parsing */ diff --git a/src/client.c b/src/client.c index 92baefc63..76ea122c9 100644 --- a/src/client.c +++ b/src/client.c @@ -384,7 +384,7 @@ int event_accept(int fd) { if ((s->req = pool_alloc2(pool2_buffer)) == NULL) goto out_fail_req; /* no memory */ - s->req->size = BUFSIZE; + s->req->size = global.tune.bufsize; buffer_init(s->req); s->req->prod = &s->si[0]; s->req->cons = &s->si[1]; @@ -393,7 +393,7 @@ int event_accept(int fd) { s->req->flags |= BF_READ_ATTACHED; /* the producer is already connected */ if (p->mode == PR_MODE_HTTP) { /* reserve some space for header rewriting */ - s->req->max_len -= MAXREWRITE; + s->req->max_len -= global.tune.maxrewrite; s->req->flags |= BF_READ_DONTWAIT; /* one read is usually enough */ } @@ -411,7 +411,7 @@ int event_accept(int fd) { if ((s->rep = pool_alloc2(pool2_buffer)) == NULL) goto out_fail_rep; /* no memory */ - s->rep->size = BUFSIZE; + s->rep->size = global.tune.bufsize; buffer_init(s->rep); s->rep->prod = &s->si[1]; s->rep->cons = &s->si[0]; diff --git a/src/haproxy.c b/src/haproxy.c index 09f828809..dd03e32aa 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -122,7 +122,11 @@ struct global global = { .mode = 0, } } - } + }, + .tune = { + .bufsize = BUFSIZE, + .maxrewrite = MAXREWRITE, + }, /* others NULL OK */ }; @@ -177,7 +181,10 @@ void display_build_opts() #ifdef BUILD_OPTIONS "\n OPTIONS = " BUILD_OPTIONS #endif - "\n\n"); + "\n\nDefault settings :" + "\n maxconn = %d, bufsize = %d, maxrewrite = %d, maxpollevents = %d" + "\n\n", + DEFAULT_MAXCONN, BUFSIZE, MAXREWRITE, MAX_POLL_EVENTS); } /* @@ -604,6 +611,9 @@ void init(int argc, char **argv) if (global.tune.recv_enough == 0) global.tune.recv_enough = MIN_RECV_AT_ONCE_ENOUGH; + if (global.tune.maxrewrite >= global.tune.bufsize / 2) + global.tune.maxrewrite = global.tune.bufsize / 2; + if (arg_mode & (MODE_DEBUG | MODE_FOREGROUND)) { /* command line debug mode inhibits configuration mode */ global.mode &= ~(MODE_DAEMON | MODE_QUIET); diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 0cd6a3586..c811f64a8 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -482,7 +482,7 @@ int uxst_event_accept(int fd) { if ((s->req = pool_alloc2(pool2_buffer)) == NULL) goto out_free_task; - s->req->size = BUFSIZE; + s->req->size = global.tune.bufsize; buffer_init(s->req); s->req->prod = &s->si[0]; s->req->cons = &s->si[1]; @@ -499,7 +499,7 @@ int uxst_event_accept(int fd) { if ((s->rep = pool_alloc2(pool2_buffer)) == NULL) goto out_free_req; - s->rep->size = BUFSIZE; + s->rep->size = global.tune.bufsize; buffer_init(s->rep); s->rep->prod = &s->si[1]; diff --git a/src/session.c b/src/session.c index 23c4409c6..f0c2d8658 100644 --- a/src/session.c +++ b/src/session.c @@ -330,7 +330,7 @@ void sess_establish(struct session *s, struct stream_interface *si) } } else { - buffer_set_rlim(rep, req->size - MAXREWRITE); /* rewrite needed */ + buffer_set_rlim(rep, req->size - global.tune.maxrewrite); /* rewrite needed */ s->txn.rsp.msg_state = HTTP_MSG_RPBEFORE; /* reset hdr_idx which was already initialized by the request. * right now, the http parser does it.