void *
ngx_pool_get_ctx(ngx_pool_t *pool, ngx_uint_t index)
{
    ngx_uint_t               hash;
    uint32_t                 key;
    ngx_pool_context_node_t *node;

    hash = (ngx_uint_t) pool + index;
    key = ngx_murmur_hash2((u_char *)&hash,
            sizeof(hash)) % ngx_pool_context_hash_size;

    node = ngx_pool_context_hash[key];

    while (node) {

        if (node->pool == pool && node->index == index) {

            return node->data;
        }
        node = node->next;
    }

    return NULL;

}
static ngx_int_t
ngx_http_split_clients_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data)
{
    ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data;

    uint32_t                        hash;
    ngx_str_t                       val;
    ngx_uint_t                      i;
    ngx_http_split_clients_part_t  *part;

    *v = ngx_http_variable_null_value;

    if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {
        return NGX_OK;
    }

    hash = ngx_murmur_hash2(val.data, val.len);

    part = ctx->parts.elts;

    for (i = 0; i < ctx->parts.nelts; i++) {

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http split: %uD %uD", hash, part[i].percent);

        if (hash < part[i].percent || part[i].percent == 0) {
            *v = part[i].value;
            return NGX_OK;
        }
    }

    return NGX_OK;
}
ngx_int_t
ngx_pool_set_ctx(ngx_pool_t *pool, ngx_uint_t index, void *data)
{
    ngx_uint_t              hash;
    uint32_t                key;
    ngx_pool_context_node_t *node;
    ngx_pool_cleanup_t     *cln;

    hash = (ngx_uint_t) pool + index;
    key = ngx_murmur_hash2((u_char *)&hash,
            sizeof(hash)) % ngx_pool_context_hash_size;

    node = ngx_pool_context_hash[key];

    while (node) {

        if (node->pool == pool
                && node->index == index) {


            node->data = data;
            return NGX_OK;
        }
        node = node->next;
    }

    cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_context_node_t));

    if (cln == NULL) {

        return NGX_ERROR;
    }

    cln->handler = ngx_pool_context_cleanup;
    node = cln->data;

    node->prev = NULL;
    node->next = NULL;
    node->pool = pool;
    node->index = index;
    node->data = data;

    ngx_pool_context_link(&ngx_pool_context_hash[key], node);

    return NGX_OK;
}
ngx_http_tfs_rcs_info_t *
ngx_http_tfs_rcs_lookup(ngx_http_tfs_rc_ctx_t *ctx,
                        ngx_str_t appkey)
{
    ngx_int_t                 rc;
    ngx_uint_t                hash;
    ngx_rbtree_node_t        *node, *sentinel;
    ngx_http_tfs_rcs_info_t  *tr;

    node = ctx->sh->rbtree.root;
    sentinel = ctx->sh->rbtree.sentinel;

    hash = ngx_murmur_hash2(appkey.data, appkey.len);

    while (node != sentinel) {

        if (hash < node->key) {
            node = node->left;
            continue;
        }

        if (hash > node->key) {
            node = node->right;
            continue;
        }

        /* hash == node->key */

        tr = (ngx_http_tfs_rcs_info_t *) &node->color;
        rc = ngx_memn2cmp(appkey.data, tr->appkey.data, appkey.len,
                          tr->appkey.len);

        if (rc == 0) {
            ngx_queue_remove(&tr->queue);
            ngx_queue_insert_head(&ctx->sh->queue, &tr->queue);

            return tr;
        }

        node = (rc < 0) ? node->left : node->right;
    }

    return NULL;
}
static ngx_int_t
ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us)
{
    ngx_str_t                            hash_value;
    ngx_http_upstream_chash_srv_conf_t  *ucscf;
    ngx_http_upstream_chash_peer_data_t *uchpd;

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

    uchpd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_chash_peer_data_t));
    if (uchpd == NULL) {
        return NGX_ERROR;
    }

    uchpd->ucscf = ucscf;
    if (ngx_http_script_run(r, &hash_value,
                ucscf->lengths->elts, 0, ucscf->values->elts) == NULL) {
        return NGX_ERROR;
    }

    uchpd->hash = ngx_murmur_hash2(hash_value.data, hash_value.len);

    r->upstream->peer.get = ngx_http_upstream_get_chash_peer;
    r->upstream->peer.free = ngx_http_upstream_free_chash_peer;
    r->upstream->peer.data = uchpd;

