in zookeeper-client/zookeeper-client-c/src/zookeeper.c [1041:1206]
int update_addrs(zhandle_t *zh, const struct timeval *ref_time)
{
int rc = ZOK;
char *hosts = NULL;
uint32_t num_old = 0;
uint32_t num_new = 0;
uint32_t i = 0;
int found_current = 0;
addrvec_t resolved = { 0 };
// Verify we have a valid handle
if (zh == NULL) {
return ZBADARGUMENTS;
}
// zh->hostname should always be set
if (zh->hostname == NULL)
{
return ZSYSTEMERROR;
}
// NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms}
lock_reconfig(zh);
// Check if we are due for a host name resolution. (See
// zoo_set_servers_resolution_delay. The answer is always "yes"
// if no reference is provided or the file descriptor is invalid.)
if (ref_time && zh->fd->sock != -1) {
int do_resolve;
if (zh->resolve_delay_ms <= 0) {
// -1 disables, 0 means unconditional. Fail safe.
do_resolve = zh->resolve_delay_ms != -1;
} else {
int elapsed_ms = calculate_interval(&zh->last_resolve, ref_time);
// Include < 0 in case of overflow, or if we are not
// backed by a monotonic clock.
do_resolve = elapsed_ms > zh->resolve_delay_ms || elapsed_ms < 0;
}
if (!do_resolve) {
goto finish;
}
}
// Copy zh->hostname for local use
hosts = strdup(zh->hostname);
if (hosts == NULL) {
rc = ZSYSTEMERROR;
goto finish;
}
rc = resolve_hosts(zh, hosts, &resolved);
if (rc != ZOK)
{
goto finish;
}
// Unconditionally note last resolution time.
if (ref_time) {
zh->last_resolve = *ref_time;
} else {
get_system_time(&zh->last_resolve);
}
// If the addrvec list is identical to last time we ran don't do anything
if (addrvec_eq(&zh->addrs, &resolved))
{
goto finish;
}
// Is the server we're connected to in the new resolved list?
found_current = addrvec_contains(&resolved, &zh->addr_cur);
// Clear out old and new address lists
zh->reconfig = 1;
addrvec_free(&zh->addrs_old);
addrvec_free(&zh->addrs_new);
// Divide server list into addrs_old if in previous list and addrs_new if not
for (i = 0; i < resolved.count; i++)
{
struct sockaddr_storage *resolved_address = &resolved.data[i];
if (addrvec_contains(&zh->addrs, resolved_address))
{
rc = addrvec_append(&zh->addrs_old, resolved_address);
if (rc != ZOK)
{
goto finish;
}
}
else {
rc = addrvec_append(&zh->addrs_new, resolved_address);
if (rc != ZOK)
{
goto finish;
}
}
}
num_old = zh->addrs_old.count;
num_new = zh->addrs_new.count;
// Number of servers increased
if (num_old + num_new > zh->addrs.count)
{
if (found_current) {
// my server is in the new config, but load should be decreased.
// Need to decide if the client is moving to one of the new servers
if (drand48() <= (1 - ((double)zh->addrs.count) / (num_old + num_new))) {
zh->pNew = 1;
zh->pOld = 0;
} else {
// do nothing special -- stay with the current server
zh->reconfig = 0;
}
} else {
// my server is not in the new config, and load on old servers must
// be decreased, so connect to one of the new servers
zh->pNew = 1;
zh->pOld = 0;
}
}
// Number of servers stayed the same or decreased
else {
if (found_current) {
// my server is in the new config, and load should be increased, so
// stay with this server and do nothing special
zh->reconfig = 0;
} else {
zh->pOld = ((double) (num_old * (zh->addrs.count - (num_old + num_new)))) / ((num_old + num_new) * (zh->addrs.count - num_old));
zh->pNew = 1 - zh->pOld;
}
}
addrvec_free(&zh->addrs);
zh->addrs = resolved;
// If we need to do a reconfig and we're currently connected to a server,
// then force close that connection so on next interest() call we'll make a
// new connection
if (zh->reconfig == 1 && zh->fd->sock != -1)
{
close_zsock(zh->fd);
zh->state = ZOO_NOTCONNECTED_STATE;
}
finish:
unlock_reconfig(zh);
// If we short-circuited out and never assigned resolved to zh->addrs then we
// need to free resolved to avoid a memleak.
if (resolved.data && zh->addrs.data != resolved.data)
{
addrvec_free(&resolved);
}
if (hosts) {
free(hosts);
hosts = NULL;
}
return rc;
}