struct conn_stream *srv_cs = NULL;
struct server *srv;
int reuse = 0;
+ int reuse_orphan = 0;
int err;
int i;
else if (srv->idle_conns && !LIST_ISEMPTY(&srv->idle_conns[tid]) &&
(s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_ALWS) {
srv_conn = LIST_ELEM(srv->idle_conns[tid].n, struct connection *, list);
+ } else if (srv->idle_orphan_conns && !LIST_ISEMPTY(&srv->idle_orphan_conns[tid]) &&
+ (((s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_ALWS) ||
+ (((s->be->options & PR_O_REUSE_MASK) != PR_O_REUSE_NEVR) &&
+ s->txn && (s->txn->flags & TX_NOT_FIRST)))) {
+ srv_conn = LIST_ELEM(srv->idle_orphan_conns[tid].n,
+ struct connection *, list);
+ reuse_orphan = 1;
}
/* If we've picked a connection from the pool, we now have to
reuse = 0;
}
}
+ /* If we're really reusing the connection, remove it from the orphan
+ * list and add it back to the idle list.
+ */
+ if (reuse && reuse_orphan) {
+ LIST_DEL(&srv_conn->list);
+ LIST_ADDQ(&srv->idle_conns[tid], &srv_conn->list);
+ if (LIST_ISEMPTY(&srv->idle_orphan_conns[tid]))
+ task_unlink_wq(srv->idle_task[tid]);
+ }
/* We're about to use another connection, let the mux know we're
* done with this one
static void srv_update_state(struct server *srv, int version, char **params);
static int srv_apply_lastaddr(struct server *srv, int *err_code);
static int srv_set_fqdn(struct server *srv, const char *fqdn, int dns_locked);
+static struct task *cleanup_idle_connections(struct task *task, void *ctx, unsigned short state);
/* List head of all known server keywords */
static struct srv_kw_list srv_keywords = {
return 0;
}
+static int srv_parse_idle_timeout(char **args, int *cur_arg, struct proxy *curproxy, struct server *newsrv, char **err)
+{
+ const char *res;
+ char *arg;
+ unsigned int time;
+
+ arg = args[*cur_arg + 1];
+ if (!*arg) {
+ memprintf(err, "'%s' expects <value> as argument.\n", args[*cur_arg]);
+ return ERR_ALERT | ERR_FATAL;
+ }
+ res = parse_time_err(arg, &time, TIME_UNIT_MS);
+ if (res) {
+ memprintf(err, "unexpected character '%c' in argument to <%s>.\n",
+ *res, args[*cur_arg]);
+ return ERR_ALERT | ERR_FATAL;
+ }
+ newsrv->idle_timeout = time;
+
+ return 0;
+}
+
/* parse the "id" server keyword */
static int srv_parse_id(char **args, int *cur_arg, struct proxy *curproxy, struct server *newsrv, char **err)
{
{ "disabled", srv_parse_disabled, 0, 1 }, /* Start the server in 'disabled' state */
{ "enabled", srv_parse_enabled, 0, 1 }, /* Start the server in 'enabled' state */
{ "id", srv_parse_id, 1, 0 }, /* set id# of server */
+ { "idle-timeout", srv_parse_idle_timeout, 1, 1 }, /* Set the time before we destroy orphan idle connections, defaults to 0 */
{ "namespace", srv_parse_namespace, 1, 1 }, /* Namespace the server socket belongs to (if supported) */
{ "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
{ "no-backup", srv_parse_no_backup, 0, 1 }, /* Flag as non-backup server */
srv->tcp_ut = src->tcp_ut;
#endif
srv->mux_proto = src->mux_proto;
+ srv->idle_timeout = src->idle_timeout;
if (srv_tmpl)
srv->srvrq = src->srvrq;
px->srv_act++;
srv_lb_commit_status(srv);
+ if (!srv->tmpl_info.prefix && srv->idle_timeout != 0) {
+ int i;
+
+ srv->idle_orphan_conns = calloc(global.nbthread, sizeof(*srv->idle_orphan_conns));
+ if (!srv->idle_orphan_conns)
+ goto err;
+ srv->idle_task = calloc(global.nbthread, sizeof(*srv->idle_task));
+ if (!srv->idle_task)
+ goto err;
+ for (i = 0; i < global.nbthread; i++) {
+ LIST_INIT(&srv->idle_orphan_conns[i]);
+ srv->idle_task[i] = task_new(1 << i);
+ if (!srv->idle_task[i])
+ goto err;
+ srv->idle_task[i]->process = cleanup_idle_connections;
+ srv->idle_task[i]->context = srv;
+ }
+ }
+
return 0;
+err:
+ return ERR_ALERT | ERR_FATAL;
}
/*
/* Linked backwards first. This will be restablished after parsing. */
newsrv->next = px->srv;
px->srv = newsrv;
+ if (newsrv->idle_timeout != 0) {
+ int i;
+
+ newsrv->idle_orphan_conns = calloc(global.nbthread, sizeof(*newsrv->idle_orphan_conns));
+ if (!newsrv->idle_orphan_conns)
+ goto err;
+ newsrv->idle_task = calloc(global.nbthread, sizeof(*newsrv->idle_task));
+ if (!newsrv->idle_task)
+ goto err;
+ for (i = 0; i < global.nbthread; i++) {
+ LIST_INIT(&newsrv->idle_orphan_conns[i]);
+ newsrv->idle_task[i] = task_new(1 << i);
+ if (!newsrv->idle_task[i])
+ goto err;
+ newsrv->idle_task[i]->process = cleanup_idle_connections;
+ newsrv->idle_task[i]->context = newsrv;
+ }
+ }
}
srv_set_id_from_prefix(srv, srv->tmpl_info.prefix, srv->tmpl_info.nb_low);
*s->adm_st_chg_cause = 0;
}
+static struct task *cleanup_idle_connections(struct task *task, void *context, unsigned short state)
+{
+ struct server *srv = context;
+ struct connection *conn, *conn_back;
+ unsigned int next_wakeup = 0;
+
+ list_for_each_entry_safe(conn, conn_back, &srv->idle_orphan_conns[tid], list) {
+ if (conn->idle_time + srv->idle_timeout > now_ms) {
+ next_wakeup = conn->idle_time + srv->idle_timeout;
+ break;
+ }
+ conn->mux->destroy(conn);
+ }
+ if (next_wakeup > 0)
+ task_schedule(task, next_wakeup);
+ return task;
+}
/*
* Local variables:
* c-indent-level: 8
list_for_each_entry_safe(conn, conn_back, &sess->srv_list[i].list, session_list) {
count++;
if (conn->mux) {
+ struct server *srv;
+
LIST_DEL(&conn->session_list);
LIST_INIT(&conn->session_list);
+ srv = objt_server(conn->target);
conn->owner = NULL;
- conn->mux->destroy(conn);
+ if (srv && srv->idle_timeout > 0 &&
+ !(conn->flags & CO_FL_PRIVATE) &&
+ conn->mux->avail_streams(conn) ==
+ conn->mux->max_streams(conn)) {
+ LIST_DEL(&conn->list);
+
+ LIST_ADDQ(&srv->idle_orphan_conns[tid],
+ &conn->list);
+
+ conn->idle_time = now_ms;
+ if (!(task_in_wq(srv->idle_task[tid])) &&
+ !(task_in_rq(srv->idle_task[tid])))
+ task_schedule(srv->idle_task[tid],
+ tick_add(now_ms, srv->idle_timeout));
+ } else
+ conn->mux->destroy(conn);
} else {
/* We have a connection, but not yet an associated mux.
* So destroy it now.