Skip to content

Commit c8bd83a

Browse files
authored
Dynamic Server List (#594)
This PR makes the server list a dynamic sorted list of servers. The sort order is [ consecutive failures, system config index ]. The server list can be updated via ares_set_servers_*(). Any queries currently directed to servers that are no longer in the list will be automatically re-queued to a different server. Also, any time a failure occurs on the server, the sort order of the servers will be updated so that the one with the fewest consecutive failures is chosen for the next query that goes on the wire, this way bad or non-responsive servers are automatically isolated. Since the server list is now dynamic, the tracking of query failures per server has been removed and instead is relying on the server sort order as previously described. This simplifies the logic while also reducing the amount of memory required per query. However, because of this dynamic nature, it may not be easy to determine the server attempt order for enqueued queries if there have been any failures. If using the ARES_OPT_ROTATE, this is now implemented to be a random selection of the configured servers. Since the server list is dynamic, its not possible to go to the next server as configuration could have changed between queries or attempts for the same query. Finally, this PR moved some existing functions into new files to logically separate them. This should address issues #550 and #440, while also setting the framework to implement #301. #301 needs a little more effort since it configures things other than the servers themselves (domains, search, sortlist, lookups), which need to make sure they can be safely updated. Fix By: Brad House (@bradh352)
1 parent 7cab8b8 commit c8bd83a

25 files changed

Lines changed: 3694 additions & 3037 deletions

src/lib/Makefile.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ CSOURCES = ares__addrinfo2hostent.c \
6363
ares_str.c \
6464
ares_strerror.c \
6565
ares_strsplit.c \
66+
ares_sysconfig.c \
67+
ares_sysconfig_files.c \
6668
ares_timeout.c \
69+
ares_update_servers.c \
6770
ares_version.c \
6871
bitncmp.c \
6972
inet_net_pton.c \

src/lib/ares__close_sockets.c

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,41 @@
3131
#include "ares_private.h"
3232
#include <assert.h>
3333

34+
static void ares__requeue_queries(struct server_connection *conn)
35+
{
36+
struct query *query;
37+
struct timeval now = ares__tvnow();
38+
39+
while ((query = ares__llist_first_val(conn->queries_to_conn)) != NULL) {
40+
ares__requeue_query(query, &now);
41+
}
42+
}
43+
3444
void ares__close_connection(struct server_connection *conn)
3545
{
3646
struct server_state *server = conn->server;
3747
ares_channel channel = server->channel;
3848

49+
/* Unlink */
50+
ares__llist_node_claim(
51+
ares__htable_asvp_get_direct(channel->connnode_by_socket, conn->fd));
52+
ares__htable_asvp_remove(channel->connnode_by_socket, conn->fd);
53+
3954
if (conn->is_tcp) {
4055
/* Reset any existing input and output buffer. */
4156
ares__buf_consume(server->tcp_parser, ares__buf_len(server->tcp_parser));
4257
ares__buf_consume(server->tcp_send, ares__buf_len(server->tcp_send));
43-
server->tcp_connection_generation = ++channel->tcp_connection_generation;
4458
server->tcp_conn = NULL;
4559
}
4660

61+
/* Requeue queries to other connections */
62+
ares__requeue_queries(conn);
63+
64+
ares__llist_destroy(conn->queries_to_conn);
4765

4866
SOCK_STATE_CALLBACK(channel, conn->fd, 0, 0);
4967
ares__close_socket(channel, conn->fd);
50-
ares__llist_node_claim(
51-
ares__htable_asvp_get_direct(channel->connnode_by_socket, conn->fd));
52-
ares__htable_asvp_remove(channel->connnode_by_socket, conn->fd);
5368

54-
#ifndef NDEBUG
55-
assert(ares__llist_len(conn->queries_to_conn) == 0);
56-
#endif
57-
ares__llist_destroy(conn->queries_to_conn);
5869
ares_free(conn);
5970
}
6071

@@ -68,18 +79,13 @@ void ares__close_sockets(struct server_state *server)
6879
}
6980
}
7081

