/src/freeradius-server/src/lib/server/pool.c
Line | Count | Source |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License as published by |
4 | | * the Free Software Foundation; either version 2 of the License, or |
5 | | * (at your option) any later version. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** |
18 | | * @file pool.c |
19 | | * @brief Handle pools of connections (threads, sockets, etc.) |
20 | | * @note This API must be used by all modules in the public distribution that |
21 | | * maintain pools of connections. |
22 | | * |
23 | | * @copyright 2012 The FreeRADIUS server project |
24 | | * @copyright 2012 Alan DeKok (aland@deployingradius.com) |
25 | | */ |
26 | | RCSID("$Id: dc3679204298804cd2fcd222838ddfd44dc6bc20 $") |
27 | | |
28 | 0 | #define LOG_PREFIX pool->log_prefix |
29 | | |
30 | | #include <freeradius-devel/server/main_config.h> |
31 | | #include <freeradius-devel/server/modpriv.h> |
32 | | #include <freeradius-devel/server/trigger.h> |
33 | | |
34 | | #include <freeradius-devel/util/debug.h> |
35 | | |
36 | | #include <freeradius-devel/util/misc.h> |
37 | | |
38 | | |
39 | | typedef struct fr_pool_connection_s fr_pool_connection_t; |
40 | | |
41 | | static int connection_check(fr_pool_t *pool, request_t *request); |
42 | | static int max_dflt(CONF_PAIR **out, void *parent, CONF_SECTION *cs, fr_token_t quote, conf_parser_t const *rule); |
43 | | |
44 | | /** An individual connection within the connection pool |
45 | | * |
46 | | * Defines connection counters, timestamps, and holds a pointer to the |
47 | | * connection handle itself. |
48 | | * |
49 | | * @see fr_pool_t |
50 | | */ |
51 | | struct fr_pool_connection_s { |
52 | | fr_pool_connection_t *prev; //!< Previous connection in list. |
53 | | fr_pool_connection_t *next; //!< Next connection in list. |
54 | | fr_heap_index_t heap_id; //!< For the next connection heap. |
55 | | |
56 | | fr_time_t created; //!< Time connection was created. |
57 | | fr_time_t last_reserved; //!< Last time the connection was reserved. |
58 | | |
59 | | fr_time_t last_released; //!< Time the connection was released. |
60 | | |
61 | | uint32_t num_uses; //!< Number of times the connection has been reserved. |
62 | | uint64_t number; //!< Unique ID assigned when the connection is created, |
63 | | //!< these will monotonically increase over the |
64 | | //!< lifetime of the connection pool. |
65 | | void *connection; //!< Pointer to whatever the module uses for a connection |
66 | | //!< handle. |
67 | | bool in_use; //!< Whether the connection is currently reserved. |
68 | | |
69 | | bool needs_reconnecting; //!< Reconnect this connection before use. |
70 | | |
71 | | #ifdef PTHREAD_DEBUG |
72 | | pthread_t pthread_id; //!< When 'in_use == true'. |
73 | | #endif |
74 | | }; |
75 | | |
76 | | /** A connection pool |
77 | | * |
78 | | * Defines the configuration of the connection pool, all the counters and |
79 | | * timestamps related to the connection pool, the mutex that stops multiple |
80 | | * threads leaving the pool in an inconsistent state, and the callbacks |
81 | | * required to open, close and check the status of connections within the pool. |
82 | | * |
83 | | * @see connection |
84 | | */ |
85 | | struct fr_pool_s { |
86 | | int ref; //!< Reference counter to prevent connection |
87 | | //!< pool being freed multiple times. |
88 | | uint32_t start; //!< Number of initial connections. |
89 | | uint32_t min; //!< Minimum number of concurrent connections to keep open. |
90 | | uint32_t max; //!< Maximum number of concurrent connections to allow. |
91 | | uint32_t max_pending; //!< Max number of pending connections to allow. |
92 | | uint32_t spare; //!< Number of spare connections to try. |
93 | | uint64_t max_uses; //!< Maximum number of times a connection can be used |
94 | | //!< before being closed. |
95 | | uint32_t pending_window; //!< Sliding window of pending connections. |
96 | | |
97 | | fr_time_delta_t retry_delay; //!< seconds to delay re-open after a failed open. |
98 | | fr_time_delta_t cleanup_interval; //!< Initial timer for how often we sweep the pool |
99 | | //!< for free connections. (0 is infinite). |
100 | | fr_time_delta_t delay_interval; //!< When we next do a cleanup. Initialized to |
101 | | //!< cleanup_interval, and increase from there based |
102 | | //!< on the delay. |
103 | | fr_time_delta_t lifetime; //!< How long a connection can be open before being |
104 | | //!< closed (irrespective of whether it's idle or not). |
105 | | fr_time_delta_t idle_timeout; //!< How long a connection can be idle before |
106 | | //!< being closed. |
107 | | fr_time_delta_t connect_timeout; //!< New connection timeout, enforced by the create |
108 | | //!< callback. |
109 | | |
110 | | bool spread; //!< If true we spread requests over the connections, |
111 | | //!< using the connection released longest ago, first. |
112 | | |
113 | | fr_heap_t *heap; //!< For the next connection heap |
114 | | |
115 | | fr_pool_connection_t *head; //!< Start of the connection list. |
116 | | fr_pool_connection_t *tail; //!< End of the connection list. |
117 | | |
118 | | pthread_mutex_t mutex; //!< Mutex used to keep consistent state when making |
119 | | //!< modifications in threaded mode. |
120 | | pthread_cond_t done_spawn; //!< Threads that need to ensure no spawning is in progress, |
121 | | //!< should block on this condition if pending != 0. |
122 | | pthread_cond_t done_reconnecting; //!< Before calling the create callback, threads should |
123 | | //!< block on this condition if reconnecting == true. |
124 | | |
125 | | CONF_SECTION const *cs; //!< Configuration section holding the section of parsed |
126 | | //!< config file that relates to this pool. |
127 | | void *opaque; //!< Pointer to context data that will be passed to callbacks. |
128 | | |
129 | | char const *log_prefix; //!< Log prefix to prepend to all log messages created |
130 | | //!< by the connection pool code. |
131 | | |
132 | | bool triggers_enabled; //!< Whether we call the trigger functions. |
133 | | |
134 | | char const *trigger_prefix; //!< Prefix to prepend to names of all triggers |
135 | | //!< fired by the connection pool code. |
136 | | fr_pair_list_t trigger_args; //!< Arguments to make available in connection pool triggers. |
137 | | |
138 | | fr_time_delta_t held_trigger_min; //!< If a connection is held for less than the specified |
139 | | //!< period, fire a trigger. |
140 | | fr_time_delta_t held_trigger_max; //!< If a connection is held for longer than the specified |
141 | | //!< period, fire a trigger. |
142 | | |
143 | | fr_pool_connection_create_t create; //!< Function used to create new connections. |
144 | | fr_pool_connection_alive_t alive; //!< Function used to check status of connections. |
145 | | |
146 | | fr_pool_reconnect_t reconnect; //!< Called during connection pool reconnect. |
147 | | |
148 | | fr_pool_state_t state; //!< Stats and state of the connection pool. |
149 | | }; |
150 | | |
151 | | static const conf_parser_t pool_config[] = { |
152 | | { FR_CONF_OFFSET("start", fr_pool_t, start), .dflt = "0" }, |
153 | | { FR_CONF_OFFSET("min", fr_pool_t, min), .dflt = "0" }, |
154 | | { FR_CONF_OFFSET("max", fr_pool_t, max), .dflt_func = max_dflt }, |
155 | | { FR_CONF_OFFSET("max_pending", fr_pool_t, max_pending), .dflt = "0" }, |
156 | | { FR_CONF_OFFSET("spare", fr_pool_t, spare), .dflt = "3" }, |
157 | | { FR_CONF_OFFSET("uses", fr_pool_t, max_uses), .dflt = "0" }, |
158 | | { FR_CONF_OFFSET("lifetime", fr_pool_t, lifetime), .dflt = "0" }, |
159 | | { FR_CONF_OFFSET("cleanup_interval", fr_pool_t, cleanup_interval), .dflt = "30" }, |
160 | | { FR_CONF_OFFSET("idle_timeout", fr_pool_t, idle_timeout), .dflt = "60" }, |
161 | | { FR_CONF_OFFSET("connect_timeout", fr_pool_t, connect_timeout), .dflt = "3.0" }, |
162 | | { FR_CONF_OFFSET("held_trigger_min", fr_pool_t, held_trigger_min), .dflt = "0.0" }, |
163 | | { FR_CONF_OFFSET("held_trigger_max", fr_pool_t, held_trigger_max), .dflt = "0.5" }, |
164 | | { FR_CONF_OFFSET("retry_delay", fr_pool_t, retry_delay), .dflt = "1" }, |
165 | | { FR_CONF_OFFSET("spread", fr_pool_t, spread), .dflt = "no" }, |
166 | | CONF_PARSER_TERMINATOR |
167 | | }; |
168 | | |
169 | | static int max_dflt(CONF_PAIR **out, UNUSED void *parent, CONF_SECTION *cs, fr_token_t quote, conf_parser_t const *rule) |
170 | 0 | { |
171 | 0 | char *strvalue; |
172 | |
|
173 | 0 | strvalue = talloc_asprintf(NULL, "%u", main_config->max_workers); |
174 | 0 | *out = cf_pair_alloc(cs, rule->name1, strvalue, T_OP_EQ, T_BARE_WORD, quote); |
175 | 0 | talloc_free(strvalue); |
176 | |
|
177 | 0 | return 0; |
178 | 0 | } |
179 | | |
180 | | /** Order connections by reserved most recently |
181 | | */ |
182 | | static fr_cmp_ret_t last_reserved_cmp(void const *one, void const *two) |
183 | 0 | { |
184 | 0 | fr_pool_connection_t const *a = one, *b = two; |
185 | |
|
186 | 0 | return fr_time_cmp(a->last_reserved, b->last_reserved); |
187 | 0 | } |
188 | | |
189 | | /** Order connections by released longest ago |
190 | | */ |
191 | | static fr_cmp_ret_t last_released_cmp(void const *one, void const *two) |
192 | 0 | { |
193 | 0 | fr_pool_connection_t const *a = one, *b = two; |
194 | |
|
195 | 0 | return fr_time_cmp(a->last_released, b->last_released); |
196 | 0 | } |
197 | | |
198 | | /** Removes a connection from the connection list |
199 | | * |
200 | | * @note Must be called with the mutex held. |
201 | | * |
202 | | * @param[in] pool to modify. |
203 | | * @param[in] this Connection to delete. |
204 | | */ |
205 | | static void connection_unlink(fr_pool_t *pool, fr_pool_connection_t *this) |
206 | 0 | { |
207 | 0 | if (this->prev) { |
208 | 0 | fr_assert(pool->head != this); |
209 | 0 | this->prev->next = this->next; |
210 | 0 | } else { |
211 | 0 | fr_assert(pool->head == this); |
212 | 0 | pool->head = this->next; |
213 | 0 | } |
214 | 0 | if (this->next) { |
215 | 0 | fr_assert(pool->tail != this); |
216 | 0 | this->next->prev = this->prev; |
217 | 0 | } else { |
218 | 0 | fr_assert(pool->tail == this); |
219 | 0 | pool->tail = this->prev; |
220 | 0 | } |
221 | |
|
222 | 0 | this->prev = this->next = NULL; |
223 | 0 | } |
224 | | |
225 | | /** Adds a connection to the head of the connection list |
226 | | * |
227 | | * @note Must be called with the mutex held. |
228 | | * |
229 | | * @param[in] pool to modify. |
230 | | * @param[in] this Connection to add. |
231 | | */ |
232 | | static void connection_link_head(fr_pool_t *pool, fr_pool_connection_t *this) |
233 | 0 | { |
234 | 0 | fr_assert(pool != NULL); |
235 | 0 | fr_assert(this != NULL); |
236 | 0 | fr_assert(pool->head != this); |
237 | 0 | fr_assert(pool->tail != this); |
238 | |
|
239 | 0 | if (pool->head) { |
240 | 0 | pool->head->prev = this; |
241 | 0 | } |
242 | |
|
243 | 0 | this->next = pool->head; |
244 | 0 | this->prev = NULL; |
245 | 0 | pool->head = this; |
246 | 0 | if (!pool->tail) { |
247 | 0 | fr_assert(this->next == NULL); |
248 | 0 | pool->tail = this; |
249 | 0 | } else { |
250 | 0 | fr_assert(this->next != NULL); |
251 | 0 | } |
252 | 0 | } |
253 | | |
254 | | /** Send a connection pool trigger. |
255 | | * |
256 | | * @param[in] pool to send trigger for. |
257 | | * @param[in] event trigger name suffix. |
258 | | */ |
259 | | static inline void fr_pool_trigger(fr_pool_t *pool, char const *event) |
260 | 0 | { |
261 | 0 | char name[128]; |
262 | |
|
263 | 0 | fr_assert(pool != NULL); |
264 | 0 | fr_assert(event != NULL); |
265 | |
|
266 | 0 | if (!pool->triggers_enabled) return; |
267 | | |
268 | 0 | snprintf(name, sizeof(name), "%s.%s", pool->trigger_prefix, event); |
269 | 0 | trigger(unlang_interpret_get_thread_default(), pool->cs, NULL, name, true, &pool->trigger_args, pool); |
270 | 0 | } |
271 | | |
272 | | /** Find a connection handle in the connection list |
273 | | * |
274 | | * Walks over the list of connections searching for a specified connection |
275 | | * handle and returns the first connection that contains that pointer. |
276 | | * |
277 | | * @note Must be called with the mutex held. |
278 | | * |
279 | | * @param[in] pool to search in. |
280 | | * @param[in] conn handle to search for. |
281 | | * @return |
282 | | * - Connection containing the specified handle. |
283 | | * - NULL if no such connection was found. |
284 | | */ |
285 | | static fr_pool_connection_t *connection_find(fr_pool_t *pool, void *conn) |
286 | 0 | { |
287 | 0 | fr_pool_connection_t *this; |
288 | | |
289 | | /* |
290 | | * FIXME: This loop could be avoided if we passed a 'void |
291 | | * **connection' instead. We could use "offsetof" in |
292 | | * order to find top of the parent structure. |
293 | | */ |
294 | 0 | for (this = pool->head; this != NULL; this = this->next) { |
295 | 0 | if (this->connection == conn) { |
296 | | #ifdef PTHREAD_DEBUG |
297 | | pthread_t pthread_id; |
298 | | |
299 | | pthread_id = pthread_self(); |
300 | | fr_assert(pthread_equal(this->pthread_id, pthread_id) != 0); |
301 | | #endif |
302 | |
|
303 | 0 | fr_assert(this->in_use == true); |
304 | 0 | return this; |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | 0 | return NULL; |
309 | 0 | } |
310 | | |
311 | | /** Spawns a new connection |
312 | | * |
313 | | * Spawns a new connection using the create callback, and returns it for |
314 | | * adding to the connection list. |
315 | | * |
316 | | * @note Will call the 'open' trigger. |
317 | | * @note Must be called with the mutex held. Returns with the mutex held. |
318 | | * The mutex is released while the create callback runs. |
319 | | * |
320 | | * @param[in] pool to modify. |
321 | | * @param[in] request The current request. |
322 | | * @param[in] now Current time. |
323 | | * @param[in] in_use whether the new connection should be "in_use" or not |
324 | | * @return |
325 | | * - New connection struct. |
326 | | * - NULL on error. |
327 | | */ |
328 | | static fr_pool_connection_t *connection_spawn(fr_pool_t *pool, request_t *request, fr_time_t now, bool in_use) |
329 | 0 | { |
330 | 0 | uint64_t number; |
331 | 0 | uint32_t pending_window; |
332 | 0 | TALLOC_CTX *ctx; |
333 | |
|
334 | 0 | fr_pool_connection_t *this; |
335 | 0 | void *conn; |
336 | |
|
337 | 0 | fr_assert(pool != NULL); |
338 | 0 | fr_assert(pool->state.num <= pool->max); |
339 | | |
340 | | /* |
341 | | * If we have NO connections, and we've previously failed |
342 | | * opening connections, don't open multiple connections until |
343 | | * we successfully open at least one. |
344 | | */ |
345 | 0 | if ((pool->state.num == 0) && |
346 | 0 | pool->state.pending && |
347 | 0 | fr_time_gt(pool->state.last_failed, fr_time_wrap(0))) { |
348 | 0 | return NULL; |
349 | 0 | } |
350 | | |
351 | | /* |
352 | | * Don't spawn too many connections at the same time. |
353 | | */ |
354 | 0 | if ((pool->state.num + pool->state.pending) >= pool->max) { |
355 | 0 | ERROR("Cannot open new connection, already at max"); |
356 | 0 | return NULL; |
357 | 0 | } |
358 | | |
359 | | /* |
360 | | * If the last attempt failed, wait a bit before |
361 | | * retrying. |
362 | | */ |
363 | 0 | if (fr_time_gt(pool->state.last_failed, fr_time_wrap(0)) && |
364 | 0 | fr_time_gt(fr_time_add(pool->state.last_failed, pool->retry_delay), now)) { |
365 | 0 | bool complain = false; |
366 | 0 | fr_time_delta_t retry_in = fr_time_sub(fr_time_add(pool->state.last_failed, pool->retry_delay), now); |
367 | |
|
368 | 0 | if (fr_time_delta_gteq(fr_time_sub(now, pool->state.last_throttled), fr_time_delta_from_sec(1))) { |
369 | 0 | complain = true; |
370 | |
|
371 | 0 | pool->state.last_throttled = now; |
372 | 0 | } |
373 | |
|
374 | 0 | if (!fr_rate_limit_enabled() || complain) { |
375 | 0 | ERROR("Last connection attempt failed, waiting %pV seconds before retrying", |
376 | 0 | fr_box_time_delta(retry_in)); |
377 | 0 | } |
378 | |
|
379 | 0 | return NULL; |
380 | 0 | } |
381 | | |
382 | | /* |
383 | | * We limit the rate of new connections after a failed attempt. |
384 | | */ |
385 | 0 | if (pool->state.pending > pool->pending_window) { |
386 | 0 | RATE_LIMIT_GLOBAL_ROPTIONAL(RWARN, WARN, |
387 | 0 | "Cannot open a new connection due to rate limit after failure"); |
388 | |
|
389 | 0 | return NULL; |
390 | 0 | } |
391 | | |
392 | 0 | pool->state.pending++; |
393 | 0 | number = pool->state.count++; |
394 | | |
395 | | /* |
396 | | * Don't starve out the thread trying to reconnect |
397 | | * the pool, by continuously opening new connections. |
398 | | */ |
399 | 0 | while (pool->state.reconnecting) pthread_cond_wait(&pool->done_reconnecting, &pool->mutex); |
400 | | |
401 | | /* |
402 | | * The true value for pending_window is the smaller of |
403 | | * free connection slots, or pool->pending_window. |
404 | | */ |
405 | 0 | pending_window = (pool->max - pool->state.num); |
406 | 0 | if (pool->pending_window < pending_window) pending_window = pool->pending_window; |
407 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "Opening additional connection (%" PRIu64 "), %u of %u pending slots used", |
408 | 0 | number, pool->state.pending, pending_window); |
409 | | |
410 | | /* |
411 | | * Unlock the mutex while we try to open a new |
412 | | * connection. If there are issues with the back-end, |
413 | | * opening a new connection may take a LONG time. In |
414 | | * that case, we want the other connections to continue |
415 | | * to be used. |
416 | | */ |
417 | 0 | pthread_mutex_unlock(&pool->mutex); |
418 | | |
419 | | /* |
420 | | * Allocate a new top level ctx for the create callback |
421 | | * to hang its memory off of. |
422 | | */ |
423 | 0 | MEM(ctx = talloc_init("connection_ctx")); |
424 | | |
425 | | /* |
426 | | * This may take a long time, which prevents other |
427 | | * threads from releasing connections. We don't care |
428 | | * about other threads opening new connections, as we |
429 | | * already have no free connections. |
430 | | */ |
431 | 0 | conn = pool->create(ctx, pool->opaque, pool->connect_timeout); |
432 | 0 | if (!conn) { |
433 | 0 | ERROR("Opening connection failed (%" PRIu64 ")", number); |
434 | |
|
435 | 0 | pthread_mutex_lock(&pool->mutex); |
436 | 0 | pool->state.last_failed = now; |
437 | 0 | pool->pending_window = 1; |
438 | 0 | pool->state.pending--; |
439 | | |
440 | | /* |
441 | | * Must be done inside the mutex, reconnect callback |
442 | | * may modify args. |
443 | | */ |
444 | 0 | fr_pool_trigger(pool, "fail"); |
445 | 0 | pthread_cond_broadcast(&pool->done_spawn); |
446 | |
|
447 | 0 | talloc_free(ctx); |
448 | |
|
449 | 0 | return NULL; |
450 | 0 | } |
451 | | |
452 | | /* |
453 | | * And lock the mutex again while we link the new |
454 | | * connection back into the pool. |
455 | | */ |
456 | 0 | pthread_mutex_lock(&pool->mutex); |
457 | |
|
458 | 0 | this = talloc_zero(pool, fr_pool_connection_t); |
459 | 0 | if (!this) { |
460 | 0 | pool->state.last_failed = now; |
461 | 0 | pool->state.pending--; |
462 | |
|
463 | 0 | pthread_cond_broadcast(&pool->done_spawn); |
464 | |
|
465 | 0 | ERROR("Memory allocation failed for new connection (%" PRIu64 ")", number); |
466 | |
|
467 | 0 | talloc_free(ctx); |
468 | |
|
469 | 0 | return NULL; |
470 | 0 | } |
471 | 0 | MEM(talloc_link_ctx(this, ctx) >= 0); |
472 | |
|
473 | 0 | this->created = now; |
474 | 0 | this->connection = conn; |
475 | 0 | this->in_use = in_use; |
476 | |
|
477 | 0 | this->number = number; |
478 | 0 | this->last_reserved = fr_time(); |
479 | 0 | this->last_released = this->last_reserved; |
480 | | |
481 | | /* |
482 | | * The connection pool is starting up. Insert the |
483 | | * connection into the heap. |
484 | | */ |
485 | 0 | if (!in_use) fr_heap_insert(&pool->heap, this); |
486 | |
|
487 | 0 | connection_link_head(pool, this); |
488 | | |
489 | | /* |
490 | | * Do NOT insert the connection into the heap. That's |
491 | | * done when the connection is released. |
492 | | */ |
493 | |
|
494 | 0 | pool->state.num++; |
495 | |
|
496 | 0 | fr_assert(pool->state.pending > 0); |
497 | 0 | pool->state.pending--; |
498 | | |
499 | | /* |
500 | | * We've successfully opened one more connection. Allow |
501 | | * more connections to open in parallel. |
502 | | */ |
503 | 0 | if ((pool->pending_window < pool->max) && |
504 | 0 | ((pool->max_pending == 0) || (pool->pending_window < pool->max_pending))) { |
505 | 0 | pool->pending_window++; |
506 | 0 | } |
507 | |
|
508 | 0 | pool->state.last_spawned = fr_time(); |
509 | 0 | pool->delay_interval = pool->cleanup_interval; |
510 | 0 | pool->state.next_delay = pool->cleanup_interval; |
511 | 0 | pool->state.last_failed = fr_time_wrap(0); |
512 | | |
513 | | /* |
514 | | * Must be done inside the mutex, reconnect callback |
515 | | * may modify args. |
516 | | */ |
517 | 0 | fr_pool_trigger(pool, "open"); |
518 | |
|
519 | 0 | pthread_cond_broadcast(&pool->done_spawn); |
520 | |
|
521 | 0 | return this; |
522 | 0 | } |
523 | | |
524 | | /** Close an existing connection. |
525 | | * |
526 | | * Removes the connection from the list, calls the delete callback to close |
527 | | * the connection, then frees memory allocated to the connection. |
528 | | * |
529 | | * @note Will call the 'close' trigger. |
530 | | * @note Must be called with the mutex held. |
531 | | * |
532 | | * @param[in] pool to modify. |
533 | | * @param[in] this Connection to delete. |
534 | | */ |
535 | | static void connection_close_internal(fr_pool_t *pool, fr_pool_connection_t *this) |
536 | 0 | { |
537 | | /* |
538 | | * If it's in use, release it. |
539 | | */ |
540 | 0 | if (this->in_use) { |
541 | | #ifdef PTHREAD_DEBUG |
542 | | pthread_t pthread_id = pthread_self(); |
543 | | fr_assert(pthread_equal(this->pthread_id, pthread_id) != 0); |
544 | | #endif |
545 | |
|
546 | 0 | this->in_use = false; |
547 | |
|
548 | 0 | fr_assert(pool->state.active != 0); |
549 | 0 | pool->state.active--; |
550 | |
|
551 | 0 | } else { |
552 | | /* |
553 | | * Connection isn't used, remove it from the heap. |
554 | | */ |
555 | 0 | fr_heap_extract(&pool->heap, this); |
556 | 0 | } |
557 | |
|
558 | 0 | fr_pool_trigger(pool, "close"); |
559 | |
|
560 | 0 | connection_unlink(pool, this); |
561 | |
|
562 | 0 | fr_assert(pool->state.num > 0); |
563 | 0 | pool->state.num--; |
564 | 0 | talloc_free(this); |
565 | 0 | } |
566 | | |
567 | | /** Check whether a connection needs to be removed from the pool |
568 | | * |
569 | | * Will verify that the connection is within idle_timeout, max_uses, and |
570 | | * lifetime values. If it is not, the connection will be closed. |
571 | | * |
572 | | * @note Will only close connections not in use. |
573 | | * @note Must be called with the mutex held. |
574 | | * |
575 | | * @param[in] pool to modify. |
576 | | * @param[in] request The current request. |
577 | | * @param[in] this Connection to manage. |
578 | | * @param[in] now Current time. |
579 | | * @return |
580 | | * - 0 if connection was closed. |
581 | | * - 1 if connection handle was left open. |
582 | | */ |
583 | | static int connection_manage(fr_pool_t *pool, request_t *request, fr_pool_connection_t *this, fr_time_t now) |
584 | 0 | { |
585 | 0 | fr_assert(pool != NULL); |
586 | 0 | fr_assert(this != NULL); |
587 | | |
588 | | /* |
589 | | * Don't terminated in-use connections |
590 | | */ |
591 | 0 | if (this->in_use) return 1; |
592 | | |
593 | 0 | if (this->needs_reconnecting) { |
594 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "Closing expired connection (%" PRIu64 "): Needs reconnecting", |
595 | 0 | this->number); |
596 | 0 | do_delete: |
597 | 0 | if (pool->state.num <= pool->min) { |
598 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "You probably need to lower \"min\""); |
599 | 0 | } |
600 | 0 | connection_close_internal(pool, this); |
601 | 0 | return 0; |
602 | 0 | } |
603 | | |
604 | 0 | if ((pool->max_uses > 0) && |
605 | 0 | (this->num_uses >= pool->max_uses)) { |
606 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "Closing expired connection (%" PRIu64 "): Hit max_uses limit", |
607 | 0 | this->number); |
608 | 0 | goto do_delete; |
609 | 0 | } |
610 | | |
611 | 0 | if (fr_time_delta_ispos(pool->lifetime) && |
612 | 0 | (fr_time_lt(fr_time_add(this->created, pool->lifetime), now))) { |
613 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "Closing expired connection (%" PRIu64 "): Hit lifetime limit", |
614 | 0 | this->number); |
615 | 0 | goto do_delete; |
616 | 0 | } |
617 | | |
618 | 0 | if (fr_time_delta_ispos(pool->idle_timeout) && |
619 | 0 | (fr_time_lt(fr_time_add(this->last_released, pool->idle_timeout), now))) { |
620 | 0 | ROPTIONAL(RINFO, INFO, "Closing connection (%" PRIu64 "): Hit idle_timeout, was idle for %pVs", |
621 | 0 | this->number, fr_box_time_delta(fr_time_sub(now, this->last_released))); |
622 | 0 | goto do_delete; |
623 | 0 | } |
624 | | |
625 | 0 | return 1; |
626 | 0 | } |
627 | | |
628 | | |
629 | | /** Check whether any connections need to be removed from the pool |
630 | | * |
631 | | * Maintains the number of connections in the pool as per the configuration |
632 | | * parameters for the connection pool. |
633 | | * |
634 | | * @note Will only run checks the first time it's called in a given second, |
635 | | * to throttle connection spawning/closing. |
636 | | * @note Will only close connections not in use. |
637 | | * @note Must be called with the mutex held. Returns with the mutex held. |
638 | | * |
639 | | * @param[in] pool to manage. |
640 | | * @param[in] request The current request. |
641 | | * @return 1 |
642 | | */ |
643 | | static int connection_check(fr_pool_t *pool, request_t *request) |
644 | 0 | { |
645 | 0 | uint32_t num, spare; |
646 | 0 | fr_time_t now = fr_time(); |
647 | 0 | fr_pool_connection_t *this, *next; |
648 | |
|
649 | 0 | if (fr_time_delta_lt(fr_time_sub(now, pool->state.last_checked), fr_time_delta_from_sec(1))) return 1; |
650 | | |
651 | | /* |
652 | | * Get "real" number of connections, and count pending |
653 | | * connections as spare. |
654 | | */ |
655 | 0 | num = pool->state.num + pool->state.pending; |
656 | 0 | spare = pool->state.pending + (pool->state.num - pool->state.active); |
657 | | |
658 | | /* |
659 | | * The other end can close connections. If so, we'll |
660 | | * have fewer than "min". When that happens, open more |
661 | | * connections to enforce "min". |
662 | | * |
663 | | * The code for spawning connections enforces that |
664 | | * num + pending <= max. |
665 | | */ |
666 | 0 | if (num < pool->min) { |
667 | 0 | ROPTIONAL(RINFO, INFO, "Need %" PRIu32 " more connections to reach min connections (%" PRIu32 ")", pool->min - num, pool->min); |
668 | 0 | goto add_connection; |
669 | 0 | } |
670 | | |
671 | | /* |
672 | | * On the odd chance that we've opened too many |
673 | | * connections, take care of that. |
674 | | */ |
675 | 0 | if (num > pool->max) { |
676 | | /* |
677 | | * Pending connections don't get closed as "spare". |
678 | | */ |
679 | 0 | if (pool->state.pending > 0) goto manage_connections; |
680 | | |
681 | | /* |
682 | | * Otherwise close one of the connections to |
683 | | * bring us down to "max". |
684 | | */ |
685 | 0 | goto close_connection; |
686 | 0 | } |
687 | | |
688 | | /* |
689 | | * Now that we've enforced min/max connections, try to |
690 | | * keep the "spare" connections at the correct number. |
691 | | */ |
692 | | |
693 | | /* |
694 | | * Nothing to do? Go check all of the connections for |
695 | | * timeouts, etc. |
696 | | */ |
697 | 0 | if (spare == pool->spare) goto manage_connections; |
698 | | |
699 | | /* |
700 | | * Too many spare connections, delete some. |
701 | | */ |
702 | 0 | if (spare > pool->spare) { |
703 | 0 | fr_pool_connection_t *found; |
704 | | |
705 | | /* |
706 | | * Pending connections don't get closed as "spare". |
707 | | */ |
708 | 0 | if (pool->state.pending > 0) goto manage_connections; |
709 | | |
710 | | /* |
711 | | * Don't close too many connections, even they |
712 | | * are spare. |
713 | | */ |
714 | 0 | if (num <= pool->min) goto manage_connections; |
715 | | |
716 | | /* |
717 | | * Too many spares, go close one. |
718 | | */ |
719 | | |
720 | 0 | close_connection: |
721 | | /* |
722 | | * Don't close connections too often, in order to |
723 | | * prevent flapping. |
724 | | */ |
725 | 0 | if (fr_time_lt(now, fr_time_add(pool->state.last_spawned, pool->delay_interval))) goto manage_connections; |
726 | | |
727 | | /* |
728 | | * Find a connection to close. |
729 | | */ |
730 | 0 | found = NULL; |
731 | 0 | for (this = pool->tail; this != NULL; this = this->prev) { |
732 | 0 | if (this->in_use) continue; |
733 | | |
734 | 0 | if (!found || (fr_time_lt(this->last_reserved, found->last_reserved))) found = this; |
735 | 0 | } |
736 | |
|
737 | 0 | if (!fr_cond_assert(found)) goto done; |
738 | | |
739 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "Closing connection (%" PRIu64 ") as we have too many unused connections", |
740 | 0 | found->number); |
741 | 0 | connection_close_internal(pool, found); |
742 | | |
743 | | /* |
744 | | * Decrease the delay for the next time we clean |
745 | | * up. |
746 | | */ |
747 | 0 | pool->state.next_delay = fr_time_delta_wrap(fr_time_delta_unwrap(pool->state.next_delay) >> 1); |
748 | 0 | if (!fr_time_delta_ispos(pool->state.next_delay)) pool->state.next_delay = fr_time_delta_wrap(1); |
749 | 0 | pool->delay_interval = fr_time_delta_add(pool->delay_interval, pool->state.next_delay); |
750 | |
|
751 | 0 | goto manage_connections; |
752 | 0 | } |
753 | | |
754 | | /* |
755 | | * Too few connections, open some more. |
756 | | */ |
757 | 0 | if (spare < pool->spare) { |
758 | | /* |
759 | | * Don't open too many pending connections. |
760 | | */ |
761 | 0 | if (pool->state.pending >= pool->pending_window) goto manage_connections; |
762 | | |
763 | | /* |
764 | | * Don't open too many connections, even if we |
765 | | * need more spares. |
766 | | */ |
767 | 0 | if (num >= pool->max) goto manage_connections; |
768 | | |
769 | | /* |
770 | | * Too few spares, go add one. |
771 | | */ |
772 | 0 | ROPTIONAL(RINFO, INFO, "Need %" PRIu32 " more connections to reach %" PRIu32 " spares", pool->spare - spare, pool->spare); |
773 | |
|
774 | 0 | add_connection: |
775 | | /* |
776 | | * Only try to open spares if we're not already attempting to open |
777 | | * a connection. Avoids spurious log messages. |
778 | | */ |
779 | 0 | (void) connection_spawn(pool, request, now, false); |
780 | 0 | goto manage_connections; |
781 | 0 | } |
782 | | |
783 | | /* |
784 | | * Pass over all of the connections in the pool, limiting |
785 | | * lifetime, idle time, max requests, etc. |
786 | | */ |
787 | 0 | manage_connections: |
788 | 0 | for (this = pool->head; this != NULL; this = next) { |
789 | 0 | next = this->next; |
790 | 0 | connection_manage(pool, request, this, now); |
791 | 0 | } |
792 | |
|
793 | 0 | pool->state.last_checked = now; |
794 | |
|
795 | 0 | done: |
796 | 0 | return 1; |
797 | 0 | } |
798 | | |
799 | | /** Get a connection from the connection pool |
800 | | * |
801 | | * @note Must be called with the mutex free. |
802 | | * |
803 | | * @param[in] pool to reserve the connection from. |
804 | | * @param[in] request The current request. |
805 | | * @param[in] spawn whether to spawn a new connection |
806 | | * @return |
807 | | * - A pointer to the connection handle. |
808 | | * - NULL on error. |
809 | | */ |
810 | | static void *connection_get_internal(fr_pool_t *pool, request_t *request, bool spawn) |
811 | 0 | { |
812 | 0 | fr_time_t now; |
813 | 0 | fr_pool_connection_t *this; |
814 | |
|
815 | 0 | if (!pool) return NULL; |
816 | | |
817 | 0 | pthread_mutex_lock(&pool->mutex); |
818 | |
|
819 | 0 | now = fr_time(); |
820 | | |
821 | | /* |
822 | | * Grab the link with the lowest latency, and check it |
823 | | * for limits. If "connection manage" says the link is |
824 | | * no longer usable, go grab another one. |
825 | | */ |
826 | 0 | do { |
827 | 0 | this = fr_heap_peek(pool->heap); |
828 | 0 | if (!this) break; |
829 | 0 | } while (!connection_manage(pool, request, this, now)); |
830 | | |
831 | | /* |
832 | | * We have a working connection. Extract it from the |
833 | | * heap and use it. |
834 | | */ |
835 | 0 | if (this) { |
836 | 0 | fr_heap_extract(&pool->heap, this); |
837 | 0 | goto do_return; |
838 | 0 | } |
839 | | |
840 | 0 | if (pool->state.num == pool->max) { |
841 | 0 | bool complain = false; |
842 | | |
843 | | /* |
844 | | * Rate-limit complaints. |
845 | | */ |
846 | 0 | if (fr_time_delta_gt(fr_time_sub(now, pool->state.last_at_max), fr_time_delta_from_sec(1))) { |
847 | 0 | complain = true; |
848 | 0 | pool->state.last_at_max = now; |
849 | 0 | } |
850 | |
|
851 | 0 | if (!fr_rate_limit_enabled() || complain) { |
852 | 0 | ERROR("No connections available and at max connection limit"); |
853 | | |
854 | | /* |
855 | | * Must be done inside the mutex, reconnect callback |
856 | | * may modify args. |
857 | | */ |
858 | 0 | fr_pool_trigger(pool, "none"); |
859 | 0 | } |
860 | 0 | pthread_mutex_unlock(&pool->mutex); |
861 | |
|
862 | 0 | return NULL; |
863 | 0 | } |
864 | | |
865 | 0 | if (!spawn) { |
866 | 0 | pthread_mutex_unlock(&pool->mutex); |
867 | 0 | return NULL; |
868 | 0 | } |
869 | | |
870 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "%" PRIu32 " of %u connections in use. You may need to increase \"spare\"", |
871 | 0 | pool->state.active, pool->state.num); |
872 | |
|
873 | 0 | this = connection_spawn(pool, request, now, true); |
874 | 0 | if (!this) { |
875 | 0 | pthread_mutex_unlock(&pool->mutex); |
876 | 0 | return NULL; |
877 | 0 | } |
878 | | |
879 | 0 | do_return: |
880 | 0 | pool->state.active++; |
881 | 0 | this->num_uses++; |
882 | 0 | this->last_reserved = fr_time(); |
883 | 0 | this->in_use = true; |
884 | |
|
885 | | #ifdef PTHREAD_DEBUG |
886 | | this->pthread_id = pthread_self(); |
887 | | #endif |
888 | 0 | pthread_mutex_unlock(&pool->mutex); |
889 | |
|
890 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "Reserved connection (%" PRIu64 ")", this->number); |
891 | |
|
892 | 0 | return this->connection; |
893 | 0 | } |
894 | | |
895 | | /** Enable triggers for a connection pool |
896 | | * |
897 | | * @param[in] pool to enable triggers for. |
898 | | * @param[in] trigger_prefix prefix to prepend to all trigger names. Usually a path |
899 | | * to the module's trigger configuration e.g. |
900 | | * @verbatim modules.<name>.pool @endverbatim |
901 | | * @verbatim <trigger name> @endverbatim is appended to form |
902 | | * the complete path. |
903 | | * @param[in] trigger_args to make available in any triggers executed by the connection pool. |
904 | | * These will usually be fr_pair_t (s) describing the host |
905 | | * associated with the pool. |
906 | | * Trigger args will be copied, input trigger_args should be freed |
907 | | * if necessary. |
908 | | */ |
909 | | void fr_pool_enable_triggers(fr_pool_t *pool, char const *trigger_prefix, fr_pair_list_t *trigger_args) |
910 | | { |
911 | | pool->triggers_enabled = true; |
912 | | |
913 | | talloc_const_free(pool->trigger_prefix); |
914 | | MEM(pool->trigger_prefix = trigger_prefix ? talloc_strdup(pool, trigger_prefix) : ""); |
915 | | |
916 | | fr_pair_list_free(&pool->trigger_args); |
917 | | |
918 | | if (!trigger_args) return; |
919 | | |
920 | | MEM(fr_pair_list_copy(pool, &pool->trigger_args, trigger_args) >= 0); |
921 | | } |
922 | | |
923 | | /** Create a new connection pool |
924 | | * |
925 | | * Allocates structures used by the connection pool, initialises the various |
926 | | * configuration options and counters, and sets the callback functions. |
927 | | * |
928 | | * Will also spawn the number of connections specified by the 'start' configuration |
929 | | * option. |
930 | | * |
931 | | * @note Will call the 'start' trigger. |
932 | | * |
933 | | * @param[in] ctx Context to link pool's destruction to. |
934 | | * @param[in] cs pool section. |
935 | | * @param[in] opaque data pointer to pass to callbacks. |
936 | | * @param[in] c Callback to create new connections. |
937 | | * @param[in] a Callback to check the status of connections. |
938 | | * @param[in] log_prefix prefix to prepend to all log messages. |
939 | | * @return |
940 | | * - New connection pool. |
941 | | * - NULL on error. |
942 | | */ |
943 | | fr_pool_t *fr_pool_init(TALLOC_CTX *ctx, |
944 | | CONF_SECTION const *cs, |
945 | | void *opaque, |
946 | | fr_pool_connection_create_t c, fr_pool_connection_alive_t a, |
947 | | char const *log_prefix) |
948 | 0 | { |
949 | 0 | fr_pool_t *pool = NULL; |
950 | |
|
951 | 0 | if (!cs || !opaque || !c) return NULL; |
952 | | |
953 | | /* |
954 | | * Pool is allocated in the NULL context as |
955 | | * threads are likely to allocate memory |
956 | | * beneath the pool. |
957 | | */ |
958 | 0 | MEM(pool = talloc_zero(NULL, fr_pool_t)); |
959 | 0 | fr_pair_list_init(&pool->trigger_args); |
960 | | |
961 | | /* |
962 | | * Ensure the pool is freed at the same time |
963 | | * as its parent. |
964 | | */ |
965 | 0 | if (ctx && (talloc_link_ctx(ctx, pool) < 0)) { |
966 | 0 | PERROR("%s: Failed linking pool ctx", __FUNCTION__); |
967 | 0 | talloc_free(pool); |
968 | |
|
969 | 0 | return NULL; |
970 | 0 | } |
971 | | |
972 | 0 | pool->cs = cs; |
973 | 0 | pool->opaque = opaque; |
974 | 0 | pool->create = c; |
975 | 0 | pool->alive = a; |
976 | |
|
977 | 0 | pool->head = pool->tail = NULL; |
978 | | |
979 | | /* |
980 | | * We keep a heap of connections, sorted by the last time |
981 | | * we STARTED using them. Newly opened connections |
982 | | * aren't in the heap. They're only inserted in the list |
983 | | * once they're released. |
984 | | * |
985 | | * We do "most recently started" instead of "most |
986 | | * recently used", because MRU is done as most recently |
987 | | * *released*. We want to order connections by |
988 | | * responsiveness, and MRU prioritizes high latency |
989 | | * connections. |
990 | | * |
991 | | * We want most recently *started*, which gives |
992 | | * preference to low latency links, and pushes high |
993 | | * latency links down in the priority heap. |
994 | | * |
995 | | * https://code.facebook.com/posts/1499322996995183/solving-the-mystery-of-link-imbalance-a-metastable-failure-state-at-scale/ |
996 | | */ |
997 | 0 | if (!pool->spread) { |
998 | 0 | pool->heap = fr_heap_talloc_alloc(pool, last_reserved_cmp, fr_pool_connection_t, heap_id, 0); |
999 | | /* |
1000 | | * For some types of connections we need to used a different |
1001 | | * algorithm, because load balancing benefits are secondary |
1002 | | * to maintaining a cache of open connections. |
1003 | | * |
1004 | | * With libcurl's multihandle, connections can only be reused |
1005 | | * if all handles that make up the multihandle are done processing |
1006 | | * their requests. |
1007 | | * |
1008 | | * We can't tell when that's happened using libcurl, and even |
1009 | | * if we could, blocking until all servers had responded |
1010 | | * would have huge cost. |
1011 | | * |
1012 | | * The solution is to order the heap so that the connection that |
1013 | | * was released longest ago is at the top. |
1014 | | * |
1015 | | * That way we maximise time between connection use. |
1016 | | */ |
1017 | 0 | } else { |
1018 | 0 | pool->heap = fr_heap_talloc_alloc(pool, last_released_cmp, fr_pool_connection_t, heap_id, 0); |
1019 | 0 | } |
1020 | 0 | if (!pool->heap) { |
1021 | 0 | ERROR("%s: Failed creating connection heap", __FUNCTION__); |
1022 | 0 | error: |
1023 | 0 | fr_pool_free(pool); |
1024 | 0 | return NULL; |
1025 | 0 | } |
1026 | | |
1027 | 0 | pool->log_prefix = log_prefix ? talloc_strdup(pool, log_prefix) : "core"; |
1028 | 0 | pthread_mutex_init(&pool->mutex, NULL); |
1029 | 0 | pthread_cond_init(&pool->done_spawn, NULL); |
1030 | 0 | pthread_cond_init(&pool->done_reconnecting, NULL); |
1031 | |
|
1032 | 0 | DEBUG2("Initialising connection pool"); |
1033 | |
|
1034 | 0 | if (cf_section_rules_push(UNCONST(CONF_SECTION *, cs), pool_config) < 0) goto error; |
1035 | 0 | if (cf_section_parse(pool, pool, UNCONST(CONF_SECTION *, cs)) < 0) { |
1036 | 0 | PERROR("Configuration parsing failed"); |
1037 | 0 | goto error; |
1038 | 0 | } |
1039 | | |
1040 | | /* |
1041 | | * Some simple limits |
1042 | | */ |
1043 | 0 | if (pool->max == 0) { |
1044 | 0 | cf_log_err(cs, "Cannot set 'max' to zero"); |
1045 | 0 | goto error; |
1046 | 0 | } |
1047 | | |
1048 | 0 | pool->pending_window = (pool->max_pending > 0) ? pool->max_pending : pool->max; |
1049 | |
|
1050 | 0 | if (pool->min > pool->max) { |
1051 | 0 | cf_log_err(cs, "Cannot set 'min' to more than 'max'"); |
1052 | 0 | goto error; |
1053 | 0 | } |
1054 | | |
1055 | 0 | FR_INTEGER_BOUND_CHECK("max", pool->max, <=, 1024); |
1056 | 0 | FR_INTEGER_BOUND_CHECK("start", pool->start, <=, pool->max); |
1057 | 0 | FR_INTEGER_BOUND_CHECK("spare", pool->spare, <=, (pool->max - pool->min)); |
1058 | |
|
1059 | 0 | if (fr_time_delta_ispos(pool->lifetime)) { |
1060 | 0 | FR_TIME_DELTA_COND_CHECK("idle_timeout", pool->idle_timeout, |
1061 | 0 | fr_time_delta_lteq(pool->idle_timeout, pool->lifetime), fr_time_delta_wrap(0)); |
1062 | 0 | } |
1063 | |
|
1064 | 0 | if (fr_time_delta_ispos(pool->idle_timeout)) { |
1065 | 0 | FR_TIME_DELTA_BOUND_CHECK("cleanup_interval", pool->cleanup_interval, <=, pool->idle_timeout); |
1066 | 0 | } |
1067 | | |
1068 | | /* |
1069 | | * Some libraries treat 0.0 as infinite timeout, others treat it |
1070 | | * as instantaneous timeout. Solve the inconsistency by making |
1071 | | * the smallest allowable timeout 100ms. |
1072 | | */ |
1073 | 0 | FR_TIME_DELTA_BOUND_CHECK("connect_timeout", pool->connect_timeout, >=, fr_time_delta_from_msec(100)); |
1074 | | |
1075 | | /* |
1076 | | * Don't open any connections. Instead, force the limits |
1077 | | * to only 1 connection. |
1078 | | * |
1079 | | */ |
1080 | 0 | if (check_config) pool->start = pool->min = pool->max = 1; |
1081 | |
|
1082 | 0 | return pool; |
1083 | 0 | } |
1084 | | |
1085 | | int fr_pool_start(fr_pool_t *pool) |
1086 | 0 | { |
1087 | 0 | uint32_t i; |
1088 | 0 | fr_pool_connection_t *this; |
1089 | | |
1090 | | /* |
1091 | | * Don't spawn any connections |
1092 | | */ |
1093 | 0 | if (check_config) return 0; |
1094 | | |
1095 | | /* |
1096 | | * Create all of the connections, unless the admin says |
1097 | | * not to. |
1098 | | */ |
1099 | 0 | pthread_mutex_lock(&pool->mutex); |
1100 | 0 | for (i = 0; i < pool->start; i++) { |
1101 | | /* |
1102 | | * Call time() once for each spawn attempt as there |
1103 | | * could be a significant delay. |
1104 | | */ |
1105 | 0 | this = connection_spawn(pool, NULL, fr_time(), false); |
1106 | 0 | if (!this) { |
1107 | 0 | pthread_mutex_unlock(&pool->mutex); |
1108 | 0 | ERROR("Failed spawning initial connections"); |
1109 | 0 | return -1; |
1110 | 0 | } |
1111 | 0 | } |
1112 | 0 | pthread_mutex_unlock(&pool->mutex); |
1113 | |
|
1114 | 0 | fr_pool_trigger(pool, "start"); |
1115 | |
|
1116 | 0 | return 0; |
1117 | 0 | } |
1118 | | |
1119 | | /** Allocate a new pool using an existing one as a template |
1120 | | * |
1121 | | * @param[in] ctx to allocate new pool in. |
1122 | | * @param[in] pool to copy. |
1123 | | * @param[in] opaque data to pass to connection function. |
1124 | | * @return |
1125 | | * - New connection pool. |
1126 | | * - NULL on error. |
1127 | | */ |
1128 | | fr_pool_t *fr_pool_copy(TALLOC_CTX *ctx, fr_pool_t *pool, void *opaque) |
1129 | 0 | { |
1130 | 0 | fr_pool_t *copy; |
1131 | |
|
1132 | 0 | copy = fr_pool_init(ctx, pool->cs, opaque, pool->create, pool->alive, pool->log_prefix); |
1133 | 0 | if (!copy) return NULL; |
1134 | | |
1135 | 0 | if (pool->trigger_prefix) fr_pool_enable_triggers(copy, pool->trigger_prefix, &pool->trigger_args); |
1136 | |
|
1137 | 0 | return copy; |
1138 | 0 | } |
1139 | | |
1140 | | /** Get the number of connections currently in the pool |
1141 | | * |
1142 | | * @param[in] pool to count connections for. |
1143 | | * @return the number of connections in the pool |
1144 | | */ |
1145 | | fr_pool_state_t const *fr_pool_state(fr_pool_t *pool) |
1146 | 0 | { |
1147 | 0 | return &pool->state; |
1148 | 0 | } |
1149 | | |
1150 | | /** Connection pool get timeout |
1151 | | * |
1152 | | * @param[in] pool to get connection timeout for. |
1153 | | * @return the connection timeout configured for the pool. |
1154 | | */ |
1155 | | fr_time_delta_t fr_pool_timeout(fr_pool_t *pool) |
1156 | 0 | { |
1157 | 0 | return pool->connect_timeout; |
1158 | 0 | } |
1159 | | |
1160 | | /** Connection pool get start |
1161 | | * |
1162 | | * @param[in] pool to get connection start for. |
1163 | | * @return the connection start value configured for the pool. |
1164 | | */ |
1165 | | int fr_pool_start_num(fr_pool_t *pool) |
1166 | 0 | { |
1167 | 0 | return pool->start; |
1168 | 0 | } |
1169 | | |
1170 | | /** Return the opaque data associated with a connection pool |
1171 | | * |
1172 | | * @param pool to return data for. |
1173 | | * @return opaque data associated with pool. |
1174 | | */ |
1175 | | void const *fr_pool_opaque(fr_pool_t *pool) |
1176 | 0 | { |
1177 | 0 | return pool->opaque; |
1178 | 0 | } |
1179 | | |
1180 | | /** Increment pool reference by one. |
1181 | | * |
1182 | | * @param[in] pool to increment reference counter for. |
1183 | | */ |
1184 | | void fr_pool_ref(fr_pool_t *pool) |
1185 | 0 | { |
1186 | 0 | pool->ref++; |
1187 | 0 | } |
1188 | | |
1189 | | /** Set a reconnection callback for the connection pool |
1190 | | * |
1191 | | * This can be called at any time during the pool's lifecycle. |
1192 | | * |
1193 | | * @param[in] pool to set reconnect callback for. |
1194 | | * @param reconnect callback to call when reconnecting pool's connections. |
1195 | | */ |
1196 | | void fr_pool_reconnect_func(fr_pool_t *pool, fr_pool_reconnect_t reconnect) |
1197 | 0 | { |
1198 | 0 | pool->reconnect = reconnect; |
1199 | 0 | } |
1200 | | |
1201 | | /** Mark connections for reconnection, and spawn at least 'start' connections |
1202 | | * |
1203 | | * @note This call may block whilst waiting for pending connection attempts to complete. |
1204 | | * |
1205 | | * This intended to be called on a connection pool that's in use, to have it reflect |
1206 | | * a configuration change, or because the administrator knows that all connections |
1207 | | * in the pool are inviable and need to be reconnected. |
1208 | | * |
1209 | | * @param[in] pool to reconnect. |
1210 | | * @param[in] request The current request. |
1211 | | * @return |
1212 | | * - 0 On success. |
1213 | | * - -1 If we couldn't create start connections, this may be ignored |
1214 | | * depending on the context in which this function is being called. |
1215 | | */ |
1216 | | int fr_pool_reconnect(fr_pool_t *pool, request_t *request) |
1217 | 0 | { |
1218 | 0 | uint32_t i; |
1219 | 0 | fr_pool_connection_t *this; |
1220 | 0 | fr_time_t now; |
1221 | |
|
1222 | 0 | pthread_mutex_lock(&pool->mutex); |
1223 | | |
1224 | | /* |
1225 | | * Pause new spawn attempts (we release the mutex |
1226 | | * during our cond wait). |
1227 | | */ |
1228 | 0 | pool->state.reconnecting = true; |
1229 | | |
1230 | | /* |
1231 | | * When the loop exits, we'll hold the lock for the pool, |
1232 | | * and we're guaranteed the connection create callback |
1233 | | * will not be using the opaque data. |
1234 | | */ |
1235 | 0 | while (pool->state.pending) pthread_cond_wait(&pool->done_spawn, &pool->mutex); |
1236 | | |
1237 | | /* |
1238 | | * We want to ensure at least 'start' connections |
1239 | | * have been reconnected. We can't call reconnect |
1240 | | * because, we might get the same connection each |
1241 | | * time we reserve one, so we close 'start' |
1242 | | * connections, and then attempt to spawn them again. |
1243 | | */ |
1244 | 0 | for (i = 0; i < pool->start; i++) { |
1245 | 0 | this = fr_heap_peek(pool->heap); |
1246 | 0 | if (!this) break; /* There wasn't 'start' connections available */ |
1247 | | |
1248 | 0 | connection_close_internal(pool, this); |
1249 | 0 | } |
1250 | | |
1251 | | /* |
1252 | | * Mark all remaining connections in the pool as |
1253 | | * requiring reconnection. |
1254 | | */ |
1255 | 0 | for (this = pool->head; this; this = this->next) this->needs_reconnecting = true; |
1256 | | |
1257 | | /* |
1258 | | * Call the reconnect callback (if one's set) |
1259 | | * This may modify the opaque data associated |
1260 | | * with the pool. |
1261 | | */ |
1262 | 0 | if (pool->reconnect) pool->reconnect(pool, pool->opaque); |
1263 | | |
1264 | | /* |
1265 | | * Must be done inside the mutex, reconnect callback |
1266 | | * may modify args. |
1267 | | */ |
1268 | 0 | fr_pool_trigger(pool, "reconnect"); |
1269 | | |
1270 | | /* |
1271 | | * Allow new spawn attempts, and wakeup any threads |
1272 | | * waiting to spawn new connections. |
1273 | | */ |
1274 | 0 | pool->state.reconnecting = false; |
1275 | 0 | pthread_cond_broadcast(&pool->done_reconnecting); |
1276 | |
|
1277 | 0 | now = fr_time(); |
1278 | | |
1279 | | /* |
1280 | | * Now attempt to spawn 'start' connections. |
1281 | | */ |
1282 | 0 | for (i = 0; i < pool->start; i++) { |
1283 | 0 | this = connection_spawn(pool, request, now, false); |
1284 | 0 | if (!this) { |
1285 | 0 | pthread_mutex_unlock(&pool->mutex); |
1286 | 0 | return -1; |
1287 | 0 | } |
1288 | 0 | } |
1289 | 0 | pthread_mutex_unlock(&pool->mutex); |
1290 | |
|
1291 | 0 | return 0; |
1292 | 0 | } |
1293 | | |
1294 | | /** Delete a connection pool |
1295 | | * |
1296 | | * Closes, unlinks and frees all connections in the connection pool, then frees |
1297 | | * all memory used by the connection pool. |
1298 | | * |
1299 | | * @note Will call the 'stop' trigger. |
1300 | | * @note Must be called with the mutex free. |
1301 | | * |
1302 | | * @param[in,out] pool to delete. |
1303 | | */ |
1304 | | void fr_pool_free(fr_pool_t *pool) |
1305 | 0 | { |
1306 | 0 | fr_pool_connection_t *this; |
1307 | |
|
1308 | 0 | if (!pool) return; |
1309 | | |
1310 | | /* |
1311 | | * More modules hold a reference to this pool, don't free |
1312 | | * it yet. |
1313 | | */ |
1314 | 0 | if (pool->ref > 0) { |
1315 | 0 | pool->ref--; |
1316 | 0 | return; |
1317 | 0 | } |
1318 | | |
1319 | 0 | DEBUG2("Removing connection pool"); |
1320 | |
|
1321 | 0 | pthread_mutex_lock(&pool->mutex); |
1322 | | |
1323 | | /* |
1324 | | * Don't loop over the list. Just keep removing the head |
1325 | | * until they're all gone. |
1326 | | */ |
1327 | 0 | while ((this = pool->head) != NULL) { |
1328 | 0 | INFO("Closing connection (%" PRIu64 ")", this->number); |
1329 | |
|
1330 | 0 | connection_close_internal(pool, this); |
1331 | 0 | } |
1332 | |
|
1333 | 0 | fr_pool_trigger(pool, "stop"); |
1334 | |
|
1335 | 0 | fr_assert(pool->head == NULL); |
1336 | 0 | fr_assert(pool->tail == NULL); |
1337 | 0 | fr_assert(pool->state.num == 0); |
1338 | |
|
1339 | 0 | pthread_mutex_unlock(&pool->mutex); |
1340 | 0 | pthread_mutex_destroy(&pool->mutex); |
1341 | 0 | pthread_cond_destroy(&pool->done_spawn); |
1342 | 0 | pthread_cond_destroy(&pool->done_reconnecting); |
1343 | |
|
1344 | 0 | talloc_free(pool); |
1345 | 0 | } |
1346 | | |
1347 | | /** Reserve a connection in the connection pool |
1348 | | * |
1349 | | * Will attempt to find an unused connection in the connection pool, if one is |
1350 | | * found, will mark it as in use, and increment the number of active connections |
1351 | | * and return the connection handle. |
1352 | | * |
1353 | | * If no free connections are found will attempt to spawn a new one, conditional |
1354 | | * on a connection spawning not already being in progress, and not being at the |
1355 | | * 'max' connection limit. |
1356 | | * |
1357 | | * @note fr_pool_connection_release must be called once the caller has finished |
1358 | | * using the connection. |
1359 | | * |
1360 | | * @see fr_pool_connection_release |
1361 | | * @param[in] pool to reserve the connection from. |
1362 | | * @param[in] request The current request. |
1363 | | * @return |
1364 | | * - A pointer to the connection handle. |
1365 | | * - NULL on error. |
1366 | | */ |
1367 | | void *fr_pool_connection_get(fr_pool_t *pool, request_t *request) |
1368 | 0 | { |
1369 | 0 | return connection_get_internal(pool, request, true); |
1370 | 0 | } |
1371 | | |
1372 | | /** Release a connection |
1373 | | * |
1374 | | * Will mark a connection as unused and decrement the number of active |
1375 | | * connections. |
1376 | | * |
1377 | | * @see fr_pool_connection_get |
1378 | | * @param[in] pool to release the connection in. |
1379 | | * @param[in] request The current request. |
1380 | | * @param[in] conn to release. |
1381 | | */ |
1382 | | void fr_pool_connection_release(fr_pool_t *pool, request_t *request, void *conn) |
1383 | 0 | { |
1384 | 0 | fr_pool_connection_t *this; |
1385 | 0 | fr_time_delta_t held; |
1386 | 0 | bool trigger_min = false, trigger_max = false; |
1387 | |
|
1388 | 0 | if (!conn) return; |
1389 | | |
1390 | 0 | pthread_mutex_lock(&pool->mutex); |
1391 | 0 | this = connection_find(pool, conn); |
1392 | 0 | if (!this) { |
1393 | 0 | pthread_mutex_unlock(&pool->mutex); |
1394 | 0 | return; |
1395 | 0 | } |
1396 | | |
1397 | 0 | this->in_use = false; |
1398 | | |
1399 | | /* |
1400 | | * Record when the connection was last released |
1401 | | */ |
1402 | 0 | this->last_released = fr_time(); |
1403 | 0 | pool->state.last_released = this->last_released; |
1404 | | |
1405 | | /* |
1406 | | * This is done inside the mutex to ensure |
1407 | | * updates are atomic. |
1408 | | */ |
1409 | 0 | held = fr_time_sub(this->last_released, this->last_reserved); |
1410 | | |
1411 | | /* |
1412 | | * Check we've not exceeded out trigger limits |
1413 | | * |
1414 | | * These should only fire once per second. |
1415 | | */ |
1416 | 0 | if (fr_time_delta_ispos(pool->held_trigger_min) && |
1417 | 0 | (fr_time_delta_lt(held, pool->held_trigger_min)) && |
1418 | 0 | (fr_time_delta_gteq(fr_time_sub(this->last_released, pool->state.last_held_min), fr_time_delta_from_sec(1)))) { |
1419 | 0 | trigger_min = true; |
1420 | 0 | pool->state.last_held_min = this->last_released; |
1421 | 0 | } |
1422 | |
|
1423 | 0 | if (fr_time_delta_ispos(pool->held_trigger_max) && |
1424 | 0 | (fr_time_delta_gt(held, pool->held_trigger_max)) && |
1425 | 0 | (fr_time_delta_gteq(fr_time_sub(this->last_released, pool->state.last_held_max), fr_time_delta_from_sec(1)))) { |
1426 | 0 | trigger_max = true; |
1427 | 0 | pool->state.last_held_max = this->last_released; |
1428 | 0 | } |
1429 | | |
1430 | | /* |
1431 | | * Insert the connection in the heap. |
1432 | | * |
1433 | | * This will either be based on when we *started* using it |
1434 | | * (allowing fast links to be re-used, and slow links to be |
1435 | | * gradually expired), or when we released it (allowing |
1436 | | * the maximum amount of time between connection use). |
1437 | | */ |
1438 | 0 | fr_heap_insert(&pool->heap, this); |
1439 | |
|
1440 | 0 | fr_assert(pool->state.active != 0); |
1441 | 0 | pool->state.active--; |
1442 | |
|
1443 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "Released connection (%" PRIu64 ")", this->number); |
1444 | | |
1445 | | /* |
1446 | | * We mirror the "spawn on get" functionality by having |
1447 | | * "delete on release". If there are too many spare |
1448 | | * connections, go manage the pool && clean some up. |
1449 | | */ |
1450 | 0 | connection_check(pool, request); |
1451 | 0 | pthread_mutex_unlock(&pool->mutex); |
1452 | |
|
1453 | 0 | if (trigger_min) fr_pool_trigger(pool, "min"); |
1454 | 0 | if (trigger_max) fr_pool_trigger(pool, "max"); |
1455 | 0 | } |
1456 | | |
1457 | | /** Reconnect a suspected inviable connection |
1458 | | * |
1459 | | * This should be called by the module if it suspects that a connection is |
1460 | | * not viable (e.g. the server has closed it). |
1461 | | * |
1462 | | * When implementing a module that uses the connection pool API, it is advisable |
1463 | | * to pass a pointer to the pointer to the handle (void **conn) |
1464 | | * to all functions which may call reconnect. This is so that if a new handle |
1465 | | * is created and returned, the handle pointer can be updated up the callstack, |
1466 | | * and a function higher up the stack doesn't attempt to use a now invalid |
1467 | | * connection handle. |
1468 | | * |
1469 | | * @note Will free any talloced memory hung off the context of the connection, |
1470 | | * being reconnected. |
1471 | | * |
1472 | | * @warning After calling reconnect the caller *MUST NOT* attempt to use |
1473 | | * the old handle in any other operations, as its memory will have been |
1474 | | * freed. |
1475 | | * |
1476 | | * @see fr_pool_connection_get |
1477 | | * @param[in] pool to reconnect the connection in. |
1478 | | * @param[in] request The current request. |
1479 | | * @param[in] conn to reconnect. |
1480 | | * @return new connection handle if successful else NULL. |
1481 | | */ |
1482 | | void *fr_pool_connection_reconnect(fr_pool_t *pool, request_t *request, void *conn) |
1483 | 0 | { |
1484 | 0 | fr_pool_connection_t *this; |
1485 | |
|
1486 | 0 | if (!pool || !conn) return NULL; |
1487 | | |
1488 | 0 | pthread_mutex_lock(&pool->mutex); |
1489 | 0 | this = connection_find(pool, conn); |
1490 | 0 | if (!this) { |
1491 | 0 | pthread_mutex_unlock(&pool->mutex); |
1492 | 0 | return NULL; |
1493 | 0 | } |
1494 | | |
1495 | 0 | ROPTIONAL(RINFO, INFO, "Deleting inviable connection (%" PRIu64 ")", this->number); |
1496 | |
|
1497 | 0 | connection_close_internal(pool, this); |
1498 | 0 | connection_check(pool, request); |
1499 | 0 | pthread_mutex_unlock(&pool->mutex); |
1500 | | |
1501 | | /* |
1502 | | * Return an existing connection or spawn a new one. |
1503 | | */ |
1504 | 0 | return connection_get_internal(pool, request, true); |
1505 | 0 | } |
1506 | | |
1507 | | /** Delete a connection from the connection pool. |
1508 | | * |
1509 | | * Resolves the connection handle to a connection, then (if found) |
1510 | | * closes, unlinks and frees that connection. |
1511 | | * |
1512 | | * @note Must be called with the mutex free. |
1513 | | * |
1514 | | * @param[in] pool Connection pool to modify. |
1515 | | * @param[in] request The current request. |
1516 | | * @param[in] conn to delete. |
1517 | | * @return |
1518 | | * - 0 If the connection could not be found. |
1519 | | * - 1 if the connection was deleted. |
1520 | | */ |
1521 | | int fr_pool_connection_close(fr_pool_t *pool, request_t *request, void *conn) |
1522 | 0 | { |
1523 | 0 | fr_pool_connection_t *this; |
1524 | |
|
1525 | 0 | if (!conn) return 0; |
1526 | | |
1527 | 0 | pthread_mutex_lock(&pool->mutex); |
1528 | 0 | this = connection_find(pool, conn); |
1529 | 0 | if (!this) { |
1530 | 0 | pthread_mutex_unlock(&pool->mutex); |
1531 | 0 | return 0; |
1532 | 0 | } |
1533 | | |
1534 | | /* |
1535 | | * Record the last time a connection was closed |
1536 | | */ |
1537 | 0 | pool->state.last_closed = fr_time(); |
1538 | |
|
1539 | 0 | ROPTIONAL(RINFO, INFO, "Deleting connection (%" PRIu64 ")", this->number); |
1540 | |
|
1541 | 0 | connection_close_internal(pool, this); |
1542 | 0 | connection_check(pool, request); |
1543 | 0 | pthread_mutex_unlock(&pool->mutex); |
1544 | |
|
1545 | 0 | return 1; |
1546 | 0 | } |