ngx_int_t
ngx_http_upstream_init_consistent_hash(ngx_conf_t *cf, 
        ngx_http_upstream_srv_conf_t *us)
{
    /* ip max 15, :port max 6, maxweight is highest number of uchar */
    u_char                                       *last, hash_data[28];
    uint32_t                                      step;
    ngx_uint_t                                    i, j, k, n;
    ngx_uint_t                                    real_nodes, points_per_node;
    ngx_http_upstream_server_t                   *server;
    ngx_http_upstream_rr_peer_t                  *rr_peer;
    ngx_http_upstream_rr_peers_t                 *prr_peers;
    ngx_http_upstream_consistent_hash_buckets    *buckets;
    ngx_http_upstream_consistent_hash_continuum       *continuum;
    ngx_http_upstream_consistent_hash_srv_conf_t      *uchscf;


    uchscf = ngx_http_conf_upstream_srv_conf(us,
            ngx_http_upstream_consistent_hash_module);
    if (uchscf == NULL) {
        return NGX_ERROR;
    }

    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
        return NGX_ERROR;
    }

    prr_peers = us->peer.data;
    us->peer.init = ngx_http_upstream_init_consistent_hash_peer;

    buckets = ngx_pcalloc(cf->pool, 
            sizeof(ngx_http_upstream_consistent_hash_buckets));

    if (!us->servers) {
        return NGX_ERROR;
    }

    server = us->servers->elts;

    n = real_nodes = 0;
    for (i = 0; i < us->servers->nelts; i++) {
        n += server[i].naddrs;
        real_nodes += server[i].weight * server[i].naddrs;
    }
    
    /*
     * The optimal points number is Q/S
     * See the section 6.2 from the paper 'Dynamo: Amazon's Highly Available
     * Key-value Store'
     */
    points_per_node = (ngx_uint_t) MMC_CONSISTENT_BUCKETS / real_nodes;
    if (points_per_node == 0) {
        points_per_node = 1;
    }

    continuum = ngx_pcalloc(cf->pool, 
            sizeof(ngx_http_upstream_consistent_hash_continuum));
    continuum->nodes = ngx_palloc(cf->pool, 
            sizeof(ngx_http_upstream_consistent_hash_node) * MMC_CONSISTENT_BUCKETS);

    for (i = 0; i < us->servers->nelts; i++) {
        for (j = 0; j < server[i].naddrs; j++) {

            rr_peer = ngx_http_upstream_consistent_hash_find_rr_peer(prr_peers,
                    server[i].addrs[j].sockaddr);
            if (rr_peer == NULL) {
                return NGX_ERROR;
            }

            for (k = 0; k < (points_per_node * server[i].weight); k++) {
                last = ngx_snprintf(hash_data, 28, "%V-%ui",
                        &server[i].addrs[j].name, k);

                continuum->nodes[continuum->nnodes].point =
                    ngx_http_upstream_consistent_hash_node_point(hash_data, 
                            (last - hash_data));

                continuum->nodes[continuum->nnodes].rr_peer = rr_peer;
                continuum->nnodes++;
            }
        }
    }

    ngx_qsort(continuum->nodes, continuum->nnodes, 
            sizeof(ngx_http_upstream_consistent_hash_node), 
            (const void*) ngx_http_upstream_consistent_hash_compare_continuum_nodes);

    step = (uint32_t) (0xffffffff / MMC_CONSISTENT_BUCKETS);

    for (i = 0; i < MMC_CONSISTENT_BUCKETS; i++) {
        buckets->bucket[i] = 
            ngx_http_upstream_consistent_hash_find(continuum, step * i);
    }

#if (CONSISTENT_DEBUG)
    ngx_http_upstream_consistent_hash_print_continuum(cf, continuum);
    ngx_http_upstream_consistent_hash_print_buckets(cf, buckets);
#endif

    buckets->continuum = continuum;
    uchscf->data = buckets;

    return NGX_OK;
}
ngx_int_t
ngx_http_upstream_init_consistent_hash(ngx_conf_t *cf, 
        ngx_http_upstream_srv_conf_t *us)
{
    /* ip max 15, :port max 6, maxweight is highest number of uchar */
    u_char                                        hash_data[HASH_DATA_LENGTH];
    uint32_t                                      step;
    ngx_uint_t                                    i, j, k, n, points = 0;
    ngx_http_upstream_server_t                   *server;
    ngx_http_upstream_consistent_hash_buckets    *buckets;
    ngx_http_upstream_consistent_hash_continuum  *continuum;

    for (i=0;i<HASH_DATA_LENGTH;i++) hash_data[i] = 0;

    step = (uint32_t) (0xffffffff / MMC_CONSISTENT_BUCKETS);

    buckets = ngx_pcalloc(cf->pool, 
            sizeof(ngx_http_upstream_consistent_hash_buckets));

    us->peer.init = ngx_http_upstream_init_consistent_hash_peer;

    if (!us->servers) {
        return NGX_ERROR;
    }

    server = us->servers->elts;

    for (n = 0, i = 0; i < us->servers->nelts; i++) {
        n += server[i].naddrs;
        points += server[i].weight * server[i].naddrs * MMC_CONSISTENT_POINTS;
    }

    continuum = ngx_pcalloc(cf->pool, 
            sizeof(ngx_http_upstream_consistent_hash_continuum));
    continuum->nodes = ngx_pcalloc(cf->pool, 
            sizeof(ngx_http_upstream_consistent_hash_node) * points);

    for (i = 0; i < us->servers->nelts; i++) {
        for (j = 0; j < server[i].naddrs; j++) {
            for (k = 0; k < ((MMC_CONSISTENT_POINTS * server[i].weight) / server[i].naddrs); k++) {
                ngx_snprintf(hash_data, 28, "%V-%ui", &server[i].addrs[j].name, k);
                continuum->nodes[continuum->nnodes].sockaddr = server[i].addrs[j].sockaddr;
                continuum->nodes[continuum->nnodes].socklen = server[i].addrs[j].socklen;
                continuum->nodes[continuum->nnodes].name = server[i].addrs[j].name;
                continuum->nodes[continuum->nnodes].name.data[server[i].addrs[j].name.len] = 0;
                continuum->nodes[continuum->nnodes].point = ngx_crc32_long(hash_data, ngx_strlen(hash_data));
                continuum->nnodes++;
            }
        }
    }

    qsort(continuum->nodes, continuum->nnodes, 
            sizeof(ngx_http_upstream_consistent_hash_node), 
            (const void*) ngx_http_upstream_consistent_hash_compare_continuum_nodes);

    for (i = 0; i < MMC_CONSISTENT_BUCKETS; i++) {
        buckets->buckets[i] = 
            ngx_http_upstream_consistent_hash_find(continuum, step * i);
    }

#if (CONSISTENT_DEBUG)
    ngx_http_upstream_consistent_hash_print_continuum(cf, continuum);
    ngx_http_upstream_consistent_hash_print_buckets(cf, buckets);
#endif

    buckets->continuum = continuum;
    us->peer.data = buckets;

    return NGX_OK;
}