71-
void ares__check_cleanup_conn(ares_channel channel, ares_socket_t fd)
82+
void ares__check_cleanup_conn(ares_channel channel,
83+
struct server_connection *conn)
7284
{
73-
ares__llist_node_t *node;
74-
struct server_connection *conn;
75-
ares_bool_t do_cleanup = ARES_FALSE;
85+
ares_bool_t do_cleanup = ARES_FALSE;
7686

77-
node = ares__htable_asvp_get_direct(channel->connnode_by_socket, fd);
78-
if (node == NULL) {
87+
if (channel == NULL || conn == NULL)
7988
return;
80-
}
81-
82-
conn = ares__llist_node_val(node);
8389

8490
if (ares__llist_len(conn->queries_to_conn)) {
8591
return;
@@ -96,7 +102,8 @@ void ares__check_cleanup_conn(ares_channel channel, ares_socket_t fd)
96102
do_cleanup = ARES_TRUE;
97103
}
98104

99-
if (do_cleanup) {
100-
ares__close_connection(conn);
101-
}
105+
if (!do_cleanup)
106+
return;
107+
108+
ares__close_connection(conn);
102109
}

src/lib/ares__slist.c

Lines changed: 114 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -143,53 +143,10 @@ static size_t ares__slist_calc_level(ares__slist_t *list)
143143
return level;
144144
}
145145

146-
ares__slist_node_t *ares__slist_insert(ares__slist_t *list, void *val)
146+
static void ares__slist_node_push(ares__slist_t *list, ares__slist_node_t *node)
147147
{
148-
ares__slist_node_t *node = NULL;
149-
ares__slist_node_t *left = NULL;
150148
size_t i;
151-
152-
if (list == NULL || val == NULL) {
153-
return NULL;
154-
}
155-
156-
node = ares_malloc_zero(sizeof(*node));
157-
158-
if (node == NULL) {
159-
goto fail;
160-
}
161-
162-
node->data = val;
163-
node->parent = list;
164-
165-
/* Randomly determine the number of levels we want to use */
166-
node->levels = ares__slist_calc_level(list);
167-
168-
/* Allocate array of next and prev nodes for linking each level */
169-
node->next = ares_malloc_zero(sizeof(*node->next) * node->levels);
170-
if (node->next == NULL) {
171-
goto fail;
172-
}
173-
174-
node->prev = ares_malloc_zero(sizeof(*node->prev) * node->levels);
175-
if (node->prev == NULL) {
176-
goto fail;
177-
}
178-
179-
/* If the number of levels is greater than we currently support in the slist,
180-
* increase the count */
181-
if (list->levels < node->levels) {
182-
void *ptr =
183-
ares_realloc_zero(list->head, sizeof(*list->head) * list->levels,
184-
sizeof(*list->head) * node->levels);
185-
if (ptr == NULL) {
186-
goto fail;
187-
}
188-
189-
list->head = ptr;
190-
list->levels = node->levels;
191-
}
192-
149+
ares__slist_node_t *left = NULL;
193150

194151
/* Scan from highest level in the slist, even if we're not using that number
195152
* of levels for this entry as this is what makes it O(log n) */
@@ -236,6 +193,54 @@ ares__slist_node_t *ares__slist_insert(ares__slist_t *list, void *val)
236193
}
237194
}
238195
}
196+
}
197+
198+
ares__slist_node_t *ares__slist_insert(ares__slist_t *list, void *val)
199+
{
200+
ares__slist_node_t *node = NULL;
201+
202+
if (list == NULL || val == NULL) {
203+
return NULL;
204+
}
205+
206+
node = ares_malloc_zero(sizeof(*node));
207+
208+
if (node == NULL) {
209+
goto fail;
210+
}
211+
212+
node->data = val;
213+
node->parent = list;
214+
215+
/* Randomly determine the number of levels we want to use */
216+
node->levels = ares__slist_calc_level(list);
217+
218+
/* Allocate array of next and prev nodes for linking each level */
219+
node->next = ares_malloc_zero(sizeof(*node->next) * node->levels);
220+
if (node->next == NULL) {
221+
goto fail;
222+
}
223+
224+
node->prev = ares_malloc_zero(sizeof(*node->prev) * node->levels);
225+
if (node->prev == NULL) {
226+
goto fail;
227+
}
228+
229+
/* If the number of levels is greater than we currently support in the slist,
230+
* increase the count */
231+
if (list->levels < node->levels) {
232+
void *ptr =
233+
ares_realloc_zero(list->head, sizeof(*list->head) * list->levels,
234+
sizeof(*list->head) * node->levels);
235+
if (ptr == NULL) {
236+
goto fail;
237+
}
238+
239+
list->head = ptr;
240+
list->levels = node->levels;
241+
}
242+
243+
ares__slist_node_push(list, node);
239244

240245
list->cnt++;
241246

@@ -250,6 +255,70 @@ ares__slist_node_t *ares__slist_insert(ares__slist_t *list, void *val)
250255
return NULL;
251256
}
252257