#if (NGX_HTTP_SSL)
    r->upstream->peer.set_session = ngx_http_upstream_chash_set_peer_session;
    r->upstream->peer.save_session = ngx_http_upstream_chash_save_peer_session;
#endif

    return NGX_OK;
}
ngx_http_reqstat_rbnode_t *
ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val)
{
    size_t                        size, len;
    uint32_t                      hash;
    ngx_int_t                     rc, excess;
    ngx_time_t                   *tp;
    ngx_msec_t                    now;
    ngx_queue_t                  *q;
    ngx_msec_int_t                ms;
    ngx_rbtree_node_t            *node, *sentinel;
    ngx_http_reqstat_ctx_t       *ctx;
    ngx_http_reqstat_rbnode_t    *rs;

    ctx = shm_zone->data;

    hash = ngx_murmur_hash2(val->data, val->len);

    node = ctx->sh->rbtree.root;
    sentinel = ctx->sh->rbtree.sentinel;

    tp = ngx_timeofday();
    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);

    ngx_shmtx_lock(&ctx->shpool->mutex);

    while (node != sentinel) {

        if (hash < node->key) {
            node = node->left;
            continue;
        }

        if (hash > node->key) {
            node = node->right;
            continue;
        }

        /* hash == node->key */

        rs = (ngx_http_reqstat_rbnode_t *) &node->color;

        /* len < node->len */

        if (val->len < (size_t) rs->len) {
            node = node->left;
            continue;
        }

        rc = ngx_strncmp(val->data, rs->data, (size_t) rs->len);

        if (rc == 0) {

            ms = (ngx_msec_int_t) (now - rs->last_visit);

            rs->excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000
                       + 1000;
            rs->last_visit = now;

            if (rs->excess > 0) {
                ngx_queue_remove(&rs->visit);
                ngx_queue_insert_head(&ctx->sh->visit, &rs->visit);
            }

            ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0,
                           "reqstat lookup exist: %*s", rs->len, rs->data);

            ngx_shmtx_unlock(&ctx->shpool->mutex);

            return rs;
        }

        node = (rc < 0) ? node->left : node->right;
    }

    rc = 0;
    node = NULL;

    size = offsetof(ngx_rbtree_node_t, color)
         + offsetof(ngx_http_reqstat_rbnode_t, data)
         + ctx->key_len;

    if (ctx->alloc_already_fail == 0) {
        node = ngx_slab_alloc_locked(ctx->shpool, size);
        if (node == NULL) {
            ctx->alloc_already_fail = 1;
        }
    }

    if (node == NULL) {

        /* try to free a vacant node */
        q = ngx_queue_last(&ctx->sh->visit);
        rs = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, visit);

        ms = (ngx_msec_int_t) (now - rs->last_visit);

        excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000;

        ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0,
                       "reqstat lookup try recycle: %*s, %d", rs->len, rs->data, excess);

        if (excess < 0) {

            rc = 1;

            node = (ngx_rbtree_node_t *)
                            ((char *) rs - offsetof(ngx_rbtree_node_t, color));
            ngx_rbtree_delete(&ctx->sh->rbtree, node);
            ngx_queue_remove(&rs->visit);

            ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0,
                           "reqstat lookup recycle: %*s", rs->len, rs->data);

            rs->conn_total = 0;

            ngx_memzero((void *) &rs->bytes_in, size - offsetof(ngx_rbtree_node_t, color)
                                            - offsetof(ngx_http_reqstat_rbnode_t, bytes_in));

        } else {
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            return NULL;
        }
    }

    node->key = hash;

    rs = (ngx_http_reqstat_rbnode_t *) &node->color;

    len = ngx_min(ctx->key_len, (ssize_t) val->len);
    ngx_memcpy(rs->data, val->data, len);
    rs->len = len;

    ngx_rbtree_insert(&ctx->sh->rbtree, node);
    ngx_queue_insert_head(&ctx->sh->visit, &rs->visit);
    if (!rc) {
        ngx_queue_insert_head(&ctx->sh->queue, &rs->queue);
    }

    rs->last_visit = now;
    rs->excess = 1000;

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup build: %*s", rs->len, rs->data);

    ngx_shmtx_unlock(&ctx->shpool->mutex);

    return rs;
}
ngx_int_t
ngx_http_connection_pool_get(ngx_peer_connection_t *pc, void *data)
{
    u_char                            pc_addr[32] = {'\0'};
    ngx_uint_t                        bucket_id, hash;
    ngx_queue_t                      *q, *cache, *free;
    ngx_connection_t                 *c;
    ngx_http_connection_pool_t       *p;
    ngx_http_connection_pool_elt_t   *item;

    p = data;

#if (NGX_DEBUG)
    p->count--;
#endif

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                   "get keepalive peer");

    p->failed = 0;

    hash = ngx_murmur_hash2((u_char *) pc->sockaddr, pc->socklen);
    bucket_id = hash % p->bucket_count;

    cache = &p->cache[bucket_id];
    free = &p->free[bucket_id];

    ngx_sprintf(pc_addr, "%s:%d",
                inet_ntoa(((struct sockaddr_in*)(pc->sockaddr))->sin_addr),
                ntohs(((struct sockaddr_in*)(pc->sockaddr))->sin_port));

    for (q = ngx_queue_head(cache);
         q != ngx_queue_sentinel(cache);
         q = ngx_queue_next(q))
    {
        item = ngx_queue_data(q, ngx_http_connection_pool_elt_t, queue);
        c = item->connection;

        if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,
                         item->socklen, pc->socklen)
            == 0)
        {
            ngx_queue_remove(q);
            ngx_queue_insert_head(free, q);

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                           "get keepalive peer: using connection %p", c);

            c->idle = 0;
            c->log = pc->log;
            c->read->log = pc->log;
            c->write->log = pc->log;
            c->pool->log = pc->log;

            pc->connection = c;
            pc->cached = 1;

            item->free = free;
            return NGX_DONE;
        }
    }

    return NGX_OK;
}
void
ngx_http_connection_pool_free(ngx_peer_connection_t *pc,
    void *data, ngx_uint_t state)
{
    ngx_http_connection_pool_t        *p = data;
    ngx_http_connection_pool_elt_t    *item;

    ngx_uint_t                         hash, bucket_id;
    ngx_queue_t                       *q, *cache, *free;
    ngx_connection_t                  *c;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                   "free keepalive peer");

    /* remember failed state - peer.free() may be called more than once */

    if (state & NGX_PEER_FAILED) {
        p->failed = 1;
    }

    /* cache valid connections */

    c = pc->connection;

    if (p->failed
        || c == NULL
        || c->read->eof
        || c->read->error
        || c->read->timedout
        || c->write->error
        || c->write->timedout)
    {
        return;
    }

    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
        return;
    }

