/* cache_backend_cfg.c */
void VBE_Init(void);
-void VBE_DropRef(struct backend *);
struct backend *VBE_AddBackend(struct cli *cli, const struct vrt_backend *vb);
/* cache_backend_poll.c */
LOCK(&bp->mtx);
bp->refcount++;
+ bp->n_conn++; /* It mostly works */
UNLOCK(&bp->mtx);
s = -1;
if (s < 0) {
LOCK(&bp->mtx);
+ bp->n_conn--;
bp->refcount--; /* Only keep ref on success */
UNLOCK(&bp->mtx);
}
return (NULL);
}
+ if (bp->max_conn > 0 && bp->n_conn >= bp->max_conn) {
+ VSL_stats->backend_busy++;
+ return (NULL);
+ }
+
vc = VBE_NewConn();
assert(vc->fd == -1);
AZ(vc->backend);
WSL(sp->wrk, SLT_BackendClose, sp->vbe->fd, "%s", bp->vcl_name);
TCP_close(&sp->vbe->fd);
- VBE_DropRef(bp);
+ VBE_DropRefConn(bp);
sp->vbe->backend = NULL;
VBE_ReleaseConn(sp->vbe);
sp->vbe = NULL;
struct sockaddr *ipv6;
socklen_t ipv6len;
+ unsigned max_conn;
+ unsigned n_conn;
VTAILQ_HEAD(, vbe_conn) connlist;
struct vbp_target *probe;
/* cache_backend_cfg.c */
extern MTX VBE_mtx;
+void VBE_DropRefConn(struct backend *);
+void VBE_DropRef(struct backend *);
void VBE_DropRefLocked(struct backend *b);
/* cache_backend_poll.c */
VBE_DropRefLocked(b);
}
+void
+VBE_DropRefConn(struct backend *b)
+{
+
+ CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+
+ LOCK(&b->mtx);
+ assert(b->n_conn > 0);
+ b->n_conn--;
+ VBE_DropRefLocked(b);
+}
+
/*--------------------------------------------------------------------*/
static void
REPLACE(b->hosthdr, vb->hosthdr);
b->connect_timeout = vb->connect_timeout;
+ b->max_conn = vb->max_connections;
/*
* Copy over the sockaddrs
ASSERT_CLI();
VTAILQ_FOREACH(b, &backends, list) {
CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
- cli_out(cli, "%p %s %d\n",
- b,
- b->vcl_name,
- b->refcount);
+ cli_out(cli, "%p %s %d %d/%d\n",
+ b, b->vcl_name, b->refcount,
+ b->n_conn, b->max_conn);
}
}
--- /dev/null
+# $Id$
+
+test "Check backend connection limit"
+
+server s1 {
+ rxreq
+ sema r1 sync 2
+ sema r2 sync 2
+ txresp
+} -start
+
+varnish v1 -vcl {
+
+ backend default {
+ .host = "127.0.0.1";
+ .port = "9080";
+ .max_connections = 1;
+ }
+ sub vcl_recv {
+ pass;
+ }
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -start
+
+
+client c2 {
+ sema r1 sync 2
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+varnish v1 -cli "debug.backend"
+
+sema r2 sync 2
+client c1 -wait
+
+varnish v1 -expect backend_busy == 1
MAC_STAT(backend_conn, uint64_t, 'a', "Backend connections success")
MAC_STAT(backend_unhealthy, uint64_t, 'a', "Backend connections not attempted")
+MAC_STAT(backend_busy, uint64_t, 'a', "Backend connections too many")
MAC_STAT(backend_fail, uint64_t, 'a', "Backend connections failures")
MAC_STAT(backend_reuse, uint64_t, 'a', "Backend connections reuses")
MAC_STAT(backend_recycle, uint64_t, 'a', "Backend connections recycles")
const unsigned char *ipv6_sockaddr;
double connect_timeout;
+ unsigned max_connections;
struct vrt_backend_probe probe;
};
const char *ep;
struct fld_spec *fs;
struct vsb *vsb;
+ unsigned u;
fs = vcc_FldSpec(tl,
"!host",
"?port",
"?host_header",
"?connect_timeout",
- "?probe", NULL);
+ "?probe",
+ "?max_connections",
+ NULL);
t_first = tl->t;
ExpectErr(tl, '{');
Fb(tl, 0, ",\n");
ExpectErr(tl, ';');
vcc_NextToken(tl);
+ } else if (vcc_IdIs(t_field, "max_connections")) {
+ u = vcc_UintVal(tl);
+ vcc_NextToken(tl);
+ ERRCHK(tl);
+ ExpectErr(tl, ';');
+ vcc_NextToken(tl);
+ Fb(tl, 0, "\t.max_connections = %u,\n", u);
} else if (vcc_IdIs(t_field, "probe")) {
vcc_ParseProbe(tl);
ERRCHK(tl);
vsb_cat(sb, " const unsigned char *ipv6_sockaddr;\n");
vsb_cat(sb, "\n");
vsb_cat(sb, " double connect_timeout;\n");
+ vsb_cat(sb, " unsigned max_connections;\n");
vsb_cat(sb, " struct vrt_backend_probe probe;\n");
vsb_cat(sb, "};\n");
vsb_cat(sb, "\n");