int update_addrs()

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;
}