Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/varnishd/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ varnishd_SOURCES = \
cache/cache_conn_pool.c \
cache/cache_deliver_proc.c \
cache/cache_director.c \
cache/cache_drain.c \
cache/cache_esi_deliver.c \
cache/cache_esi_fetch.c \
cache/cache_esi_parse.c \
Expand Down
1 change: 1 addition & 0 deletions bin/varnishd/acceptor/cache_acceptor_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ vca_tcp_make_session(struct worker *wrk, void *arg)

vca_pace_good();
wrk->stats->sess_conn++;
DRAIN_IncSess();

if (wa->acceptlsock->test_heritage) {
vca_tcp_sockopt_test(wa->acceptlsock, sp);
Expand Down
1 change: 1 addition & 0 deletions bin/varnishd/acceptor/cache_acceptor_uds.c
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ vca_uds_make_session(struct worker *wrk, void *arg)

vca_pace_good();
wrk->stats->sess_conn++;
DRAIN_IncSess();

if (wa->acceptlsock->test_heritage) {
vca_uds_sockopt_test(wa->acceptlsock, sp);
Expand Down
12 changes: 11 additions & 1 deletion bin/varnishd/cache/cache_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pthread_t cli_thread;
static struct lock cli_mtx;
static int add_check;
static struct VCLS *cache_cls;
static volatile int cli_wakeup = 0;

/*
* The CLI commandlist is split in three:
Expand Down Expand Up @@ -108,11 +109,20 @@ CLI_Run(void)
cli->auth = 255; // Non-zero to disable paranoia in vcli_serve

do {
i = VCLS_Poll(cache_cls, cli, -1);
i = VCLS_Poll(cache_cls, cli, 100);
if (cli_wakeup)
break;
} while (i == 0);
VSL(SLT_CLI, NO_VXID, "EOF on CLI connection, worker stops");
}

void
CLI_Wakeup(void)
{

cli_wakeup = 1;
}

/*--------------------------------------------------------------------*/

static struct cli_proto cli_cmds[] = {
Expand Down
147 changes: 147 additions & 0 deletions bin/varnishd/cache/cache_drain.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*-
* Copyright (c) 2026 Varnish Software AS
* SPDX-License-Identifier: BSD-2-Clause
*
* Connection draining support for graceful shutdown
*
* When draining is started, Varnish stops accepting new connections
* and adds "Connection: close" headers to responses. The child process
* monitors active sessions and exits when all sessions have closed or
* the timeout expires.
*/

#include "config.h"

#include "cache_varnishd.h"
#include "acceptor/cache_acceptor.h"
#include "vcli_serve.h"
#include "vtim.h"
#include "vnum.h"

static struct lock drain_mtx;
static int draining = 0;
static vtim_real drain_deadline = 0.0;
static volatile long n_active_sess = 0;
static volatile int drain_exiting = 0;

/*--------------------------------------------------------------------
* Signal drain completion by waking the CLI thread.
* This causes CLI_Run() to return, triggering clean child shutdown.
* Uses atomic compare-and-swap to ensure we only signal once.
*/

static void
drain_signal_exit(void)
{

if (__sync_bool_compare_and_swap(&drain_exiting, 0, 1))
CLI_Wakeup();
}

/*--------------------------------------------------------------------
* Monitor thread that waits for drain timeout or session completion.
*/

static void *
drain_monitor(void *arg)
{

(void)arg;

while (VTIM_real() < drain_deadline) {
if (n_active_sess <= 0)
drain_signal_exit();
VTIM_sleep(0.1);
}

/* Timeout expired */
drain_signal_exit();
return (NULL);
}

/*--------------------------------------------------------------------*/

int
DRAIN_Active(void)
{
int r;

Lck_Lock(&drain_mtx);
r = draining;
Lck_Unlock(&drain_mtx);
return (r);
}

void
DRAIN_IncSess(void)
{

(void)__sync_fetch_and_add(&n_active_sess, 1);
}

void
DRAIN_DecSess(void)
{
long n;

n = __sync_sub_and_fetch(&n_active_sess, 1);

/* If draining and no more sessions, exit immediately */
if (n <= 0 && DRAIN_Active())
drain_signal_exit();
}

void
DRAIN_Start(vtim_dur timeout)
{
pthread_t thr;

Lck_Lock(&drain_mtx);
if (draining) {
Lck_Unlock(&drain_mtx);
return;
}
draining = 1;
drain_deadline = VTIM_real() + timeout;
Lck_Unlock(&drain_mtx);

/* Stop accepting new connections */
VCA_Shutdown();

/* Wake idle workers to release VCL references faster */
Pool_WakeIdle();

/* Start monitor thread for timeout */
PTOK(pthread_create(&thr, NULL, drain_monitor, NULL));
}

static void v_matchproto_(cli_func_t)
cli_drain(struct cli *cli, const char * const *av, void *priv)
{
vtim_dur timeout;

(void)priv;

timeout = VNUM_duration(av[2]);
if (isnan(timeout) || timeout < 0) {
VCLI_SetResult(cli, CLIS_PARAM);
VCLI_Out(cli, "Invalid timeout");
return;
}

DRAIN_Start(timeout);
VCLI_Out(cli, "Draining started");
}

static struct cli_proto drain_cmds[] = {
{ CLICMD_DEBUG_DRAIN, "d", cli_drain },
{ NULL }
};

void
DRAIN_Init(void)
{

Lck_New(&drain_mtx, lck_drain);
CLI_AddFuncs(drain_cmds);
}
1 change: 1 addition & 0 deletions bin/varnishd/cache/cache_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ child_main(int sigmagic, size_t altstksz)

CLI_AddFuncs(debug_cmds);
CLI_AddFuncs(child_cmds);
DRAIN_Init();

#ifdef WITH_PERSISTENT_STORAGE
/* Wait for persistent storage to load if asked to */
Expand Down
25 changes: 25 additions & 0 deletions bin/varnishd/cache/cache_pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,31 @@ Pool_PurgeStat(unsigned nobj)
Lck_Unlock(&wstat_mtx);
}

/*--------------------------------------------------------------------
* Wake all idle workers so they can release their cached VCL references.
* Called when entering drain mode to speed up graceful shutdown.
*/

void
Pool_WakeIdle(void)
{
struct pool *pp;
struct pool_task *pt;
struct worker *wrk;

Lck_Lock(&pool_mtx);
VTAILQ_FOREACH(pp, &pools, list) {
Lck_Lock(&pp->mtx);
VTAILQ_FOREACH(pt, &pp->idle_queue, list) {
wrk = pt->priv;
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
PTOK(pthread_cond_signal(&wrk->cond));
}
Lck_Unlock(&pp->mtx);
}
Lck_Unlock(&pool_mtx);
}

/*--------------------------------------------------------------------
* Special function to summ stats
*/
Expand Down
2 changes: 2 additions & 0 deletions bin/varnishd/cache/cache_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,8 @@ SES_Delete(struct sess *sp, stream_close_t reason, vtim_real now)
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
CHECK_OBJ_NOTNULL(reason, STREAM_CLOSE_MAGIC);

DRAIN_DecSess();

if (reason != SC_NULL)
SES_Close(sp, reason);
assert(sp->fd < 0);
Expand Down
9 changes: 9 additions & 0 deletions bin/varnishd/cache/cache_varnishd.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,14 @@ void VBO_Init(void);
void CLI_Init(void);
void CLI_Run(void);
void CLI_AddFuncs(struct cli_proto *p);
void CLI_Wakeup(void);

/* cache_drain.c */
int DRAIN_Active(void);
void DRAIN_Init(void);
void DRAIN_Start(vtim_dur timeout);
void DRAIN_IncSess(void);
void DRAIN_DecSess(void);

/* cache_expire.c */
void EXP_Init(void);
Expand Down Expand Up @@ -416,6 +424,7 @@ void Pool_Sumstat(const struct worker *w);
int Pool_TrySumstat(const struct worker *wrk);
void Pool_PurgeStat(unsigned nobj);
int Pool_Task_Any(struct pool_task *task, enum task_prio prio);
void Pool_WakeIdle(void);
void pan_pool(struct vsb *);

/* cache_range.c */
Expand Down
3 changes: 2 additions & 1 deletion bin/varnishd/cache/cache_wrk.c
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,8 @@ Pool_Work_Thread(struct pool *pp, struct worker *wrk)
tmo = now + 1.;
else if (wrk->wpriv->vcl == NULL)
tmo = INFINITY;
else if (DO_DEBUG(DBG_VTC_MODE))
else if (DO_DEBUG(DBG_VTC_MODE) ||
DRAIN_Active())
tmo = now + 1.;
else
tmo = now + 60.;
Expand Down
4 changes: 4 additions & 0 deletions bin/varnishd/http1/cache_http1_deliver.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ V1D_Deliver(struct req *req, int sendbody)
CHECK_OBJ_ORNULL(req->boc, BOC_MAGIC);
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);

/* Force connection close during draining */
if (DRAIN_Active() && req->doclose == SC_NULL)
req->doclose = SC_RESP_CLOSE;

if (req->doclose == SC_NULL &&
http_HdrIs(req->resp, H_Connection, "close")) {
req->doclose = SC_RESP_CLOSE;
Expand Down
Loading