blob: b5c35f44c8ff535e9339194bf91eb13d16637e07 [file] [log] [blame]
Willy Tarreaude5b33e2022-05-27 17:47:16 +020012022-05-27 - Stream layers in HAProxy 2.6
2
3
41. Background
5
6There are streams at plenty of levels in haproxy, essentially due to the
7introduction of multiplexed protocols which provide high-level streams on top
8of low-level streams, themselves either based on stream-oriented protocols or
9datagram-oriented protocols.
10
11The refactoring of the appctx and muxes that allowed to drop a lot of duplicate
12code between 2.5 and 2.6-dev6 raised another concern with some entities like
13"conn_stream" that were not specific to connections anymore, "endpoints" that
14became entities on their own, and "targets" whose life had been extended to
15last all along a connection.
16
17It was time to rename all such legacy entities introduced in 1.8 and which had
18turned particularly confusing over time as their roles evolved.
19
20
212. Naming principles
22
23The global renaming of some entities between streams and connections was
24articulated around several principles:
25
26 - avoid the confusing use of "context" in shared places. For example, the
27 endpoint's connection is in "ctx" and nothing makes it obvious that the
28 endpoint's context is a connection, especially when an applet is there.
29
30 - reserve relative nouns for pointers and not for types. "endpoint", just
31 like "owner" or "peer" is relative, but when accessed from a different
32 layer it starts to make no sense at all, or to make one believe it's
33 something else, particularly with void*.
34
35 - avoid too generic terms that have multiple meanings, or words that are
36 synonyms in a same place (e.g. "peer" and "remote", or "endpoint" and
37 "target"). If two synonyms are needed to designate two distinct entities,
38 there's probably a problem elsewhere, or the problem is poorly defined.
39
40 - make it clearer that all that is manipulated is related to streams. This
41 particularly important in sample fetch functions for example, which tend
42 to require low-level access and could be mislead in trying to follow the
43 wrong chain when trying to get information about a connection.
44
45 - use easily spellable short names that abbreviate unambiguously when used
46 together in adjacent contexts
47
48
493. Current state as of 2.6
50
51- when a name is required to designate the lower block that starts at the mux
52 stream or the appctx, it is spoken of as a "stream endpoint", and abbreviated
53 "se". It's okay because while "endpoint" itself is relative, "stream
54 endpoint" unequivocally designates one extremity of a stream. If a type is
55 needed for this in the future (e.g. via obj_type), then the type "stendp"
56 may be used. Before 2.6-dev6 there was no name for this, it was known as
57 conn_stream->ctx.
58
59- the 2.6-dev6 cs_endpoint which preserves the state of a mux stream or an
60 appctx and abstracts them in front of a conn_stream becomes a "stream
61 endpoint descriptor", of type "sedesc" and often abbreviated "sd", "sed"
62 or "ed". Its "target" pointer became "se" as per the rule above. Before
63 2.6-dev6, these elements were mixed with others inside conn_stream. From
64 the appctx it's called "sedesc" (few occurrences hence long name OK).
65
66- the conn_stream which is always attached to either a stream or a health check
67 and that is used to reach a mux or an applet becomes a "stream connector" of
68 type "stconn", generally abbreviated "sc". Its "endp" pointer becomes
69 "sedesc" as per the rule above, and that one has a back pointer "sc". The
70 stream uses "scf" and "scb" as the respective front and back pointers to the
71 stconns. Prior to 2.6-dev6, these parts were split between conn_stream and
72 stream_interface.
73
74- the sedesc's "ctx" which is solely used to store the connection as of now, is
75 renamed "conn" to void any doubt in the context of applets or even muxes. In
76 the future the connection should be attached to the "se" instead and this
77 pointer should disappear (or be recycled for anything else).
78
79The new 2.6 model looks like this:
80
81 +------------------------+
82 | stream or health check |
83 +------------------------+
84 ^ \ scf, scb
85 / \
86 | |
87 \ /
88 app \ v
89 +----------+
90 | stconn |
91 +----------+
92 ^ \ sedesc
93 / \
94 . . . . | . . . | . . . . . split point (retries etc)
95 \ /
96 sc \ v
97 +----------+
98 flags <--| sedesc | : sedesc :
99 +----------+ ... +----------+
100 conn / ^ \ se ^ \
101 +------------+ / / \ | \
102 | connection |<--' | | ... OR ... | |
103 +------------+ \ / \ |
104 mux| ^ |ctx sd \ v : sedesc \ v
105 | | | +----------------------+ \ # +----------+ svcctx
106 | | | | mux stream or appctx | | # | appctx |--.
107 | | | +----------------------+ | # +----------+ |
108 | | | ^ | / private # : : |
109 v | | | v > to the # +----------+ |
110 mux_ops | | +----------------+ \ mux # | svcctx |<-'
111 | +---->| mux connection | ) # +----------+
112 +------ +----------------+ / #
113
114Stream descriptors may exist in the following modes:
115 - .conn = NULL, .se = NULL : backend, not connection attempt yet
116 - .conn = NULL, .se = <appctx> : frontend or backend, applet
117 - .conn = <conn>, .se = NULL : backend, connection in progress
118 - .conn = <conn>, .se = <muxs> : frontend or backend, connected
119
120Notes:
121 - for historical reasons (connect, forced protocol upgrades, etc), during a
122 connection setup or a rule-based protocol upgrade, the connection's "ctx"
123 may temporarily point to the stconn
124
125
1264. Invariants and cardinalities
127
128Usually a stream is created from an existing stconn from a mux or some applets,
129but may also be allocated first by other applets schedulers. After stream_new()
130a stream always has exactly one stconn per side (scf, scb), each of which has
131one ->sedesc. Each side is initialized with either one or no stream endpoint
132attached to the descriptor.
133
134Both applets and a mux stream always have a stream endpoint descriptor. AS SUCH
135IT IS NEVER NECESSARY TO TEST FOR THE EXISTENCE OF THE SEDESC FROM ANY SIDE, IT
136ALWAYS EXISTS. This explains why as much as possible it's preferable to use the
137sedesc to access flags and statuses from any side, rather than bouncing via the
138stconn.
139
140An applet's app layer is always a stream, which means that there are always
141channels accessible above, and there is always an opposite stream connector and
142a stream endpoint descriptor. As such, it's always safe for an applet to access
143the other side using sc_opposite().
144
145When an outgoing connection is in the process of being established, the backend
146side sedesc has its ->conn pointer pointing to the pending connection, and no
147->se. Once the connection is established and a mux is chosen, it's attached to
148the ->se. If an applet is used instead of a mux, the appctx is attached to the
149sedesc's ->se and ->conn remains NULL.
150
151If either side wants to detach from the other, it must allocate a new virgin
152sedesc to replace the existing one, and leave the existing one to the endpoint,
153since it continues to describe the stream endpoint. The stconn keeps its state
154(modulo the updates related to the disconnection). The previous sedesc points
155to a NULL stconn. For example, disconnecting from a backend mux will leave the
156entities like this:
157
158 +------------------------+
159 | stream or health check |
160 +------------------------+
161 ^ \ scf, scb
162 / \
163 | |
164 \ /
165 app \ v
166 +----------+
167 | stconn |
168 +----------+
169 ^ \ sedesc
170 / \
171 NULL | |
172 ^ \ /
173 sc | / sc \ v
174 +----------+ / +----------+
175 flags <--| sedesc1 | . . . . . | sedesc2 |--> flags
176 +----------+ / +----------+
177 conn / ^ \ se / conn / \ se
178 +------------+ / / \ | |
179 | connection |<--' | | v v
180 +------------+ \ / NULL NULL
181 mux| ^ |ctx sd \ v
182 | | | +----------------------+
183 | | | | mux stream or appctx |
184 | | | +----------------------+
185 | | | ^ |
186 v | | | v
187 mux_ops | | +----------------+
188 | +---->| mux connection |
189 +------ +----------------+
190