MEDIUM: stick-table: set the track-sc limit at boottime via tune.stick-counters

The number of stick-counter entries usable by track-sc rules is currently
set at build time. There is no good value for this since the vast majority
of users don't need any, most need only a few and rare users need more.
Adding more counters for everyone increases memory and CPU usages for no
reason.

This patch moves the per-session and per-stream arrays to a pool of a size
defined at boot time. This way it becomes possible to set the number of
entries at boot time via a new global setting "tune.stick-counters" that
sets the limit for the whole process. When not set, the MAX_SESS_STR_CTR
value still applies, or 3 if not set, as before.

It is also possible to lower the value to 0 to save a bit of memory if
not used at all.

Note that a few low-level sample-fetch functions had to be protected due
to the ability to use sample-fetches in the global section to set some
variables.
This commit is contained in:
Willy Tarreau 2023-01-06 16:09:58 +01:00
parent 3120284c29
commit 6c0117168e
13 changed files with 202 additions and 61 deletions

View File

@ -1158,6 +1158,7 @@ The following keywords are supported in the "global" section :
- tune.sched.low-latency
- tune.sndbuf.client
- tune.sndbuf.server
- tune.stick-counters
- tune.ssl.cachesize
- tune.ssl.capture-buffer-size
- tune.ssl.capture-cipherlist-size (deprecated)
@ -3305,6 +3306,21 @@ tune.ssl.ssl-ctx-cache-size <number>
dynamically is expensive, they are cached. The default cache size is set to
1000 entries.
tune.stick-counters <number>
Sets the number of stick-counters that may be tracked at the same time by a
connection or a request via "track-sc*" actions in "tcp-request" or
"http-request" rules. The defaut value is set at build time by the macro
MAX_SESS_STK_CTR, and defaults to 3. With this setting it is possible to
change the value and ignore the one passed at build time. Increasing this
value may be needed when porting complex configurations to haproxy, but users
are warned against the costs: each entry takes 16 bytes per connection and
16 bytes per request, all of which need to be allocated and zeroed for all
requests even when not used. As such a value of 10 will inflate the memory
consumption per request by 320 bytes and will cause this memory to be erased
for each request, which does have measurable CPU impacts. Conversely, when
no "track-sc" rules are used, the value may be lowered (0 being valid to
entirely disable stick-counters).
tune.vars.global-max-size <size>
tune.vars.proc-max-size <size>
tune.vars.reqres-max-size <size>
@ -7617,9 +7633,10 @@ http-request track-sc2 <key> [table <table>] [ { if | unless } <condition> ]
This enables tracking of sticky counters from current request. These rules do
not stop evaluation and do not change default action. The number of counters
that may be simultaneously tracked by the same connection is set in
MAX_SESS_STKCTR at build time (reported in haproxy -vv) which defaults to 3,
so the track-sc number is between 0 and (MAX_SESS_STKCTR-1). The first
that may be simultaneously tracked by the same connection is set by the
global "tune.stick-counters" setting, which defaults to MAX_SESS_STKCTR if
set at build time (it is reported in haproxy -vv) and which defaults to 3,
so the track-sc number is between 0 and (tune.stick-counters-1). The first
"track-sc0" rule executed enables tracking of the counters of the specified
table as the first set. The first "track-sc1" rule executed enables tracking
of the counters of the specified table as the second set. The first
@ -18924,12 +18941,12 @@ TCP/IP addresses and ports, as well as elements from stick-tables related to
the incoming connection. For retrieving a value from a sticky counters, the
counter number can be explicitly set as 0, 1, or 2 using the pre-defined
"sc0_", "sc1_", or "sc2_" prefix. These three pre-defined prefixes can only be
used if MAX_SESS_STKCTR value does not exceed 3, otherwise the counter number
can be specified as the first integer argument when using the "sc_" prefix.
Starting from "sc_0" to "sc_N" where N is (MAX_SESS_STKCTR-1). An optional
table may be specified with the "sc*" form, in which case the currently
tracked key will be looked up into this alternate table instead of the table
currently being tracked.
used if the global "tune.stick-counters" value does not exceed 3, otherwise the
counter number can be specified as the first integer argument when using the
"sc_" prefix starting from "sc_0" to "sc_N" where N is (tune.stick-counters-1).
An optional table may be specified with the "sc*" form, in which case the
currently tracked key will be looked up into this alternate table instead of
the table currently being tracked.
bc_dst : ip
This is the destination ip address of the connection on the server side,