#if (NGX_DEBUG)
    p->count++;
#endif
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                   "free keepalive peer: saving connection %p", c);

    hash = ngx_murmur_hash2((u_char *) pc->sockaddr, pc->socklen);
    bucket_id = hash % p->bucket_count;

    cache = &p->cache[bucket_id];
    free = &p->free[bucket_id];

    if (ngx_queue_empty(free)) {
        q = ngx_queue_last(cache);
        ngx_queue_remove(q);

        item = ngx_queue_data(q, ngx_http_connection_pool_elt_t, queue);

        ngx_http_connection_pool_close(item->connection);

    } else {
        q = ngx_queue_head(free);
        ngx_queue_remove(q);

        item = ngx_queue_data(q, ngx_http_connection_pool_elt_t, queue);
    }

    item->connection = c;
    item->free = free;
    ngx_queue_insert_head(cache, q);

    pc->connection = NULL;

    if (c->read->timer_set) {
        ngx_del_timer(c->read);
    }

    if (c->write->timer_set) {
        ngx_del_timer(c->write);
    }

    c->write->handler = ngx_http_connection_pool_dummy_handler;
    c->read->handler = ngx_http_connection_pool_close_handler;

    c->data = item;
    c->idle = 1;
    c->log = ngx_cycle->log;
    c->read->log = ngx_cycle->log;
    c->write->log = ngx_cycle->log;
    c->pool->log = ngx_cycle->log;

    item->socklen = pc->socklen;
    ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);

    if (c->read->ready) {
        ngx_http_connection_pool_close_handler(c->read);
    }
}
static ngx_int_t
ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
{
    u_char                               hash_buf[256];
    ngx_int_t                            j, weight;
    ngx_uint_t                           sid, id, hash_len;
    ngx_uint_t                           i, n, *number, rnindex;
    ngx_http_upstream_rr_peer_t         *peer;
    ngx_http_upstream_rr_peers_t        *peers;
    ngx_http_upstream_chash_server_t    *server;
    ngx_http_upstream_chash_srv_conf_t  *ucscf;

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

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

    us->peer.init = ngx_http_upstream_init_chash_peer;

    peers = (ngx_http_upstream_rr_peers_t *) us->peer.data;
    if (peers == NULL) {
        return NGX_ERROR;
    }

    n = peers->number;
    ucscf->number = 0;
    ucscf->real_node = ngx_pcalloc(cf->pool, n *
                                   sizeof(ngx_http_upstream_chash_server_t**));
    if (ucscf->real_node == NULL) {
        return NGX_ERROR;
    }
    for (i = 0; i < n; i++) {
        ucscf->number += peers->peer[i].weight * NGX_CHASH_VIRTUAL_NODE_NUMBER;
        ucscf->real_node[i] = ngx_pcalloc(cf->pool,
                                    (peers->peer[i].weight
                                     * NGX_CHASH_VIRTUAL_NODE_NUMBER + 1) *
                                     sizeof(ngx_http_upstream_chash_server_t*));
        if (ucscf->real_node[i] == NULL) {
            return NGX_ERROR;
        }
    }

    ucscf->servers = ngx_pcalloc(cf->pool,
                                 (ucscf->number + 1) *
                                  sizeof(ngx_http_upstream_chash_server_t));

    if (ucscf->servers == NULL) {
        return NGX_ERROR;
    }

    ucscf->d_servers = ngx_pcalloc(cf->pool,
                                (ucscf->number + 1) *
                                sizeof(ngx_http_upstream_chash_down_server_t));

    if (ucscf->d_servers == NULL) {
        return NGX_ERROR;
    }

    ucscf->number = 0;
    for (i = 0; i < n; i++) {

        peer = &peers->peer[i];
        sid = (ngx_uint_t) ngx_atoi(peer->id.data, peer->id.len);

        if (sid == (ngx_uint_t) NGX_ERROR || sid > 65535) {

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0, "server id %d", sid);

            ngx_snprintf(hash_buf, 256, "%V%Z", &peer->name);
            hash_len = ngx_strlen(hash_buf);
            sid = ngx_murmur_hash2(hash_buf, hash_len);
        }

        weight = peer->weight * NGX_CHASH_VIRTUAL_NODE_NUMBER;

        if (weight >= 1 << 14) {
            ngx_log_error(NGX_LOG_WARN, cf->log, 0,
                          "weigth[%d] is too large, is must be less than %d",
                          weight / NGX_CHASH_VIRTUAL_NODE_NUMBER,
                          (1 << 14) / NGX_CHASH_VIRTUAL_NODE_NUMBER);
            weight = 1 << 14;
        }

        for (j = 0; j < weight; j++) {
            server = &ucscf->servers[++ucscf->number];
            server->peer = peer;
            server->rnindex = i;

            id = sid * 256 * 16 + j;
            server->hash = ngx_murmur_hash2((u_char *) (&id), 4);
        }
    }

    ngx_qsort(ucscf->servers + 1, ucscf->number,
              sizeof(ngx_http_upstream_chash_server_t),
              (const void *)ngx_http_upstream_chash_cmp);

    number = ngx_calloc(n * sizeof(ngx_uint_t), cf->log);
    if (number == NULL) {
        return NGX_ERROR;
    }

    for (i = 1; i <= ucscf->number; i++) {
        ucscf->servers[i].index = i;
        ucscf->d_servers[i].id = i;
        rnindex = ucscf->servers[i].rnindex;
        ucscf->real_node[rnindex][number[rnindex]] = &ucscf->servers[i];
        number[rnindex]++;
    }

    ngx_free(number);

    ucscf->tree = ngx_pcalloc(cf->pool, sizeof(ngx_segment_tree_t));
    if (ucscf->tree == NULL) {
        return NGX_ERROR;
    }

    ngx_segment_tree_init(ucscf->tree, ucscf->number, cf->pool);
    ucscf->tree->build(ucscf->tree, 1, 1, ucscf->number);

    ngx_queue_init(&ucscf->down_servers);

    return NGX_OK;
}
static ngx_int_t
ngx_http_tfs_create_info_node(ngx_http_tfs_t *t, ngx_http_tfs_rc_ctx_t *rc_ctx,
    u_char *data, ngx_str_t appkey)
{
    u_char                                   *p;
    size_t                                    n;
    uint32_t                                  len;
    ngx_int_t                                 rc;
    ngx_rbtree_node_t                        *node;
    ngx_http_tfs_rcs_info_t                  *rc_info_node;

    rc_info_node = NULL;

    n = offsetof(ngx_rbtree_node_t, color)
        + sizeof(ngx_http_tfs_rcs_info_t);

    node = ngx_slab_alloc_locked(rc_ctx->shpool, n);
    if (node == NULL) {
        ngx_http_tfs_expire_and_alloc(node, n);
    }

    rc_info_node = (ngx_http_tfs_rcs_info_t *) &node->color;

    node->key = ngx_murmur_hash2(appkey.data, appkey.len);

    rc_info_node->appkey.data = ngx_slab_alloc_locked(rc_ctx->shpool, appkey.len);
    if (rc_info_node->appkey.data == NULL) {
        ngx_http_tfs_rc_server_expire(rc_ctx);
        rc_info_node->appkey.data = ngx_slab_alloc_locked(rc_ctx->shpool, appkey.len);
        if (rc_info_node->appkey.data == NULL) {
            goto login_error;
        }
    }

    ngx_memcpy(rc_info_node->appkey.data, appkey.data, appkey.len);
    rc_info_node->appkey.len = appkey.len;

    /* parse session id */
    len = *((uint32_t *) data);
    p = data + sizeof(uint32_t);
    if (len <= 0) {
        rc_info_node->session_id.len = 0;
        goto login_error;
    }

    rc_info_node->session_id.len = len - 1;
    rc_info_node->session_id.data = ngx_slab_alloc_locked(rc_ctx->shpool, len);
    if (rc_info_node->session_id.data == NULL) {
        ngx_http_tfs_rc_server_expire(rc_ctx);
        rc_info_node->session_id.data = ngx_slab_alloc_locked(rc_ctx->shpool,
                                                              rc_info_node->session_id.len);
        if (rc_info_node->session_id.data == NULL) {
            goto login_error;
        }
    }

    ngx_memcpy(rc_info_node->session_id.data, p, rc_info_node->session_id.len);

    p += rc_info_node->session_id.len + 1;

    /* parse rc info */
    rc = ngx_http_tfs_parse_rc_info(rc_info_node, rc_ctx, p);
    if (rc == NGX_ERROR) {
        goto login_error;
    }

    t->rc_info_node = rc_info_node;
    ngx_rbtree_insert(&rc_ctx->sh->rbtree, node);
    ngx_queue_insert_head(&rc_ctx->sh->queue, &rc_info_node->queue);
    ngx_queue_insert_tail(&rc_ctx->sh->kp_queue, &rc_info_node->kp_queue);

    return NGX_OK;

login_error:

    ngx_http_tfs_rc_server_destroy_node(rc_ctx, rc_info_node);
    t->rc_info_node = NULL;
    return NGX_ERROR;
}
static ngx_int_t
ngx_http_tfs_parse_rc_info(ngx_http_tfs_rcs_info_t *rc_info_node,
    ngx_http_tfs_rc_ctx_t *rc_ctx,  u_char *data)
{
    u_char                                   *p;
    uint8_t                                   is_master;
    uint32_t                                  cluster_id, cluster_id_len, len;
    ngx_int_t                                 dup_info_size, rc;
    ngx_uint_t                                i, j;
    ngx_http_tfs_group_info_t                *group_info;
    ngx_http_tfs_logical_cluster_t           *logical_cluster;
    ngx_http_tfs_physical_cluster_t          *physical_cluster;
    ngx_http_tfs_cluster_group_info_t        *cluster_group_info, *cluster_group_info2;
    ngx_http_tfs_tair_server_addr_info_t     *dup_server_info;

    p = data;

    /* rc servers count */
    rc_info_node->rc_servers_count = *((uint32_t *) p);
    p += sizeof(uint32_t);

    if (rc_info_node->rc_servers_count > 0) {
        rc_info_node->rc_servers = ngx_slab_alloc_locked(rc_ctx->shpool,
                                                         rc_info_node->rc_servers_count * sizeof(uint64_t));
        if (rc_info_node->rc_servers == NULL) {
            ngx_http_tfs_expire_and_alloc(rc_info_node->rc_servers,
                                          rc_info_node->rc_servers_count * sizeof(uint64_t));
        }

        ngx_memcpy(rc_info_node->rc_servers, p,
                   rc_info_node->rc_servers_count * sizeof(uint64_t));
        p += sizeof(uint64_t) * rc_info_node->rc_servers_count;
    }

    /* logical cluster count */
    rc_info_node->logical_cluster_count = *((uint32_t *) p);
    p += sizeof(uint32_t);

    logical_cluster = rc_info_node->logical_clusters;
    for (i = 0; i < rc_info_node->logical_cluster_count; i++) {
        logical_cluster->need_duplicate = *p;
        p += sizeof(uint8_t);

        if (logical_cluster->need_duplicate) {
            len = *((uint32_t *) p);
            p += sizeof(uint32_t);

            if (len > 0) {
                dup_info_size = len - 1;
                dup_server_info = &logical_cluster->dup_server_info;

                rc = ngx_http_tfs_parse_tair_server_addr_info(dup_server_info, p,
                                                              dup_info_size, rc_ctx->shpool, 1);
                if (rc == NGX_ERROR) {
                    return NGX_ERROR;
                }

                logical_cluster->dup_server_addr_hash = ngx_murmur_hash2(p, dup_info_size);
                p += dup_info_size + 1;

                rc_info_node->need_duplicate = 1;
            }
        }

        logical_cluster->rw_cluster_count = *((uint32_t *) p);
        p += sizeof(uint32_t);

        physical_cluster = logical_cluster->rw_clusters;
        for (j = 0; j < logical_cluster->rw_cluster_count; j++) {
            /* cluster stat */
            physical_cluster->cluster_stat = *((uint32_t *) p);
            p += sizeof(uint32_t);

            /* access type */
            physical_cluster->access_type = *((uint32_t *) p);
            p += sizeof(uint32_t);

            /* cluster id */
            len = *((uint32_t *) p);
            if (len <= 0) {
                physical_cluster->cluster_id_text.len = 0;
                return NGX_ERROR;
            }

            physical_cluster->cluster_id_text.len = len - 1;
            p += sizeof(uint32_t);

            physical_cluster->cluster_id_text.data = ngx_slab_alloc_locked(rc_ctx->shpool,
                                                   physical_cluster->cluster_id_text.len);
            if (physical_cluster->cluster_id_text.data == NULL) {
                ngx_http_tfs_expire_and_alloc(physical_cluster->cluster_id_text.data,
                                              physical_cluster->cluster_id_text.len);
            }
            ngx_memcpy(physical_cluster->cluster_id_text.data, p, physical_cluster->cluster_id_text.len);
            /* this cluster id need get from ns */
            physical_cluster->cluster_id = 0;
            p += physical_cluster->cluster_id_text.len + 1;

            /* name server vip */
            len = *((uint32_t *) p);
            if (len <= 0) {
                physical_cluster->ns_vip_text.len = 0;
                return NGX_ERROR;
            }

            physical_cluster->ns_vip_text.len = len - 1;
            p += sizeof(uint32_t);

            physical_cluster->ns_vip_text.data = ngx_slab_alloc_locked(rc_ctx->shpool,
                                                                       physical_cluster->ns_vip_text.len);
            if (physical_cluster->ns_vip_text.data == NULL) {
                ngx_http_tfs_expire_and_alloc(physical_cluster->ns_vip_text.data,
                                              physical_cluster->ns_vip_text.len);
            }
            ngx_memcpy(physical_cluster->ns_vip_text.data, p, physical_cluster->ns_vip_text.len);

            p += physical_cluster->ns_vip_text.len + 1;

            ngx_http_tfs_parse_inet(&physical_cluster->ns_vip_text, &physical_cluster->ns_vip);

            physical_cluster++;
        }

        logical_cluster++;
    }

    /* report interval */
    rc_info_node->report_interval = *((uint32_t *) p);
    p += sizeof(uint32_t);

    /* modify time */
    rc_info_node->modify_time = *((uint64_t *) p);
    p += sizeof(uint64_t);

    /* root server */
    rc_info_node->meta_root_server = *((uint64_t *) p);
    p += sizeof(uint64_t);

    /* remote block cache */
    len = *((uint32_t *) p);
    p += sizeof(uint32_t);
    rc_info_node->remote_block_cache_info.len = 0;

    if (len > 0) {
        rc_info_node->remote_block_cache_info.len = len - 1;

        rc_info_node->remote_block_cache_info.data = ngx_slab_alloc_locked(rc_ctx->shpool,
                                    rc_info_node->remote_block_cache_info.len);
        if (rc_info_node->remote_block_cache_info.data == NULL) {
            ngx_http_tfs_expire_and_alloc(rc_info_node->remote_block_cache_info.data,
                                          rc_info_node->remote_block_cache_info.len);
        }

        ngx_memcpy(rc_info_node->remote_block_cache_info.data, p, len - 1);
        p += len;
    }

    /* unlink & update cluster */
    rc_info_node->unlink_cluster_count = *((uint32_t *) p);
    p += sizeof(uint32_t);

    cluster_group_info = rc_info_node->unlink_clusters;

    for (i = 0; i < rc_info_node->unlink_cluster_count; i++) {
        /* skip cluster_stat */
        p += sizeof(uint32_t);
        /* skip access type */
        p += sizeof(uint32_t);

        cluster_id_len = *((uint32_t *) p);
        p += sizeof(uint32_t);

        cluster_id = ngx_http_tfs_get_cluster_id(p);
        is_master = ngx_http_tfs_cluster_is_master(p);
        p += cluster_id_len;

        for (j = 0; j < i; j++) {
            cluster_group_info2 = &rc_info_node->unlink_clusters[j];
            if (cluster_group_info2->cluster_id == cluster_id) {
                break;
            }
        }

        if (j >= i) {
            group_info = &cluster_group_info[i].group_info[0];
            cluster_group_info[i].info_count = 1;
            cluster_group_info[i].group_count = 0;
            cluster_group_info[i].cluster_id = cluster_id;
            group_info->is_master = is_master;

        } else {
            group_info = &cluster_group_info2->group_info[cluster_group_info2->info_count++];
            group_info->is_master = is_master;
        }

        /* name server vip */
        len = *((uint32_t *) p);
        if (len <= 0) {
            group_info->ns_vip_text.len = 0;
            return NGX_ERROR;
        }

        group_info->ns_vip_text.len = len - 1;
        p += sizeof(uint32_t);

        group_info->ns_vip_text.data = ngx_slab_alloc_locked(rc_ctx->shpool,
                                                             group_info->ns_vip_text.len);
        if (group_info->ns_vip_text.data == NULL) {
            ngx_http_tfs_expire_and_alloc(group_info->ns_vip_text.data,
                                          group_info->ns_vip_text.len);
        }

        memcpy(group_info->ns_vip_text.data, p, group_info->ns_vip_text.len);

        group_info->group_seq = -1;
        p += len;

        ngx_http_tfs_parse_inet(&group_info->ns_vip_text, &group_info->ns_vip);
    }

    /* use remote cache flag */
    rc_info_node->use_remote_block_cache = *((uint32_t *) p);
    return NGX_OK;
}
ngx_int_t
ngx_http_tfs_get_remote_block_cache_instance(
    ngx_http_tfs_remote_block_cache_ctx_t *ctx,
    ngx_str_t *server_addr)
{
    size_t                                server_addr_len;
    uint32_t                              server_addr_hash;
    ngx_int_t                             rc, i;
    ngx_str_t                            *st, *group_name;
    ngx_array_t                           config_server;
    ngx_http_tfs_t                       *t;
    ngx_http_tfs_tair_instance_t         *instance;
    ngx_http_tfs_tair_server_addr_info_t  server_addr_info;

    if (server_addr->len == 0
        || server_addr->data == NULL)
    {
        return NGX_ERROR;
    }

    t = ctx->data;
    server_addr_len = server_addr->len;
    server_addr_hash = ngx_murmur_hash2(server_addr->data, server_addr_len);

    instance = ctx->tair_instance;
    if (instance->server != NULL) {
        if (instance->server_addr_hash == server_addr_hash) {
            return NGX_OK;
        }

        ngx_http_etair_destory_server(instance->server,
                                      (ngx_cycle_t *) ngx_cycle);
        instance->server = NULL;
    }

    rc = ngx_http_tfs_parse_tair_server_addr_info(&server_addr_info,
                                                  server_addr->data,
                                                  server_addr_len,
                                                  t->pool, 0);
    if (rc == NGX_ERROR) {
        return NGX_ERROR;
    }

    rc = ngx_array_init(&config_server, t->pool,
                        NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT,
                        sizeof(ngx_str_t));
    if (rc == NGX_ERROR) {
        return NGX_ERROR;
    }

    for (i = 0; i < NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT; i++) {
        if (server_addr_info.server[i].len > 0 ) {
            st = (ngx_str_t *) ngx_array_push(&config_server);
            *st = server_addr_info.server[i];
        }
    }

    group_name = &server_addr_info.server[NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT];
    instance->server = ngx_http_etair_create_server(group_name,
                                                    &config_server,
                                                    t->main_conf->tair_timeout,
                                                    (ngx_cycle_t *) ngx_cycle);
    if (instance->server == NULL) {
        return NGX_ERROR;
    }
    instance->server_addr_hash = server_addr_hash;
    instance->area = server_addr_info.area;

    return NGX_OK;
}