258+
static void ares__slist_node_pop(ares__slist_node_t *node)
259+
{
260+
ares__slist_t *list = node->parent;
261+
size_t i;
262+
263+
/* relink each node at each level */
264+
for (i = node->levels; i-- > 0;) {
265+
if (node->next[i] == NULL) {
266+
if (i == 0) {
267+
list->tail = node->prev[0];
268+
}
269+
} else {
270+
node->next[i]->prev[i] = node->prev[i];
271+
}
272+
273+
if (node->prev[i] == NULL) {
274+
list->head[i] = node->next[i];
275+
} else {
276+
node->prev[i]->next[i] = node->next[i];
277+
}
278+
}
279+
280+
memset(node->next, 0, sizeof(*node->next) * node->levels);
281+
memset(node->prev, 0, sizeof(*node->prev) * node->levels);
282+
}
283+
284+
void *ares__slist_node_claim(ares__slist_node_t *node)
285+
{
286+
ares__slist_t *list;
287+
void *val;
288+
289+
if (node == NULL) {
290+
return NULL;
291+
}
292+
293+
list = node->parent;
294+
val = node->data;
295+
296+
ares__slist_node_pop(node);
297+
298+
ares_free(node->next);
299+
ares_free(node->prev);
300+
ares_free(node);
301+
302+
list->cnt--;
303+
304+
return val;
305+
}
306+
307+
308+
void ares__slist_node_reinsert(ares__slist_node_t *node)
309+
{
310+
ares__slist_t *list;
311+
312+
if (node == NULL)
313+
return;
314+
315+
list = node->parent;
316+
317+
ares__slist_node_pop(node);
318+
ares__slist_node_push(list, node);
319+
}
320+
321+
253322
ares__slist_node_t *ares__slist_node_find(const ares__slist_t *list,
254323
const void *val)
255324
{
@@ -378,45 +447,6 @@ void *ares__slist_last_val(const ares__slist_t *list)
378447
return ares__slist_node_val(ares__slist_node_last(list));
379448
}
380449

