| | varnish-cache/bin/varnishd/cache/cache_backend_probe.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2006 Verdens Gang AS |
2 |
|
* Copyright (c) 2006-2011 Varnish Software AS |
3 |
|
* All rights reserved. |
4 |
|
* |
5 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
6 |
|
* |
7 |
|
* SPDX-License-Identifier: BSD-2-Clause |
8 |
|
* |
9 |
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
* modification, are permitted provided that the following conditions |
11 |
|
* are met: |
12 |
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
* documentation and/or other materials provided with the distribution. |
17 |
|
* |
18 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
19 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
22 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 |
|
* SUCH DAMAGE. |
29 |
|
* |
30 |
|
* Poll backends for collection of health statistics |
31 |
|
* |
32 |
|
* We co-opt threads from the worker pool for probing the backends, |
33 |
18461 |
* but we want to avoid a potentially messy cleanup operation when we |
34 |
18461 |
* retire the backend, so the thread owns the health information, which |
35 |
18461 |
* the backend references, rather than the other way around. |
36 |
18461 |
* |
37 |
18461 |
*/ |
38 |
18461 |
|
39 |
18461 |
#include "config.h" |
40 |
18461 |
|
41 |
|
#include <poll.h> |
42 |
|
#include <stdio.h> |
43 |
|
#include <stdlib.h> |
44 |
|
|
45 |
|
#include "cache_varnishd.h" |
46 |
|
|
47 |
|
#include "vbh.h" |
48 |
|
#include "vcli_serve.h" |
49 |
|
#include "vsa.h" |
50 |
|
#include "vtcp.h" |
51 |
|
#include "vtim.h" |
52 |
|
|
53 |
|
#include "cache_backend.h" |
54 |
|
#include "cache_conn_pool.h" |
55 |
|
|
56 |
|
#include "VSC_vbe.h" |
57 |
|
|
58 |
|
/* Default averaging rate, we want something pretty responsive */ |
59 |
|
#define AVG_RATE 4 |
60 |
|
|
61 |
|
struct vbp_target { |
62 |
|
unsigned magic; |
63 |
|
#define VBP_TARGET_MAGIC 0x6b7cb656 |
64 |
|
|
65 |
|
VRT_BACKEND_PROBE_FIELDS() |
66 |
|
|
67 |
|
struct backend *backend; |
68 |
|
struct conn_pool *conn_pool; |
69 |
|
|
70 |
|
char *req; |
71 |
|
int req_len; |
72 |
|
|
73 |
|
char resp_buf[128]; |
74 |
|
unsigned good; |
75 |
|
|
76 |
|
/* Collected statistics */ |
77 |
|
#define BITMAP(n, c, t, b) uintmax_t n; |
78 |
|
#include "tbl/backend_poll.h" |
79 |
|
|
80 |
|
vtim_dur last; |
81 |
|
vtim_dur avg; |
82 |
|
double rate; |
83 |
|
|
84 |
|
vtim_real due; |
85 |
|
int running; |
86 |
|
int heap_idx; |
87 |
|
struct pool_task task[1]; |
88 |
|
}; |
89 |
|
|
90 |
|
static struct lock vbp_mtx; |
91 |
|
static pthread_cond_t vbp_cond; |
92 |
|
static struct vbh *vbp_heap; |
93 |
|
|
94 |
|
static const unsigned char vbp_proxy_local[] = { |
95 |
|
0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, |
96 |
|
0x55, 0x49, 0x54, 0x0a, 0x20, 0x00, 0x00, 0x00, |
97 |
|
}; |
98 |
|
|
99 |
|
/*--------------------------------------------------------------------*/ |
100 |
|
|
101 |
|
static void |
102 |
150 |
vbp_delete(struct vbp_target *vt) |
103 |
|
{ |
104 |
150 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
105 |
|
|
106 |
|
#define DN(x) /**/ |
107 |
150 |
VRT_BACKEND_PROBE_HANDLE(); |
108 |
|
#undef DN |
109 |
150 |
VCP_Rel(&vt->conn_pool); |
110 |
150 |
free(vt->req); |
111 |
150 |
FREE_OBJ(vt); |
112 |
150 |
} |
113 |
|
|
114 |
|
|
115 |
|
/*-------------------------------------------------------------------- |
116 |
|
* Record pokings... |
117 |
|
*/ |
118 |
|
|
119 |
|
static void |
120 |
9671 |
vbp_start_poke(struct vbp_target *vt) |
121 |
|
{ |
122 |
9671 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
123 |
|
|
124 |
|
#define BITMAP(n, c, t, b) \ |
125 |
|
vt->n <<= 1; |
126 |
|
#include "tbl/backend_poll.h" |
127 |
|
|
128 |
9671 |
vt->last = 0; |
129 |
9671 |
vt->resp_buf[0] = '\0'; |
130 |
9671 |
} |
131 |
|
|
132 |
|
static void |
133 |
9590 |
vbp_has_poked(struct vbp_target *vt) |
134 |
|
{ |
135 |
|
unsigned i, j; |
136 |
|
uint64_t u; |
137 |
|
|
138 |
9590 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
139 |
|
|
140 |
|
/* Calculate exponential average */ |
141 |
9590 |
if (vt->happy & 1) { |
142 |
8407 |
if (vt->rate < AVG_RATE) |
143 |
4408 |
vt->rate += 1.0; |
144 |
8407 |
vt->avg += (vt->last - vt->avg) / vt->rate; |
145 |
8407 |
} |
146 |
|
|
147 |
9590 |
u = vt->happy; |
148 |
80147 |
for (i = j = 0; i < vt->window; i++) { |
149 |
70557 |
if (u & 1) |
150 |
38950 |
j++; |
151 |
70557 |
u >>= 1; |
152 |
70557 |
} |
153 |
9590 |
vt->good = j; |
154 |
9590 |
} |
155 |
|
|
156 |
|
void |
157 |
6590 |
VBP_Update_Backend(struct vbp_target *vt) |
158 |
|
{ |
159 |
6590 |
unsigned i = 0, chg; |
160 |
|
char bits[10]; |
161 |
|
|
162 |
6590 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
163 |
|
|
164 |
|
#define BITMAP(n, c, t, b) \ |
165 |
|
bits[i++] = (vt->n & 1) ? c : '-'; |
166 |
|
#include "tbl/backend_poll.h" |
167 |
6590 |
bits[i] = '\0'; |
168 |
6590 |
assert(i < sizeof bits); |
169 |
|
|
170 |
6590 |
Lck_Lock(&vbp_mtx); |
171 |
6590 |
if (vt->backend == NULL) { |
172 |
0 |
Lck_Unlock(&vbp_mtx); |
173 |
0 |
return; |
174 |
|
} |
175 |
|
|
176 |
6590 |
i = (vt->good < vt->threshold); |
177 |
6590 |
chg = (i != vt->backend->sick); |
178 |
6590 |
vt->backend->sick = i; |
179 |
|
|
180 |
6590 |
AN(vt->backend->vcl_name); |
181 |
13180 |
VSL(SLT_Backend_health, NO_VXID, |
182 |
|
"%s %s %s %s %u %u %u %.6f %.6f \"%s\"", |
183 |
6590 |
vt->backend->vcl_name, chg ? "Went" : "Still", |
184 |
6590 |
i ? "sick" : "healthy", bits, |
185 |
6590 |
vt->good, vt->threshold, vt->window, |
186 |
6590 |
vt->last, vt->avg, vt->resp_buf); |
187 |
6590 |
vt->backend->vsc->happy = vt->happy; |
188 |
6590 |
if (chg) |
189 |
1685 |
vt->backend->changed = VTIM_real(); |
190 |
6590 |
Lck_Unlock(&vbp_mtx); |
191 |
6590 |
} |
192 |
|
|
193 |
|
static void |
194 |
1875 |
vbp_reset(struct vbp_target *vt) |
195 |
|
{ |
196 |
|
unsigned u; |
197 |
|
|
198 |
1875 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
199 |
1875 |
vt->avg = 0.0; |
200 |
1875 |
vt->rate = 0.0; |
201 |
|
#define BITMAP(n, c, t, b) \ |
202 |
|
vt->n = 0; |
203 |
|
#include "tbl/backend_poll.h" |
204 |
|
|
205 |
6750 |
for (u = 0; u < vt->initial; u++) { |
206 |
4875 |
vbp_start_poke(vt); |
207 |
4875 |
vt->happy |= 1; |
208 |
4875 |
vbp_has_poked(vt); |
209 |
4875 |
} |
210 |
1875 |
} |
211 |
|
|
212 |
|
/*-------------------------------------------------------------------- |
213 |
|
* Poke one backend, once, but possibly at both IPv4 and IPv6 addresses. |
214 |
|
* |
215 |
|
* We do deliberately not use the stuff in cache_backend.c, because we |
216 |
|
* want to measure the backends response without local distractions. |
217 |
|
*/ |
218 |
|
|
219 |
|
static int |
220 |
4814 |
vbp_write(struct vbp_target *vt, int *sock, const void *buf, size_t len) |
221 |
|
{ |
222 |
|
int i; |
223 |
|
|
224 |
4814 |
i = write(*sock, buf, len); |
225 |
4814 |
VTCP_Assert(i); |
226 |
4814 |
if (i != len) { |
227 |
0 |
if (i < 0) { |
228 |
0 |
vt->err_xmit |= 1; |
229 |
0 |
bprintf(vt->resp_buf, "Write error %d (%s)", |
230 |
|
errno, VAS_errtxt(errno)); |
231 |
0 |
} else { |
232 |
0 |
bprintf(vt->resp_buf, |
233 |
|
"Short write (%d/%zu) error %d (%s)", |
234 |
|
i, len, errno, VAS_errtxt(errno)); |
235 |
|
} |
236 |
0 |
VTCP_close(sock); |
237 |
0 |
return (-1); |
238 |
|
} |
239 |
4814 |
return (0); |
240 |
4814 |
} |
241 |
|
|
242 |
|
static int |
243 |
154 |
vbp_write_proxy_v1(struct vbp_target *vt, int *sock) |
244 |
|
{ |
245 |
|
char buf[105]; /* maximum size for a TCP6 PROXY line with null char */ |
246 |
|
char addr[VTCP_ADDRBUFSIZE]; |
247 |
|
char port[VTCP_PORTBUFSIZE]; |
248 |
154 |
char vsabuf[vsa_suckaddr_len]; |
249 |
|
const struct suckaddr *sua; |
250 |
|
int proto; |
251 |
|
struct vsb vsb; |
252 |
|
|
253 |
154 |
sua = VSA_getsockname(*sock, vsabuf, sizeof vsabuf); |
254 |
154 |
AN(sua); |
255 |
154 |
AN(VSB_init(&vsb, buf, sizeof buf)); |
256 |
|
|
257 |
154 |
proto = VSA_Get_Proto(sua); |
258 |
154 |
if (proto == AF_INET || proto == AF_INET6) { |
259 |
79 |
VTCP_name(sua, addr, sizeof addr, port, sizeof port); |
260 |
79 |
VSB_printf(&vsb, "PROXY %s %s %s %s %s\r\n", |
261 |
79 |
proto == AF_INET ? "TCP4" : "TCP6", |
262 |
79 |
addr, addr, port, port); |
263 |
79 |
} else { |
264 |
75 |
VSB_cat(&vsb, "PROXY UNKNOWN\r\n"); |
265 |
|
} |
266 |
154 |
AZ(VSB_finish(&vsb)); |
267 |
|
|
268 |
154 |
VSB_fini(&vsb); |
269 |
154 |
return (vbp_write(vt, sock, buf, strlen(buf))); |
270 |
154 |
} |
271 |
|
|
272 |
|
static void |
273 |
4771 |
vbp_poke(struct vbp_target *vt) |
274 |
|
{ |
275 |
|
int s, tmo, i, proxy_header, err; |
276 |
|
vtim_real t_start, t_now, t_end; |
277 |
|
unsigned rlen, resp; |
278 |
|
char buf[8192], *p; |
279 |
4771 |
struct pollfd pfda[1], *pfd = pfda; |
280 |
|
const struct suckaddr *sa; |
281 |
|
|
282 |
4771 |
t_start = t_now = VTIM_real(); |
283 |
4771 |
t_end = t_start + vt->timeout; |
284 |
|
|
285 |
4771 |
s = VCP_Open(vt->conn_pool, t_end - t_now, &sa, &err); |
286 |
4771 |
if (s < 0) { |
287 |
303 |
bprintf(vt->resp_buf, "Open error %d (%s)", err, VAS_errtxt(err)); |
288 |
303 |
Lck_Lock(&vbp_mtx); |
289 |
303 |
if (vt->backend) |
290 |
303 |
VBE_Connect_Error(vt->backend->vsc, err); |
291 |
303 |
Lck_Unlock(&vbp_mtx); |
292 |
303 |
return; |
293 |
|
} |
294 |
|
|
295 |
4468 |
i = VSA_Get_Proto(sa); |
296 |
4468 |
if (VSA_Compare(sa, bogo_ip) == 0) |
297 |
1584 |
vt->good_unix |= 1; |
298 |
2884 |
else if (i == AF_INET) |
299 |
2884 |
vt->good_ipv4 |= 1; |
300 |
0 |
else if (i == AF_INET6) |
301 |
0 |
vt->good_ipv6 |= 1; |
302 |
|
else |
303 |
0 |
WRONG("Wrong probe protocol family"); |
304 |
|
|
305 |
4468 |
t_now = VTIM_real(); |
306 |
4468 |
tmo = (int)round((t_end - t_now) * 1e3); |
307 |
4468 |
if (tmo <= 0) { |
308 |
56 |
bprintf(vt->resp_buf, |
309 |
|
"Open timeout %.3fs exceeded by %.3fs", |
310 |
|
vt->timeout, t_now - t_end); |
311 |
56 |
VTCP_close(&s); |
312 |
56 |
return; |
313 |
|
} |
314 |
|
|
315 |
4412 |
Lck_Lock(&vbp_mtx); |
316 |
4412 |
if (vt->backend != NULL) |
317 |
4412 |
proxy_header = vt->backend->proxy_header; |
318 |
|
else |
319 |
0 |
proxy_header = -1; |
320 |
4412 |
Lck_Unlock(&vbp_mtx); |
321 |
|
|
322 |
4412 |
if (proxy_header < 0) { |
323 |
0 |
bprintf(vt->resp_buf, "%s", "No backend"); |
324 |
0 |
VTCP_close(&s); |
325 |
0 |
return; |
326 |
|
} |
327 |
|
|
328 |
|
/* Send the PROXY header */ |
329 |
4412 |
assert(proxy_header <= 2); |
330 |
4412 |
if (proxy_header == 1) { |
331 |
154 |
if (vbp_write_proxy_v1(vt, &s) != 0) |
332 |
0 |
return; |
333 |
4412 |
} else if (proxy_header == 2 && |
334 |
192 |
vbp_write(vt, &s, vbp_proxy_local, sizeof vbp_proxy_local) != 0) |
335 |
0 |
return; |
336 |
|
|
337 |
|
/* Send the request */ |
338 |
4412 |
if (vbp_write(vt, &s, vt->req, vt->req_len) != 0) |
339 |
0 |
return; |
340 |
|
|
341 |
4412 |
vt->good_xmit |= 1; |
342 |
|
|
343 |
4412 |
pfd->fd = s; |
344 |
4412 |
rlen = 0; |
345 |
8807 |
while (1) { |
346 |
8807 |
pfd->events = POLLIN; |
347 |
8807 |
pfd->revents = 0; |
348 |
8807 |
t_now = VTIM_real(); |
349 |
8807 |
tmo = (int)round((t_end - t_now) * 1e3); |
350 |
8807 |
if (tmo <= 0) { |
351 |
0 |
bprintf(vt->resp_buf, |
352 |
|
"Poll timeout %.3fs exceeded by %.3fs", |
353 |
|
vt->timeout, t_now - t_end); |
354 |
0 |
i = -1; |
355 |
0 |
break; |
356 |
|
} |
357 |
8807 |
i = poll(pfd, 1, tmo); |
358 |
8807 |
if (i <= 0) { |
359 |
505 |
if (!i) { |
360 |
505 |
if (!vt->exp_close) |
361 |
107 |
break; |
362 |
398 |
errno = ETIMEDOUT; |
363 |
398 |
} |
364 |
398 |
bprintf(vt->resp_buf, "Poll error %d (%s)", |
365 |
|
errno, VAS_errtxt(errno)); |
366 |
398 |
i = -1; |
367 |
398 |
break; |
368 |
|
} |
369 |
8302 |
if (rlen < sizeof vt->resp_buf) |
370 |
15444 |
i = read(s, vt->resp_buf + rlen, |
371 |
7722 |
sizeof vt->resp_buf - rlen); |
372 |
|
else |
373 |
580 |
i = read(s, buf, sizeof buf); |
374 |
8302 |
VTCP_Assert(i); |
375 |
8302 |
if (i <= 0) { |
376 |
3907 |
if (i < 0) |
377 |
32 |
bprintf(vt->resp_buf, "Read error %d (%s)", |
378 |
|
errno, VAS_errtxt(errno)); |
379 |
3907 |
break; |
380 |
|
} |
381 |
4395 |
rlen += i; |
382 |
|
} |
383 |
|
|
384 |
4412 |
VTCP_close(&s); |
385 |
|
|
386 |
4412 |
if (i < 0) { |
387 |
|
/* errno reported above */ |
388 |
430 |
vt->err_recv |= 1; |
389 |
430 |
return; |
390 |
|
} |
391 |
|
|
392 |
3982 |
if (rlen == 0) { |
393 |
150 |
bprintf(vt->resp_buf, "%s", "Empty response"); |
394 |
150 |
return; |
395 |
|
} |
396 |
|
|
397 |
|
/* So we have a good receive ... */ |
398 |
3832 |
t_now = VTIM_real(); |
399 |
3832 |
vt->last = t_now - t_start; |
400 |
3832 |
vt->good_recv |= 1; |
401 |
|
|
402 |
|
/* Now find out if we like the response */ |
403 |
3832 |
vt->resp_buf[sizeof vt->resp_buf - 1] = '\0'; |
404 |
3832 |
p = strchr(vt->resp_buf, '\r'); |
405 |
3832 |
if (p != NULL) |
406 |
3832 |
*p = '\0'; |
407 |
3832 |
p = strchr(vt->resp_buf, '\n'); |
408 |
3832 |
if (p != NULL) |
409 |
0 |
*p = '\0'; |
410 |
|
|
411 |
3832 |
i = sscanf(vt->resp_buf, "HTTP/%*f %u ", &resp); |
412 |
|
|
413 |
3832 |
if (i == 1 && resp == vt->exp_status) |
414 |
3507 |
vt->happy |= 1; |
415 |
4771 |
} |
416 |
|
|
417 |
|
/*-------------------------------------------------------------------- |
418 |
|
*/ |
419 |
|
static void |
420 |
5540 |
vbp_heap_insert(struct vbp_target *vt) |
421 |
|
{ |
422 |
|
// Lck_AssertHeld(&vbp_mtx); |
423 |
5540 |
VBH_insert(vbp_heap, vt); |
424 |
5540 |
if (VBH_root(vbp_heap) == vt) |
425 |
5133 |
PTOK(pthread_cond_signal(&vbp_cond)); |
426 |
5540 |
} |
427 |
|
|
428 |
|
/*-------------------------------------------------------------------- |
429 |
|
*/ |
430 |
|
|
431 |
|
static void v_matchproto_(task_func_t) |
432 |
4715 |
vbp_task(struct worker *wrk, void *priv) |
433 |
|
{ |
434 |
|
struct vbp_target *vt; |
435 |
|
|
436 |
4715 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
437 |
4715 |
CAST_OBJ_NOTNULL(vt, priv, VBP_TARGET_MAGIC); |
438 |
|
|
439 |
4715 |
AN(vt->running); |
440 |
4715 |
AN(vt->req); |
441 |
4715 |
assert(vt->req_len > 0); |
442 |
|
|
443 |
4715 |
vbp_start_poke(vt); |
444 |
4715 |
vbp_poke(vt); |
445 |
4715 |
vbp_has_poked(vt); |
446 |
4715 |
VBP_Update_Backend(vt); |
447 |
|
|
448 |
4715 |
Lck_Lock(&vbp_mtx); |
449 |
4715 |
if (vt->running < 0) { |
450 |
0 |
assert(vt->heap_idx == VBH_NOIDX); |
451 |
0 |
vbp_delete(vt); |
452 |
0 |
} else { |
453 |
4715 |
vt->running = 0; |
454 |
4715 |
if (vt->heap_idx != VBH_NOIDX) { |
455 |
4690 |
vt->due = VTIM_real() + vt->interval; |
456 |
4690 |
VBH_delete(vbp_heap, vt->heap_idx); |
457 |
4690 |
vbp_heap_insert(vt); |
458 |
4690 |
} |
459 |
|
} |
460 |
4715 |
Lck_Unlock(&vbp_mtx); |
461 |
4715 |
} |
462 |
|
|
463 |
|
/*-------------------------------------------------------------------- |
464 |
|
*/ |
465 |
|
|
466 |
|
static void * v_matchproto_(bgthread_t) |
467 |
0 |
vbp_thread(struct worker *wrk, void *priv) |
468 |
|
{ |
469 |
|
vtim_real now, nxt; |
470 |
|
struct vbp_target *vt; |
471 |
|
int r; |
472 |
|
|
473 |
0 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
474 |
0 |
AZ(priv); |
475 |
0 |
Lck_Lock(&vbp_mtx); |
476 |
42384 |
while (1) { |
477 |
42384 |
now = VTIM_real(); |
478 |
42384 |
vt = VBH_root(vbp_heap); |
479 |
42384 |
if (vt == NULL) { |
480 |
22750 |
nxt = 8.192 + now; |
481 |
22750 |
(void)Lck_CondWaitUntil(&vbp_cond, &vbp_mtx, nxt); |
482 |
42384 |
} else if (vt->due > now) { |
483 |
11956 |
nxt = vt->due; |
484 |
11956 |
vt = NULL; |
485 |
11956 |
(void)Lck_CondWaitUntil(&vbp_cond, &vbp_mtx, nxt); |
486 |
11956 |
} else { |
487 |
7678 |
VBH_delete(vbp_heap, vt->heap_idx); |
488 |
7678 |
vt->due = now + vt->interval; |
489 |
7678 |
VBH_insert(vbp_heap, vt); |
490 |
7678 |
if (!vt->running) { |
491 |
4796 |
vt->running = 1; |
492 |
4796 |
vt->task->func = vbp_task; |
493 |
4796 |
vt->task->priv = vt; |
494 |
4796 |
Lck_Unlock(&vbp_mtx); |
495 |
4796 |
r = Pool_Task_Any(vt->task, TASK_QUEUE_REQ); |
496 |
4796 |
Lck_Lock(&vbp_mtx); |
497 |
4796 |
if (r) |
498 |
0 |
vt->running = 0; |
499 |
4796 |
} |
500 |
|
} |
501 |
|
} |
502 |
|
NEEDLESS(Lck_Unlock(&vbp_mtx)); |
503 |
|
NEEDLESS(return (NULL)); |
504 |
|
} |
505 |
|
|
506 |
|
|
507 |
|
/*-------------------------------------------------------------------- |
508 |
|
* Cli functions |
509 |
|
*/ |
510 |
|
|
511 |
|
static void |
512 |
975 |
vbp_bitmap(struct vsb *vsb, char c, uint64_t map, const char *lbl) |
513 |
|
{ |
514 |
|
int i; |
515 |
975 |
uint64_t u = (1ULL << 63); |
516 |
|
|
517 |
975 |
VSB_cat(vsb, " "); |
518 |
63375 |
for (i = 0; i < 64; i++) { |
519 |
62400 |
if (map & u) |
520 |
6227 |
VSB_putc(vsb, c); |
521 |
|
else |
522 |
56173 |
VSB_putc(vsb, '-'); |
523 |
62400 |
map <<= 1; |
524 |
62400 |
} |
525 |
975 |
VSB_printf(vsb, " %s\n", lbl); |
526 |
975 |
} |
527 |
|
|
528 |
|
/*lint -e{506} constant value boolean */ |
529 |
|
/*lint -e{774} constant value boolean */ |
530 |
|
void |
531 |
1950 |
VBP_Status(struct vsb *vsb, const struct backend *be, int details, int json) |
532 |
|
{ |
533 |
|
struct vbp_target *vt; |
534 |
|
|
535 |
1950 |
CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC); |
536 |
1950 |
vt = be->probe; |
537 |
1950 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
538 |
|
|
539 |
1950 |
if (!details) { |
540 |
1625 |
if (json) |
541 |
150 |
VSB_printf(vsb, "[%u, %u, \"%s\"]", |
542 |
75 |
vt->good, vt->window, |
543 |
75 |
vt->backend->sick ? "sick" : "healthy"); |
544 |
|
else |
545 |
3100 |
VSB_printf(vsb, "%u/%u\t%s", vt->good, vt->window, |
546 |
1550 |
vt->backend->sick ? "sick" : "healthy"); |
547 |
1625 |
return; |
548 |
|
} |
549 |
|
|
550 |
325 |
if (json) { |
551 |
50 |
VSB_cat(vsb, "{\n"); |
552 |
50 |
VSB_indent(vsb, 2); |
553 |
|
#define BITMAP(nn, cc, tt, bb) \ |
554 |
|
VSB_printf(vsb, "\"bits_%c\": %ju,\n", cc, vt->nn); |
555 |
|
#include "tbl/backend_poll.h" |
556 |
50 |
VSB_printf(vsb, "\"good\": %u,\n", vt->good); |
557 |
50 |
VSB_printf(vsb, "\"threshold\": %u,\n", vt->threshold); |
558 |
50 |
VSB_printf(vsb, "\"window\": %u", vt->window); |
559 |
50 |
VSB_indent(vsb, -2); |
560 |
50 |
VSB_cat(vsb, "\n"); |
561 |
50 |
VSB_cat(vsb, "},\n"); |
562 |
50 |
return; |
563 |
|
} |
564 |
|
|
565 |
550 |
VSB_printf(vsb, |
566 |
|
"\n Current states good: %2u threshold: %2u window: %2u\n", |
567 |
275 |
vt->good, vt->threshold, vt->window); |
568 |
550 |
VSB_printf(vsb, |
569 |
275 |
" Average response time of good probes: %.6f\n", vt->avg); |
570 |
275 |
VSB_cat(vsb, |
571 |
|
" Oldest ======================" |
572 |
|
"============================ Newest\n"); |
573 |
|
|
574 |
|
#define BITMAP(n, c, t, b) \ |
575 |
|
if ((vt->n != 0) || (b)) \ |
576 |
|
vbp_bitmap(vsb, (c), vt->n, (t)); |
577 |
|
#include "tbl/backend_poll.h" |
578 |
1950 |
} |
579 |
|
|
580 |
|
/*-------------------------------------------------------------------- |
581 |
|
* Build request from probe spec |
582 |
|
*/ |
583 |
|
|
584 |
|
static void |
585 |
850 |
vbp_build_req(struct vbp_target *vt, const struct vrt_backend_probe *vbp, |
586 |
|
const struct backend *be) |
587 |
|
{ |
588 |
|
struct vsb *vsb; |
589 |
|
|
590 |
850 |
vsb = VSB_new_auto(); |
591 |
850 |
AN(vsb); |
592 |
850 |
VSB_clear(vsb); |
593 |
850 |
if (vbp->request != NULL) { |
594 |
50 |
VSB_cat(vsb, vbp->request); |
595 |
50 |
} else { |
596 |
800 |
AN(be->hosthdr); |
597 |
1600 |
VSB_printf(vsb, |
598 |
|
"GET %s HTTP/1.1\r\n" |
599 |
|
"Host: %s\r\n" |
600 |
|
"Connection: close\r\n" |
601 |
|
"\r\n", |
602 |
800 |
vbp->url != NULL ? vbp->url : "/", |
603 |
800 |
be->hosthdr); |
604 |
|
} |
605 |
850 |
AZ(VSB_finish(vsb)); |
606 |
850 |
vt->req = strdup(VSB_data(vsb)); |
607 |
850 |
AN(vt->req); |
608 |
850 |
vt->req_len = VSB_len(vsb); |
609 |
850 |
VSB_destroy(&vsb); |
610 |
850 |
} |
611 |
|
|
612 |
|
/*-------------------------------------------------------------------- |
613 |
|
* Sanitize and set defaults |
614 |
|
* XXX: we could make these defaults parameters |
615 |
|
*/ |
616 |
|
|
617 |
|
static void |
618 |
850 |
vbp_set_defaults(struct vbp_target *vt, const struct vrt_backend_probe *vp) |
619 |
|
{ |
620 |
|
|
621 |
|
#define DN(x) do { vt->x = vp->x; } while (0) |
622 |
850 |
VRT_BACKEND_PROBE_HANDLE(); |
623 |
|
#undef DN |
624 |
|
|
625 |
850 |
if (vt->timeout == 0.0) |
626 |
650 |
vt->timeout = 2.0; |
627 |
850 |
if (vt->interval == 0.0) |
628 |
200 |
vt->interval = 5.0; |
629 |
850 |
if (vt->window == 0) |
630 |
400 |
vt->window = 8; |
631 |
850 |
if (vt->threshold == 0) |
632 |
400 |
vt->threshold = 3; |
633 |
850 |
if (vt->exp_status == 0) |
634 |
850 |
vt->exp_status = 200; |
635 |
|
|
636 |
850 |
if (vt->initial == ~0U) |
637 |
525 |
vt->initial = vt->threshold - 1; |
638 |
|
|
639 |
850 |
vt->initial = vmin(vt->initial, vt->threshold); |
640 |
850 |
} |
641 |
|
|
642 |
|
/*-------------------------------------------------------------------- |
643 |
|
*/ |
644 |
|
|
645 |
|
void |
646 |
1025 |
VBP_Control(const struct backend *be, int enable) |
647 |
|
{ |
648 |
|
struct vbp_target *vt; |
649 |
|
|
650 |
1025 |
CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC); |
651 |
1025 |
vt = be->probe; |
652 |
1025 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
653 |
|
|
654 |
1025 |
vbp_reset(vt); |
655 |
1025 |
VBP_Update_Backend(vt); |
656 |
|
|
657 |
1025 |
Lck_Lock(&vbp_mtx); |
658 |
1025 |
if (enable) { |
659 |
850 |
assert(vt->heap_idx == VBH_NOIDX); |
660 |
850 |
vt->due = VTIM_real(); |
661 |
850 |
vbp_heap_insert(vt); |
662 |
850 |
} else { |
663 |
175 |
assert(vt->heap_idx != VBH_NOIDX); |
664 |
175 |
VBH_delete(vbp_heap, vt->heap_idx); |
665 |
|
} |
666 |
1025 |
Lck_Unlock(&vbp_mtx); |
667 |
1025 |
} |
668 |
|
|
669 |
|
/*-------------------------------------------------------------------- |
670 |
|
* Insert/Remove/Use called from cache_backend.c |
671 |
|
*/ |
672 |
|
|
673 |
|
void |
674 |
850 |
VBP_Insert(struct backend *b, const struct vrt_backend_probe *vp, |
675 |
|
struct conn_pool *tp) |
676 |
|
{ |
677 |
|
struct vbp_target *vt; |
678 |
|
|
679 |
850 |
CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC); |
680 |
850 |
CHECK_OBJ_NOTNULL(vp, VRT_BACKEND_PROBE_MAGIC); |
681 |
|
|
682 |
850 |
AZ(b->probe); |
683 |
|
|
684 |
850 |
ALLOC_OBJ(vt, VBP_TARGET_MAGIC); |
685 |
850 |
XXXAN(vt); |
686 |
|
|
687 |
850 |
vt->conn_pool = tp; |
688 |
850 |
VCP_AddRef(vt->conn_pool); |
689 |
850 |
vt->backend = b; |
690 |
850 |
b->probe = vt; |
691 |
|
|
692 |
850 |
vbp_set_defaults(vt, vp); |
693 |
850 |
vbp_build_req(vt, vp, b); |
694 |
|
|
695 |
850 |
vbp_reset(vt); |
696 |
850 |
} |
697 |
|
|
698 |
|
void |
699 |
150 |
VBP_Remove(struct backend *be) |
700 |
|
{ |
701 |
|
struct vbp_target *vt; |
702 |
|
|
703 |
150 |
CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC); |
704 |
150 |
vt = be->probe; |
705 |
150 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
706 |
|
|
707 |
150 |
Lck_Lock(&vbp_mtx); |
708 |
150 |
be->sick = 1; |
709 |
150 |
be->probe = NULL; |
710 |
150 |
vt->backend = NULL; |
711 |
150 |
if (vt->running) { |
712 |
|
// task scheduled, it calls vbp_delete() |
713 |
0 |
vt->running = -1; |
714 |
0 |
vt = NULL; |
715 |
150 |
} else if (vt->heap_idx != VBH_NOIDX) { |
716 |
|
// task done, not yet rescheduled |
717 |
0 |
VBH_delete(vbp_heap, vt->heap_idx); |
718 |
0 |
} |
719 |
150 |
Lck_Unlock(&vbp_mtx); |
720 |
150 |
if (vt != NULL) { |
721 |
150 |
assert(vt->heap_idx == VBH_NOIDX); |
722 |
150 |
vbp_delete(vt); |
723 |
150 |
} |
724 |
150 |
} |
725 |
|
|
726 |
|
/*-------------------------------------------------------------------*/ |
727 |
|
|
728 |
|
static int v_matchproto_(vbh_cmp_t) |
729 |
1747 |
vbp_cmp(void *priv, const void *a, const void *b) |
730 |
|
{ |
731 |
|
const struct vbp_target *aa, *bb; |
732 |
|
int ar, br; |
733 |
|
|
734 |
1747 |
AZ(priv); |
735 |
1747 |
CAST_OBJ_NOTNULL(aa, a, VBP_TARGET_MAGIC); |
736 |
1747 |
CAST_OBJ_NOTNULL(bb, b, VBP_TARGET_MAGIC); |
737 |
|
|
738 |
1747 |
ar = aa->running == 0; |
739 |
1747 |
br = bb->running == 0; |
740 |
|
|
741 |
1747 |
if (ar != br) |
742 |
581 |
return (ar); |
743 |
|
|
744 |
1166 |
return (aa->due < bb->due); |
745 |
1747 |
} |
746 |
|
|
747 |
|
static void v_matchproto_(vbh_update_t) |
748 |
28593 |
vbp_update(void *priv, void *p, unsigned u) |
749 |
|
{ |
750 |
|
struct vbp_target *vt; |
751 |
|
|
752 |
28593 |
AZ(priv); |
753 |
28593 |
CAST_OBJ_NOTNULL(vt, p, VBP_TARGET_MAGIC); |
754 |
28593 |
vt->heap_idx = u; |
755 |
28593 |
} |
756 |
|
|
757 |
|
/*-------------------------------------------------------------------*/ |
758 |
|
|
759 |
|
void |
760 |
22197 |
VBP_Init(void) |
761 |
|
{ |
762 |
|
pthread_t thr; |
763 |
|
|
764 |
22197 |
Lck_New(&vbp_mtx, lck_probe); |
765 |
22197 |
vbp_heap = VBH_new(NULL, vbp_cmp, vbp_update); |
766 |
22197 |
AN(vbp_heap); |
767 |
22197 |
PTOK(pthread_cond_init(&vbp_cond, NULL)); |
768 |
22197 |
WRK_BgThread(&thr, "backend-poller", vbp_thread, NULL); |
769 |
22197 |
} |