rstatus_t modula_update(struct server_pool *pool) { uint32_t nserver; /* # server - live and dead */ uint32_t nlive_server; /* # live server */ uint32_t pointer_per_server; /* pointers per server proportional to weight */ uint32_t pointer_counter; /* # pointers on continuum */ uint32_t points_per_server; /* points per server */ uint32_t continuum_index; /* continuum index */ uint32_t continuum_addition; /* extra space in the continuum */ uint32_t server_index; /* server index */ uint32_t weight_index; /* weight index */ uint32_t total_weight; /* total live server weight */ int64_t now; /* current timestamp in usec */ now = nc_usec_now(); if (now < 0) { return NC_ERROR; } nserver = array_n(&pool->server); nlive_server = 0; total_weight = 0; pool->next_rebuild = 0LL; for (server_index = 0; server_index < nserver; server_index++) { struct server *server = array_get(&pool->server, server_index); if (pool->auto_eject_hosts) { if (server->fail == 0) { nlive_server++; } } else { nlive_server++; } ASSERT(server->weight > 0); /* count weight only for live servers */ if (!pool->auto_eject_hosts || server->fail == 0) { total_weight += server->weight; } } pool->nlive_server = nlive_server; if (nlive_server == 0) { ASSERT(pool->continuum != NULL); ASSERT(pool->ncontinuum != 0); log_debug(LOG_DEBUG, "no live servers for pool %"PRIu32" '%.*s'", pool->idx, pool->name.len, pool->name.data); return NC_OK; } log_debug(LOG_DEBUG, "%"PRIu32" of %"PRIu32" servers are live for pool " "%"PRIu32" '%.*s'", nlive_server, nserver, pool->idx, pool->name.len, pool->name.data); continuum_addition = MODULA_CONTINUUM_ADDITION; points_per_server = MODULA_POINTS_PER_SERVER; /* * Allocate the continuum for the pool, the first time, and every time we * add a new server to the pool */ if (total_weight > pool->nserver_continuum) { struct continuum *continuum; uint32_t nserver_continuum = total_weight + MODULA_CONTINUUM_ADDITION; uint32_t ncontinuum = nserver_continuum * MODULA_POINTS_PER_SERVER; continuum = nc_realloc(pool->continuum, sizeof(*continuum) * ncontinuum); if (continuum == NULL) { return NC_ENOMEM; } pool->continuum = continuum; pool->nserver_continuum = nserver_continuum; /* pool->ncontinuum is initialized later as it could be <= ncontinuum */ } /* update the continuum with the servers that are live */ continuum_index = 0; pointer_counter = 0; for (server_index = 0; server_index < nserver; server_index++) { struct server *server = array_get(&pool->server, server_index); if (pool->auto_eject_hosts && server->next_retry > now) { continue; } for (weight_index = 0; weight_index < server->weight; weight_index++) { pointer_per_server = 1; pool->continuum[continuum_index].index = server_index; pool->continuum[continuum_index++].value = 0; pointer_counter += pointer_per_server; } } pool->ncontinuum = pointer_counter; log_debug(LOG_VERB, "updated pool %"PRIu32" '%.*s' with %"PRIu32" of " "%"PRIu32" servers live in %"PRIu32" slots and %"PRIu32" " "active points in %"PRIu32" slots", pool->idx, pool->name.len, pool->name.data, nlive_server, nserver, pool->nserver_continuum, pool->ncontinuum, (pool->nserver_continuum + continuum_addition) * points_per_server); return NC_OK; }
rstatus_t ketama_update(struct server_pool *pool) { uint32_t nserver; /* # server - live and dead */ uint32_t nlive_server; /* # live server */ uint32_t pointer_per_server; /* pointers per server proportional to weight */ uint32_t pointer_per_hash; /* pointers per hash */ uint32_t pointer_counter; /* # pointers on continuum */ uint32_t pointer_index; /* pointer index */ uint32_t points_per_server; /* points per server */ uint32_t continuum_index; /* continuum index */ uint32_t continuum_addition; /* extra space in the continuum */ uint32_t server_index; /* server index */ uint32_t value; /* continuum value */ uint32_t total_weight; /* total live server weight */ int64_t now; /* current timestamp in usec */ ASSERT(array_n(&pool->server) > 0); now = nc_usec_now(); if (now < 0) { return NC_ERROR; } /* * Count live servers and total weight, and also update the next time to * rebuild the distribution */ nserver = array_n(&pool->server); nlive_server = 0; total_weight = 0; pool->next_rebuild = 0LL; for (server_index = 0; server_index < nserver; server_index++) { struct server *server = array_get(&pool->server, server_index); if (pool->auto_eject_hosts) { if (server->next_retry <= now) { server->next_retry = 0LL; nlive_server++; } else if (pool->next_rebuild == 0LL || server->next_retry < pool->next_rebuild) { pool->next_rebuild = server->next_retry; } } else { nlive_server++; } ASSERT(server->weight > 0); /* count weight only for live servers */ if (!pool->auto_eject_hosts || server->next_retry <= now) { total_weight += server->weight; } } pool->nlive_server = nlive_server; if (nlive_server == 0) { log_debug(LOG_DEBUG, "no live servers for pool %"PRIu32" '%.*s'", pool->idx, pool->name.len, pool->name.data); return NC_OK; } log_debug(LOG_DEBUG, "%"PRIu32" of %"PRIu32" servers are live for pool " "%"PRIu32" '%.*s'", nlive_server, nserver, pool->idx, pool->name.len, pool->name.data); continuum_addition = KETAMA_CONTINUUM_ADDITION; points_per_server = KETAMA_POINTS_PER_SERVER; /* * Allocate the continuum for the pool, the first time, and every time we * add a new server to the pool */ if (nlive_server > pool->nserver_continuum) { struct continuum *continuum; uint32_t nserver_continuum = nlive_server + continuum_addition; uint32_t ncontinuum = nserver_continuum * points_per_server; continuum = nc_realloc(pool->continuum, sizeof(*continuum) * ncontinuum); if (continuum == NULL) { return NC_ENOMEM; } pool->continuum = continuum; pool->nserver_continuum = nserver_continuum; /* pool->ncontinuum is initialized later as it could be <= ncontinuum */ } /* * Build a continuum with the servers that are live and points from * these servers that are proportial to their weight */ continuum_index = 0; pointer_counter = 0; for (server_index = 0; server_index < nserver; server_index++) { struct server *server; float pct; server = array_get(&pool->server, server_index); if (pool->auto_eject_hosts && pool->gutter == NULL && server->next_retry > now) { continue; } pct = (float)server->weight / (float)total_weight; pointer_per_server = (uint32_t) ((floorf((float) (pct * KETAMA_POINTS_PER_SERVER / 4 * (float)nlive_server + 0.0000000001))) * 4); pointer_per_hash = 4; log_debug(LOG_VERB, "%.*s:%"PRIu16" weight %"PRIu32" of %"PRIu32" " "pct %0.5f points per server %"PRIu32"", server->name.len, server->name.data, server->port, server->weight, total_weight, pct, pointer_per_server); for (pointer_index = 1; pointer_index <= pointer_per_server / pointer_per_hash; pointer_index++) { char host[KETAMA_MAX_HOSTLEN]= ""; size_t hostlen; uint32_t x; hostlen = snprintf(host, KETAMA_MAX_HOSTLEN, "%.*s-%u", server->name.len, server->name.data, pointer_index - 1); for (x = 0; x < pointer_per_hash; x++) { value = ketama_hash(host, hostlen, x); pool->continuum[continuum_index].index = server_index; pool->continuum[continuum_index++].value = value; } } pointer_counter += pointer_per_server; } pool->ncontinuum = pointer_counter; qsort(pool->continuum, pool->ncontinuum, sizeof(*pool->continuum), ketama_item_cmp); for (pointer_index = 0; pointer_index < ((nlive_server * KETAMA_POINTS_PER_SERVER) - 1); pointer_index++) { if (pointer_index + 1 >= pointer_counter) { break; } ASSERT(pool->continuum[pointer_index].value <= pool->continuum[pointer_index + 1].value); } log_debug(LOG_VERB, "updated pool %"PRIu32" '%.*s' with %"PRIu32" of " "%"PRIu32" servers live in %"PRIu32" slots and %"PRIu32" " "active points in %"PRIu32" slots", pool->idx, pool->name.len, pool->name.data, nlive_server, nserver, pool->nserver_continuum, pool->ncontinuum, (pool->nserver_continuum + continuum_addition) * points_per_server); return NC_OK; }
/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */ NC_MSG_TYPE nc_read_msg_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data, int passing_io_lock) { int ret, io_locked = passing_io_lock; char *msg = NULL, *chunk; uint64_t chunk_len, len = 0; /* use timeout in milliseconds instead seconds */ uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000; struct timespec ts_act_timeout; struct nc_server_reply *reply; assert(session && data); *data = NULL; if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) { ERR("Session %u: invalid session to read from.", session->id); ret = NC_MSG_ERROR; goto cleanup; } nc_gettimespec_mono(&ts_act_timeout); nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000); if (!io_locked) { /* SESSION IO LOCK */ ret = nc_session_io_lock(session, io_timeout, __func__); if (ret < 0) { ret = NC_MSG_ERROR; goto cleanup; } else if (!ret) { ret = NC_MSG_WOULDBLOCK; goto cleanup; } io_locked = 1; } /* read the message */ switch (session->version) { case NC_VERSION_10: ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg); if (ret == -1) { ret = NC_MSG_ERROR; goto cleanup; } /* cut off the end tag */ msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0'; break; case NC_VERSION_11: while (1) { ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL); if (ret == -1) { ret = NC_MSG_ERROR; goto cleanup; } ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk); if (ret == -1) { ret = NC_MSG_ERROR; goto cleanup; } if (!strcmp(chunk, "#\n")) { /* end of chunked framing message */ free(chunk); if (!msg) { ERR("Session %u: invalid frame chunk delimiters.", session->id); goto malformed_msg; } break; } /* convert string to the size of the following chunk */ chunk_len = strtoul(chunk, (char **)NULL, 10); free(chunk); if (!chunk_len) { ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id); goto malformed_msg; } /* now we have size of next chunk, so read the chunk */ ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk); if (ret == -1) { ret = NC_MSG_ERROR; goto cleanup; } /* realloc message buffer, remember to count terminating null byte */ msg = nc_realloc(msg, len + chunk_len + 1); if (!msg) { ERRMEM; ret = NC_MSG_ERROR; goto cleanup; } memcpy(msg + len, chunk, chunk_len); len += chunk_len; msg[len] = '\0'; free(chunk); } break; } /* SESSION IO UNLOCK */ assert(io_locked); nc_session_io_unlock(session, __func__); io_locked = 0; DBG("Session %u: received message:\n%s\n", session->id, msg); /* build XML tree */ *data = lyxml_parse_mem(session->ctx, msg, 0); if (!*data) { goto malformed_msg; } else if (!(*data)->ns) { ERR("Session %u: invalid message root element (invalid namespace).", session->id); goto malformed_msg; } free(msg); msg = NULL; /* get and return message type */ if (!strcmp((*data)->ns->value, NC_NS_BASE)) { if (!strcmp((*data)->name, "rpc")) { return NC_MSG_RPC; } else if (!strcmp((*data)->name, "rpc-reply")) { return NC_MSG_REPLY; } else if (!strcmp((*data)->name, "hello")) { return NC_MSG_HELLO; } else { ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name); goto malformed_msg; } } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) { if (!strcmp((*data)->name, "notification")) { return NC_MSG_NOTIF; } else { ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name); goto malformed_msg; } } else { ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value); goto malformed_msg; } malformed_msg: ERR("Session %u: malformed message received.", session->id); if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) { /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */ reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG)); if (io_locked) { /* nc_write_msg_io locks and unlocks the lock by itself */ nc_session_io_unlock(session, __func__); io_locked = 0; } if (nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, NULL, reply) != NC_MSG_REPLY) { ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id); if (session->status != NC_STATUS_INVALID) { session->status = NC_STATUS_INVALID; session->term_reason = NC_SESSION_TERM_OTHER; } } nc_server_reply_free(reply); } ret = NC_MSG_ERROR; cleanup: if (io_locked) { nc_session_io_unlock(session, __func__); } free(msg); free(*data); *data = NULL; return ret; }
rstatus_t ketama_update(struct server_pool *pool) { uint32_t nserver; /* # server - live and dead */ uint32_t nlive_server; /* # live server */ uint32_t pointer_per_server; /* pointers per server proportional to weight */ uint32_t pointer_per_hash; /* pointers per hash */ uint32_t pointer_counter; /* # pointers on continuum */ uint32_t pointer_index; /* pointer index */ uint32_t points_per_server; /* points per server */ uint32_t continuum_index; /* continuum index */ uint32_t continuum_addition; /* extra space in the continuum */ uint32_t server_index; /* server index */ uint32_t value; /* continuum value */ uint32_t total_weight; /* total live server weight */ int64_t now; /* current timestamp in usec */ ASSERT(array_n(&pool->server) > 0); now = nc_usec_now(); if (now < 0) { return NC_ERROR; } /* * Count live servers and total weight, and also update the next time to * rebuild the distribution */ nserver = array_n(&pool->server); nlive_server = 0; total_weight = 0; pool->next_rebuild = 0LL; for (server_index = 0; server_index < nserver; server_index++) { struct server *server = array_get(&pool->server, server_index); if (pool->auto_eject_hosts) { if (server->next_retry <= now) { server->next_retry = 0LL; nlive_server++; } else if (pool->next_rebuild == 0LL || server->next_retry < pool->next_rebuild) { pool->next_rebuild = server->next_retry; } } else { nlive_server++; } ASSERT(server->weight > 0); /* count weight only for live servers */ if (!pool->auto_eject_hosts || server->next_retry <= now) { total_weight += server->weight; } } pool->nlive_server = nlive_server; if (nlive_server == 0) { log_debug(LOG_DEBUG, "no live servers for pool %"PRIu32" '%.*s'", pool->idx, pool->name.len, pool->name.data); return NC_OK; } //log_debug(LOG_DEBUG, "%"PRIu32" of %"PRIu32" servers are live for pool " loga("%"PRIu32" of %"PRIu32" servers are live for pool " "%"PRIu32" '%.*s'", nlive_server, nserver, pool->idx, pool->name.len, pool->name.data); /* * Allocate the continuum for the pool, the first time, and every time we * add a new server to the pool */ if (nlive_server > pool->nserver_continuum) { struct continuum *continuum; //uint32_t nserver_continuum = nlive_server + continuum_addition; //uint32_t ncontinuum = nserver_continuum * points_per_server; uint32_t nserver_continuum = nlive_server; uint32_t ncontinuum = nserver_continuum; continuum = nc_realloc(pool->continuum, sizeof(*continuum) * ncontinuum); if (continuum == NULL) { return NC_ENOMEM; } pool->continuum = continuum; pool->nserver_continuum = nserver_continuum; pool->ncontinuum = ncontinuum; loga("pool->continuum: %u pool->ncontinuum: %u, pool->nserver_continuum: %u", pool->continuum, pool->ncontinuum, pool->nserver_continuum); /* pool->ncontinuum is initialized later as it could be <= ncontinuum */ } /* * Build a continuum with the servers that are live and points from * these servers that are proportial to their weight */ continuum_index = 0; pointer_counter = 0; for (server_index = 0; server_index < nserver; server_index++) { struct server *server; float pct; server = array_get(&pool->server, server_index); if (pool->auto_eject_hosts && server->next_retry > now) { continue; } uint32_t hash_value = strtoul (server->name.data, NULL, 10); pool->continuum[continuum_index].index = server_index; pool->continuum[continuum_index++].value = hash_value; loga("+++++++ hash_value: %u index: %u +++++++", hash_value, server_index); } return NC_OK; }
static ssize_t nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint32_t inact_timeout, struct timespec *ts_act_timeout, char **result) { char *chunk = NULL; size_t size, count = 0, r, len, i, matched = 0; assert(session); assert(endtag); if (limit && limit < BUFFERSIZE) { size = limit; } else { size = BUFFERSIZE; } chunk = malloc((size + 1) * sizeof *chunk); if (!chunk) { ERRMEM; return -1; } len = strlen(endtag); while (1) { if (limit && count == limit) { free(chunk); WRN("Session %u: reading limit (%d) reached.", session->id, limit); ERR("Session %u: invalid input data (missing \"%s\" sequence).", session->id, endtag); return -1; } /* resize buffer if needed */ if ((count + (len - matched)) >= size) { /* get more memory */ size = size + BUFFERSIZE; chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk); if (!chunk) { ERRMEM; return -1; } } /* get another character */ r = nc_read(session, &(chunk[count]), len - matched, inact_timeout, ts_act_timeout); if (r != len - matched) { free(chunk); return -1; } count += len - matched; for (i = len - matched; i > 0; i--) { if (!strncmp(&endtag[matched], &(chunk[count - i]), i)) { /*part of endtag found */ matched += i; break; } else { matched = 0; } } /* whole endtag found */ if (matched == len) { break; } } /* terminating null byte */ chunk[count] = 0; if (result) { *result = chunk; } else { free(chunk); } return count; }