From 1e62de615be9a5b03fbd4414ed4082f86da1ced3 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 11 Nov 2008 20:20:02 +0100 Subject: [PATCH] [MEDIUM] add the SN_CURR_SESS flag to the session to track open sessions It is quite hard to track when the current session has already been counted or discounted from the server's total number of established sessions. For this reason, we introduce a new session flag, SN_CURR_SESS, which indicates if the current session is one of those reported by the server or not. It simplifies session accounting and makes it far more robust. It also makes it possible to perform a last-minute cleanup during session_free(). Right now, with this fix and a few more buffer transitions fixes, no session were found to remain after a test. --- include/types/session.h | 2 +- src/backend.c | 1 + src/proto_http.c | 56 ++++++++++++++++++++++------------------- src/session.c | 7 +++++- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/include/types/session.h b/include/types/session.h index 8b87be21a..1177924c5 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -47,7 +47,7 @@ #define SN_BE_ASSIGNED 0x00000008 /* a backend was assigned. Conns are accounted. */ #define SN_CONN_CLOSED 0x00000010 /* "Connection: close" was present or added */ #define SN_MONITOR 0x00000020 /* this session comes from a monitoring system */ -/* unused: 0x00000040 */ +#define SN_CURR_SESS 0x00000040 /* a connection is currently being counted on the server */ #define SN_FRT_ADDR_SET 0x00000080 /* set if the frontend address has been filled */ #define SN_REDISP 0x00000100 /* set if this session was redispatched from one server to another */ #define SN_CONN_TAR 0x00000200 /* set if this session is turning around before reconnecting */ diff --git a/src/backend.c b/src/backend.c index de0537a19..0d6fef12f 100644 --- a/src/backend.c +++ b/src/backend.c @@ -1820,6 +1820,7 @@ int connect_server(struct session *s) s->req->cons->state = SI_ST_CON; if (s->srv) { + s->flags |= SN_CURR_SESS; s->srv->cur_sess++; if (s->srv->cur_sess > s->srv->cur_sess_max) s->srv->cur_sess_max = s->srv->cur_sess; diff --git a/src/proto_http.c b/src/proto_http.c index 6f2dab3cf..fe59841a6 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -963,8 +963,8 @@ void process_session(struct task *t, int *next) * upper by setting flags into the buffers. Note that the side towards * the client cannot have connect (hence retryable) errors. */ - if (unlikely(s->si[0].state == SI_ST_EST)) { - if (s->si[0].flags & SI_FL_ERR) { + if (s->si[0].state == SI_ST_EST) { + if (unlikely(s->si[0].flags & SI_FL_ERR)) { s->si[0].state = SI_ST_CLO; fd_delete(s->si[0].fd); stream_int_report_error(&s->si[0]); @@ -972,10 +972,13 @@ void process_session(struct task *t, int *next) } if (s->si[1].state == SI_ST_EST) { - if (s->si[1].flags & SI_FL_ERR) { + if (unlikely(s->si[1].flags & SI_FL_ERR)) { s->si[1].state = SI_ST_CLO; fd_delete(s->si[1].fd); stream_int_report_error(&s->si[1]); + s->be->failed_resp++; + if (s->srv) + s->srv->failed_resp++; } } else if (s->si[1].state != SI_ST_INI && s->si[1].state != SI_ST_CLO) { @@ -1044,35 +1047,33 @@ void process_session(struct task *t, int *next) s->req->cons->shutw(s->req->cons); } + if (unlikely((s->req->flags & (BF_SHUTW|BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTW)) { + /* write closed on server side, let's forward it to the client */ + buffer_shutr_now(s->req); + s->req->prod->shutr(s->req->prod); + } + if (unlikely((s->rep->flags & (BF_SHUTW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) == (BF_EMPTY|BF_WRITE_ENA|BF_SHUTR)) || unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTW_NOW)) == BF_SHUTW_NOW)) { buffer_shutw(s->rep); s->rep->cons->shutw(s->rep->cons); } + if (unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTW)) { + /* write closed on client side, let's forward it to the server */ + buffer_shutr_now(s->rep); + s->rep->prod->shutr(s->rep->prod); + } + /* 3: When a server-side connection is released, we have to * count it and check for pending connections on this server. - * FIXME: the test below is not accurate. An audit is needed - * to find all uncaught transitions. We need a way to ensure - * that shutdowns called right after connect() after TAR will - * correctly be caught for instance. In fact we need a way to - * track when the connection is assigned to the server. */ - if (unlikely(s->req->cons->state == SI_ST_CLO && - (s->req->cons->prev_state == SI_ST_EST || s->req->cons->prev_state == SI_ST_CON))) { - /* Count server-side errors (but not timeouts). */ - if (s->req->flags & BF_WRITE_ERROR) { - s->be->failed_resp++; - if (s->srv) - s->srv->failed_resp++; - } - - if (s->srv) { - s->srv->cur_sess--; - sess_change_server(s, NULL); - if (may_dequeue_tasks(s->srv, s->be)) - process_srv_queue(s->srv); - } + if (unlikely(s->req->cons->state == SI_ST_CLO && s->srv && (s->flags & SN_CURR_SESS))) { + s->flags &= ~SN_CURR_SESS; + s->srv->cur_sess--; + sess_change_server(s, NULL); + if (may_dequeue_tasks(s->srv, s->be)) + process_srv_queue(s->srv); } /* Dirty trick: force one first pass everywhere */ @@ -1153,7 +1154,8 @@ void process_session(struct task *t, int *next) * count it and check for pending connections on this server. */ if (s->req->cons->state == SI_ST_CLO) { - if (s->srv) { + if (s->srv && (s->flags & SN_CURR_SESS)) { + s->flags &= ~SN_CURR_SESS; s->srv->cur_sess--; sess_change_server(s, NULL); if (may_dequeue_tasks(s->srv, s->be)) @@ -3734,7 +3736,8 @@ int sess_update_st_con_tcp(struct session *s, struct stream_interface *si) si->state = SI_ST_CER; fd_delete(si->fd); - if (s->srv) { + if (s->srv && (s->flags & SN_CURR_SESS)) { + s->flags &= ~SN_CURR_SESS; s->srv->cur_sess--; sess_change_server(s, NULL); si->err_loc = s->srv; @@ -3759,7 +3762,8 @@ int sess_update_st_con_tcp(struct session *s, struct stream_interface *si) /* give up */ req->wex = TICK_ETERNITY; fd_delete(si->fd); - if (s->srv) { + if (s->srv && (s->flags & SN_CURR_SESS)) { + s->flags &= ~SN_CURR_SESS; s->srv->cur_sess--; sess_change_server(s, NULL); } diff --git a/src/session.c b/src/session.c index ceef0b7df..fb7de4a3b 100644 --- a/src/session.c +++ b/src/session.c @@ -37,8 +37,13 @@ void session_free(struct session *s) if (s->pend_pos) pendconn_free(s->pend_pos); - if (s->srv) /* there may be requests left pending in queue */ + if (s->srv) { /* there may be requests left pending in queue */ + if (s->flags & SN_CURR_SESS) { + s->flags &= ~SN_CURR_SESS; + s->srv->cur_sess--; + } process_srv_queue(s->srv); + } if (unlikely(s->srv_conn)) { /* the session still has a reserved slot on a server, but * it should normally be only the same as the one above,