381-
void *ares__slist_node_claim(ares__slist_node_t *node)
382-
{
383-
void *val;
384-
ares__slist_t *list;
385-
size_t i;
386-
387-
if (node == NULL) {
388-
return NULL;
389-
}
390-
391-
list = node->parent;
392-
val = node->data;
393-
394-
/* relink each node at each level */
395-
for (i = node->levels; i-- > 0;) {
396-
if (node->next[i] == NULL) {
397-
if (i == 0) {
398-
list->tail = node->prev[0];
399-
}
400-
} else {
401-
node->next[i]->prev[i] = node->prev[i];
402-
}
403-
404-
if (node->prev[i] == NULL) {
405-
list->head[i] = node->next[i];
406-
} else {
407-
node->prev[i]->next[i] = node->next[i];
408-
}
409-
}
410-
411-
ares_free(node->next);
412-
ares_free(node->prev);
413-
ares_free(node);
414-
415-
list->cnt--;
416-
417-
return val;
418-
}
419-
420450
void ares__slist_node_destroy(ares__slist_node_t *node)
421451
{
422452
ares__slist_destructor_t destruct;

src/lib/ares__slist.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,15 @@ void *ares__slist_last_val(const ares__slist_t *list);
181181
*/
182182
void *ares__slist_node_claim(ares__slist_node_t *node);
183183

184+
/*! The internals of the node have changed, thus its position in the sorted
185+
* list is no longer valid. This function will remove it and readd it to the
186+
* proper position without needing to perform any memory allocations and
187+
* thus cannot fail.
188+
*
189+
* \param[in] node SkipList Node Object
190+
*/
191+
void ares__slist_node_reinsert(ares__slist_node_t *node);
192+
184193
/*! Remove Node from SkipList, calling destructor for Node Value.
185194
*
186195
* \param[in] node SkipList Node Object

src/lib/ares__socket.c

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -245,35 +245,28 @@ ares_status_t ares__open_connection(ares_channel channel,
245245
struct sockaddr_in6 sa6;
246246
} saddr;
247247
struct sockaddr *sa;
248-
unsigned short port;
249248
struct server_connection *conn;
250249
ares__llist_node_t *node;
251250
int type = is_tcp ? SOCK_STREAM : SOCK_DGRAM;
252251

253-
if (is_tcp) {
254-
port = server->addr.tcp_port ? server->addr.tcp_port : channel->tcp_port;
255-
} else {
256-
port = server->addr.udp_port ? server->addr.udp_port : channel->udp_port;
257-
}
258-
259252
switch (server->addr.family) {
260253
case AF_INET:
261254
sa = (void *)&saddr.sa4;
262255
salen = sizeof(saddr.sa4);
263256
memset(sa, 0, (size_t)salen);
264257
saddr.sa4.sin_family = AF_INET;
265-
saddr.sa4.sin_port = port;
266-
memcpy(&saddr.sa4.sin_addr, &server->addr.addrV4,
267-
sizeof(server->addr.addrV4));
258+
saddr.sa4.sin_port = htons(is_tcp?server->tcp_port:server->udp_port);
259+
memcpy(&saddr.sa4.sin_addr, &server->addr.addr.addr4,
260+
sizeof(saddr.sa4.sin_addr));
268261
break;
269262
case AF_INET6:
270263
sa = (void *)&saddr.sa6;
271264
salen = sizeof(saddr.sa6);
272265
memset(sa, 0, (size_t)salen);
273266
saddr.sa6.sin6_family = AF_INET6;
274-
saddr.sa6.sin6_port = port;
275-
memcpy(&saddr.sa6.sin6_addr, &server->addr.addrV6,
276-
sizeof(server->addr.addrV6));
267+
saddr.sa6.sin6_port = htons(is_tcp?server->tcp_port:server->udp_port);
268+
memcpy(&saddr.sa6.sin6_addr, &server->addr.addr.addr6,
269+
sizeof(saddr.sa6.sin6_addr));
277270
break;
278271
default:
279272
return ARES_EBADFAMILY; /* LCOV_EXCL_LINE */
@@ -380,7 +373,6 @@ ares_status_t ares__open_connection(ares_channel channel,
380373
SOCK_STATE_CALLBACK(channel, s, 1, 0);
381374

382375
if (is_tcp) {
383-
server->tcp_connection_generation = ++channel->tcp_connection_generation;
384376
server->tcp_conn = conn;
385377
}
386378

0 commit comments

Comments
 (0)