View File

@ -162,6 +162,7 @@ struct global {
int pool_high_count; /* max number of opened fd before we start killing idle connections when creating new connections */
size_t pool_cache_size; /* per-thread cache size per pool (defaults to CONFIG_HAP_POOL_CACHE_SIZE) */
unsigned short idle_timer; /* how long before an empty buffer is considered idle (ms) */
int nb_stk_ctr; /* number of stick counters, defaults to MAX_SESS_STKCTR */
#ifdef USE_QUIC
unsigned int quic_backend_max_idle_timeout;
unsigned int quic_frontend_max_idle_timeout;

View File

@ -50,7 +50,7 @@ struct session {
enum obj_type *origin; /* the connection / applet which initiated this session */
struct timeval accept_date; /* date of the session's accept() in user date */
struct timeval tv_accept; /* date of the session's accept() in internal date (monotonic) */
struct stkctr stkctr[MAX_SESS_STKCTR]; /* stick counters for tcp-connection */
struct stkctr *stkctr; /* stick counters for tcp-connection */
struct vars vars; /* list of variables for the session scope. */
struct task *task; /* handshake timeout processing */
long t_handshake; /* handshake duration, -1 = not completed */

View File

@ -50,7 +50,10 @@ static inline void session_store_counters(struct session *sess)
int i;
struct stksess *ts;
for (i = 0; i < MAX_SESS_STKCTR; i++) {
if (unlikely(!sess->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
struct stkctr *stkctr = &sess->stkctr[i];
ts = stkctr_entry(stkctr);
@ -80,7 +83,10 @@ static inline void session_inc_http_req_ctr(struct session *sess)
{
int i;
for (i = 0; i < MAX_SESS_STKCTR; i++)
if (unlikely(!sess->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++)
stkctr_inc_http_req_ctr(&sess->stkctr[i]);
}
@ -94,7 +100,10 @@ static inline void session_inc_http_err_ctr(struct session *sess)
{
int i;
for (i = 0; i < MAX_SESS_STKCTR; i++)
if (unlikely(!sess->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++)
stkctr_inc_http_err_ctr(&sess->stkctr[i]);
}
@ -107,7 +116,10 @@ static inline void session_inc_http_fail_ctr(struct session *sess)
{
int i;
for (i = 0; i < MAX_SESS_STKCTR; i++)
if (unlikely(!sess->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++)
stkctr_inc_http_fail_ctr(&sess->stkctr[i]);
}

View File

@ -32,6 +32,7 @@
#include <haproxy/ticks.h>
extern struct stktable *stktables_list;
extern struct pool_head *pool_head_stk_ctr;
extern struct stktable_type stktable_types[];
#define stktable_data_size(type) (sizeof(((union stktable_data*)0)->type))

View File

@ -245,7 +245,7 @@ struct stream {
struct stktable *table;
} store[8]; /* tracked stickiness values to store */
struct stkctr stkctr[MAX_SESS_STKCTR]; /* content-aware stick counters */
struct stkctr *stkctr; /* content-aware stick counters */
struct strm_flt strm_flt; /* current state of filters active on this stream */

View File

@ -116,7 +116,10 @@ static inline void stream_store_counters(struct stream *s)
int i;
struct stksess *ts;
for (i = 0; i < MAX_SESS_STKCTR; i++) {
if (unlikely(!s->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
ts = stkctr_entry(&s->stkctr[i]);
if (!ts)
continue;
@ -152,7 +155,10 @@ static inline void stream_stop_content_counters(struct stream *s)
void *ptr;
int i;
for (i = 0; i < MAX_SESS_STKCTR; i++) {
if (unlikely(!s->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
ts = stkctr_entry(&s->stkctr[i]);
if (!ts)
continue;
@ -231,7 +237,10 @@ static inline void stream_inc_http_req_ctr(struct stream *s)
{
int i;
for (i = 0; i < MAX_SESS_STKCTR; i++) {
if (unlikely(!s->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
if (!stkctr_inc_http_req_ctr(&s->stkctr[i]))
stkctr_inc_http_req_ctr(&s->sess->stkctr[i]);
}
@ -244,7 +253,10 @@ static inline void stream_inc_be_http_req_ctr(struct stream *s)
{
int i;
for (i = 0; i < MAX_SESS_STKCTR; i++) {
if (unlikely(!s->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
if (!stkctr_entry(&s->stkctr[i]) || !(stkctr_flags(&s->stkctr[i]) & STKCTR_TRACK_BACKEND))
continue;
@ -262,7 +274,10 @@ static inline void stream_inc_http_err_ctr(struct stream *s)
{
int i;
for (i = 0; i < MAX_SESS_STKCTR; i++) {
if (unlikely(!s->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
if (!stkctr_inc_http_err_ctr(&s->stkctr[i]))
stkctr_inc_http_err_ctr(&s->sess->stkctr[i]);
}
@ -277,7 +292,10 @@ static inline void stream_inc_http_fail_ctr(struct stream *s)
{
int i;
for (i = 0; i < MAX_SESS_STKCTR; i++) {
if (unlikely(!s->stkctr)) // pool not allocated yet
return;
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
if (!stkctr_inc_http_fail_ctr(&s->stkctr[i]))
stkctr_inc_http_fail_ctr(&s->sess->stkctr[i]);
}

View File

@ -1564,9 +1564,13 @@ cfg_parse_track_sc_num(unsigned int *track_sc_num,
return -1;
}
if (num >= MAX_SESS_STKCTR) {
memprintf(errmsg, "%u track-sc number exceeding "
"%d (MAX_SESS_STKCTR-1) value", num, MAX_SESS_STKCTR - 1);
if (num >= global.tune.nb_stk_ctr) {
if (!global.tune.nb_stk_ctr)
memprintf(errmsg, "%u track-sc number not usable, stick-counters "
"are disabled by tune.stick-counters", num);
else
memprintf(errmsg, "%u track-sc number exceeding "
"%d (tune.stick-counters-1) value", num, global.tune.nb_stk_ctr - 1);
return -1;
}

View File

@ -204,6 +204,7 @@ struct global global = {
#else
.idle_timer = 1000, /* 1 second */
#endif
.nb_stk_ctr = MAX_SESS_STKCTR,
#ifdef USE_QUIC
.quic_backend_max_idle_timeout = QUIC_TP_DFLT_BACK_MAX_IDLE_TIMEOUT,
.quic_frontend_max_idle_timeout = QUIC_TP_DFLT_FRONT_MAX_IDLE_TIMEOUT,

View File

@ -46,7 +46,13 @@ struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type
sess->origin = origin;
sess->accept_date = date; /* user-visible date for logging */
sess->tv_accept = now; /* corrected date for internal use */
memset(sess->stkctr, 0, sizeof(sess->stkctr));
sess->stkctr = NULL;
if (pool_head_stk_ctr) {
sess->stkctr = pool_alloc(pool_head_stk_ctr);
if (!sess->stkctr)
goto out_fail_alloc;
memset(sess->stkctr, 0, sizeof(sess->stkctr[0]) * global.tune.nb_stk_ctr);
}
vars_init_head(&sess->vars, SCOPE_SESS);
sess->task = NULL;
sess->t_handshake = -1; /* handshake not done yet */
@ -60,6 +66,9 @@ struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type
sess->dst = NULL;
}
return sess;
out_fail_alloc:
pool_free(pool_head_session, sess);
return NULL;
}
void session_free(struct session *sess)
@ -70,6 +79,7 @@ void session_free(struct session *sess)
if (sess->listener)
listener_release(sess->listener);
session_store_counters(sess);
pool_free(pool_head_stk_ctr, sess->stkctr);
vars_prune_per_sess(&sess->vars);
conn = objt_conn(sess->origin);
if (conn != NULL && conn->mux)
@ -116,7 +126,7 @@ static void session_count_new(struct session *sess)
proxy_inc_fe_sess_ctr(sess->listener, sess->fe);
for (i = 0; i < MAX_SESS_STKCTR; i++) {
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
stkctr = &sess->stkctr[i];
if (!stkctr_entry(stkctr))
continue;

View File

@ -50,7 +50,7 @@
/* structure used to return a table key built from a sample */
static THREAD_LOCAL struct stktable_key static_table_key;
static int (*smp_fetch_src)(const struct arg *, struct sample *, const char *, void *);
struct pool_head *pool_head_stk_ctr __read_mostly = NULL;
struct stktable *stktables_list;
struct eb_root stktable_by_name = EB_ROOT;
@ -2456,14 +2456,16 @@ static enum act_return action_inc_gpc1(struct act_rule *rule, struct proxy *px,
struct session *sess, struct stream *s, int flags)
{
struct stksess *ts;
struct stkctr *stkctr;
struct stkctr *stkctr = NULL;
unsigned int period = 0;
/* Extract the stksess, return OK if no stksess available. */
if (s)
if (s && s->stkctr)
stkctr = &s->stkctr[rule->arg.gpc.sc];
else
else if (sess->stkctr)
stkctr = &sess->stkctr[rule->arg.gpc.sc];
else
return ACT_RET_CONT;
ts = stkctr_entry(stkctr);
if (ts) {
@ -2522,6 +2524,11 @@ static enum act_parse_ret parse_inc_gpc(const char **args, int *arg, struct prox
const char *cmd_name = args[*arg-1];
char *error;
if (!global.tune.nb_stk_ctr) {
memprintf(err, "Cannot use '%s', stick-counters are disabled via tune.stick-counters", args[*arg-1]);
return ACT_RET_PRS_ERR;
}
cmd_name += strlen("sc-inc-gpc");
if (*cmd_name == '(') {
cmd_name++; /* skip the '(' */
@ -2538,9 +2545,9 @@ static enum act_parse_ret parse_inc_gpc(const char **args, int *arg, struct prox
return ACT_RET_PRS_ERR;
}
if (rule->arg.gpc.sc >= MAX_SESS_STKCTR) {
memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d",
args[*arg-1], MAX_SESS_STKCTR-1);
if (rule->arg.gpc.sc >= global.tune.nb_stk_ctr) {
memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d (tune.stick-counters)",
args[*arg-1], global.tune.nb_stk_ctr-1);
return ACT_RET_PRS_ERR;
}
}
@ -2566,9 +2573,9 @@ static enum act_parse_ret parse_inc_gpc(const char **args, int *arg, struct prox
return ACT_RET_PRS_ERR;
}
if (rule->arg.gpc.sc >= MAX_SESS_STKCTR) {
memprintf(err, "invalid stick table track ID. The max allowed ID is %d",
MAX_SESS_STKCTR-1);
if (rule->arg.gpc.sc >= global.tune.nb_stk_ctr) {
memprintf(err, "invalid stick table track ID. The max allowed ID is %d (tune.stick-counters)",
global.tune.nb_stk_ctr-1);
return ACT_RET_PRS_ERR;
}
}
@ -2599,16 +2606,18 @@ static enum act_return action_set_gpt(struct act_rule *rule, struct proxy *px,
{
void *ptr;
struct stksess *ts;
struct stkctr *stkctr;
struct stkctr *stkctr = NULL;
unsigned int value = 0;
struct sample *smp;
int smp_opt_dir;
/* Extract the stksess, return OK if no stksess available. */
if (s)
if (s && s->stkctr)
stkctr = &s->stkctr[rule->arg.gpt.sc];
else
else if (sess->stkctr)
stkctr = &sess->stkctr[rule->arg.gpt.sc];
else
return ACT_RET_CONT;
ts = stkctr_entry(stkctr);
if (!ts)
@ -2663,16 +2672,18 @@ static enum act_return action_set_gpt0(struct act_rule *rule, struct proxy *px,
{
void *ptr;
struct stksess *ts;
struct stkctr *stkctr;
struct stkctr *stkctr = NULL;
unsigned int value = 0;
struct sample *smp;
int smp_opt_dir;
/* Extract the stksess, return OK if no stksess available. */
if (s)
if (s && s->stkctr)
stkctr = &s->stkctr[rule->arg.gpt.sc];
else
else if (sess->stkctr)
stkctr = &sess->stkctr[rule->arg.gpt.sc];
else
return ACT_RET_CONT;
ts = stkctr_entry(stkctr);
if (!ts)
@ -2741,6 +2752,11 @@ static enum act_parse_ret parse_set_gpt(const char **args, int *arg, struct prox
char *error;
int smp_val;
if (!global.tune.nb_stk_ctr) {
memprintf(err, "Cannot use '%s', stick-counters are disabled via tune.stick-counters", args[*arg-1]);
return ACT_RET_PRS_ERR;
}
cmd_name += strlen("sc-set-gpt");
if (*cmd_name == '(') {
cmd_name++; /* skip the '(' */
@ -2757,9 +2773,9 @@ static enum act_parse_ret parse_set_gpt(const char **args, int *arg, struct prox
return ACT_RET_PRS_ERR;
}
if (rule->arg.gpt.sc >= MAX_SESS_STKCTR) {
if (rule->arg.gpt.sc >= global.tune.nb_stk_ctr) {
memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d",
args[*arg-1], MAX_SESS_STKCTR-1);
args[*arg-1], global.tune.nb_stk_ctr-1);
return ACT_RET_PRS_ERR;
}
}
@ -2783,9 +2799,9 @@ static enum act_parse_ret parse_set_gpt(const char **args, int *arg, struct prox
return ACT_RET_PRS_ERR;
}
if (rule->arg.gpt.sc >= MAX_SESS_STKCTR) {
if (rule->arg.gpt.sc >= global.tune.nb_stk_ctr) {
memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d",
args[*arg-1], MAX_SESS_STKCTR-1);
args[*arg-1], global.tune.nb_stk_ctr-1);
return ACT_RET_PRS_ERR;
}
}
@ -2914,15 +2930,19 @@ smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg
* the sc[0-9]_ form, or even higher using sc_(num) if needed.
* args[arg] is the first optional argument. We first lookup the
* ctr form the stream, then from the session if it was not there.
* But we must be sure the counter does not exceed MAX_SESS_STKCTR.
* But we must be sure the counter does not exceed global.tune.nb_stk_ctr.
*/
if (num >= MAX_SESS_STKCTR)
if (num >= global.tune.nb_stk_ctr)
return NULL;
if (strm)
stkptr = NULL;
if (strm && strm->stkctr)
stkptr = &strm->stkctr[num];
if (!strm || !stkctr_entry(stkptr)) {
stkptr = &sess->stkctr[num];
if (!strm || !stkptr || !stkctr_entry(stkptr)) {
if (sess->stkctr)
stkptr = &sess->stkctr[num];
else
return NULL;
if (!stkctr_entry(stkptr))
return NULL;
}
@ -4990,6 +5010,45 @@ static void cli_release_show_table(struct appctx *appctx)
}
}
static int stk_parse_stick_counters(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
char *error;
int counters;
counters = strtol(args[1], &error, 10);
if (*error != 0) {
memprintf(err, "%s: '%s' is an invalid number", args[0], args[1]);
return -1;
}
if (counters < 0) {
memprintf(err, "%s: the number of stick-counters may not be negative (was %d)", args[0], counters);
return -1;
}
global.tune.nb_stk_ctr = counters;
return 0;
}
/* This function creates the stk_ctr pools after the configuration parsing. It
* returns 0 on success otherwise ERR_*. If nb_stk_ctr is 0, the pool remains
* NULL.
*/
static int stkt_create_stk_ctr_pool(void)
{
if (!global.tune.nb_stk_ctr)
return 0;
pool_head_stk_ctr = create_pool("stk_ctr", sizeof(*((struct session*)0)->stkctr) * global.tune.nb_stk_ctr, MEM_F_SHARED);
if (!pool_head_stk_ctr) {
ha_alert("out of memory while creating the stick-counters pool.\n");
return ERR_ABORT;
}
return 0;
}
static void stkt_late_init(void)
{
struct sample_fetch *f;
@ -4997,6 +5056,7 @@ static void stkt_late_init(void)
f = find_sample_fetch("src", strlen("src"));
if (f)
smp_fetch_src = f->process;
hap_register_post_check(stkt_create_stk_ctr_pool);
}
INITCALL0(STG_INIT, stkt_late_init);
@ -5273,3 +5333,10 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
}};
INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "tune.stick-counters", stk_parse_stick_counters },
{ /* END */ }
}};
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);

View File

@ -388,13 +388,20 @@ struct stream *stream_new(struct session *sess, struct stconn *sc, struct buffer
s->last_rule_file = NULL;
s->last_rule_line = 0;
/* Copy SC counters for the stream. We don't touch refcounts because
* any reference we have is inherited from the session. Since the stream
* doesn't exist without the session, the session's existence guarantees
* we don't lose the entry. During the store operation, the stream won't
* touch these ones.
*/
memcpy(s->stkctr, sess->stkctr, sizeof(s->stkctr));
s->stkctr = NULL;
if (pool_head_stk_ctr) {
s->stkctr = pool_alloc(pool_head_stk_ctr);
if (!s->stkctr)
goto out_fail_alloc;
/* Copy SC counters for the stream. We don't touch refcounts because
* any reference we have is inherited from the session. Since the stream
* doesn't exist without the session, the session's existence guarantees
* we don't lose the entry. During the store operation, the stream won't
* touch these ones.
*/
memcpy(s->stkctr, sess->stkctr, sizeof(s->stkctr[0]) * global.tune.nb_stk_ctr);
}
s->sess = sess;
@ -582,6 +589,8 @@ struct stream *stream_new(struct session *sess, struct stconn *sc, struct buffer
out_fail_attach_scf:
task_destroy(t);
out_fail_alloc:
if (s)
pool_free(pool_head_stk_ctr, s->stkctr);
pool_free(pool_head_stream, s);
DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_NEW|STRM_EV_STRM_ERR);
return NULL;
@ -701,6 +710,7 @@ void stream_free(struct stream *s)
vars_prune(&s->vars_reqres, s->sess, s);
stream_store_counters(s);
pool_free(pool_head_stk_ctr, s->stkctr);
list_for_each_entry_safe(bref, back, &s->back_refs, users) {
/* we have to unlink all watchers. We must not relink them if
@ -797,7 +807,7 @@ void stream_process_counters(struct stream *s)
if (sess->listener && sess->listener->counters)
_HA_ATOMIC_ADD(&sess->listener->counters->bytes_in, bytes);
for (i = 0; i < MAX_SESS_STKCTR; i++) {
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
if (!stkctr_inc_bytes_in_ctr(&s->stkctr[i], bytes))
stkctr_inc_bytes_in_ctr(&sess->stkctr[i], bytes);
}
@ -815,7 +825,7 @@ void stream_process_counters(struct stream *s)
if (sess->listener && sess->listener->counters)
_HA_ATOMIC_ADD(&sess->listener->counters->bytes_out, bytes);
for (i = 0; i < MAX_SESS_STKCTR; i++) {
for (i = 0; i < global.tune.nb_stk_ctr; i++) {
if (!stkctr_inc_bytes_out_ctr(&s->stkctr[i], bytes))
stkctr_inc_bytes_out_ctr(&sess->stkctr[i], bytes);
}

View File

@ -1058,7 +1058,7 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
memprintf(err,
"'%s %s' expects 'accept', 'reject', 'capture', 'expect-proxy', 'expect-netscaler-cip', 'track-sc0' ... 'track-sc%d', %s "
"in %s '%s' (got '%s').%s%s%s\n",
args[0], args[1], MAX_SESS_STKCTR-1,
args[0], args[1], global.tune.nb_stk_ctr-1,
trash.area, proxy_type_str(curpx),
curpx->id, args[arg],
best ? " Did you mean '" : "",