Willy Tarreau 758cb450a2 OPTIM: sink: drop the sink lock used to count drops
The sink lock was made to prevent event producers from passing while
there were other threads trying to print a "dropped" message, in order
to guarantee the absence of reordering. It has a serious impact however,
which is that all threads need to take the read lock when producing a
regular trace even when there's no reader.

This patch takes a different approach. The drop counter is shifted left
by one so that the lowest bit is used to indicate that one thread is
already taking care of trying to dump the counter. Threads only read
this value normally, and will only try to change it if it's non-null,
in which case they'll first check if they are the first ones trying to
dump it, otherwise will simply count another drop and leave. This has
a large benefit. First, it will avoid the locking that causes stalls
as soon as a slow reader is present. Second, it avoids any write on the
fast path as long as there's no drop. And it remains very lightweight
since we just need to add +2 or subtract 2*dropped in operations, while
offering the guarantee that the sink_write() has succeeded before
unlocking the counter.

While a reader was previously limiting the traffic to 11k RPS under
4C/8T, now we reach 36k RPS vs 14k with no reader, so readers will no
longer slow the traffic down and will instead even speed it up due to
avoiding the contention down the chain in the ring. The locking cost
dropped from ~75% to ~60% now (it's in ring_write now).
2024-03-09 11:23:52 +01:00

76 lines
2.6 KiB
C

/*
* include/haproxy/sink-t.h
* This file provides definitions for event sinks
*
* Copyright (C) 2000-2019 Willy Tarreau - w@1wt.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_SINK_T_H
#define _HAPROXY_SINK_T_H
#include <import/ist.h>
#include <haproxy/api-t.h>
#include <haproxy/log-t.h>
/* A sink may be of 4 distinct types :
* - file descriptor (such as stdout)
* - ring buffer, readable from CLI
*/
enum sink_type {
SINK_TYPE_NEW, // not yet initialized
SINK_TYPE_FD, // events sent to a file descriptor
SINK_TYPE_BUFFER, // events sent to a ring buffer
};
struct sink_forward_target {
struct server *srv; // used server
struct appctx *appctx; // appctx of current session
size_t ofs; // ring buffer reader offset
struct sink *sink; // the associated sink
struct sink_forward_target *next;
__decl_thread(HA_SPINLOCK_T lock); // lock to protect current struct
};
/* describes the configuration and current state of an event sink */
struct sink {
struct list sink_list; // position in the sink list
char *name; // sink name
char *desc; // sink description
char *store; // backing-store file when buffer
enum log_fmt fmt; // format expected by the sink
enum sink_type type; // type of storage
uint32_t maxlen; // max message length (truncated above)
struct proxy* forward_px; // internal proxy used to forward (only set when exclusive to sink)
struct sink_forward_target *sft; // sink forward targets
struct task *forward_task; // task to handle forward targets conns
struct sig_handler *forward_sighandler; /* signal handler */
struct {
struct ring *ring; // used by ring buffer and STRM sender
unsigned int dropped; // 2*dropped events since last one + 1 for purge in progress.
int fd; // fd num for FD type sink
} ctx;
};
#endif /* _HAPROXY_SINK_T_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/