diff --git a/src/ngx_http_lua_shdict.c b/src/ngx_http_lua_shdict.c index b300673e82..f4f12ce451 100644 --- a/src/ngx_http_lua_shdict.c +++ b/src/ngx_http_lua_shdict.c @@ -41,7 +41,12 @@ static int ngx_http_lua_shdict_lpop(lua_State *L); static int ngx_http_lua_shdict_rpop(lua_State *L); static int ngx_http_lua_shdict_pop_helper(lua_State *L, int flags); static int ngx_http_lua_shdict_llen(lua_State *L); - +static int ngx_http_lua_shdict_zset(lua_State *L); +static int ngx_http_lua_shdict_zrem(lua_State *L); +static int ngx_http_lua_shdict_zgetall(lua_State *L); +static int ngx_http_lua_shdict_zget(lua_State *L); +static int ngx_http_lua_shdict_zcard(lua_State *L); +static int ngx_http_lua_shdict_zscan(lua_State *L); static ngx_inline ngx_shm_zone_t *ngx_http_lua_shdict_get_zone(lua_State *L, int index); @@ -67,6 +72,7 @@ enum { SHDICT_TNUMBER = 3, /* same as LUA_TNUMBER */ SHDICT_TSTRING = 4, /* same as LUA_TSTRING */ SHDICT_TLIST = 5, + SHDICT_TZSET = 6 }; @@ -77,6 +83,136 @@ ngx_http_lua_shdict_get_list_head(ngx_http_lua_shdict_node_t *sd, size_t len) NGX_ALIGNMENT); } +static ngx_inline void +ngx_http_lua_shdict_free_list(ngx_http_lua_shdict_ctx_t *ctx, + ngx_http_lua_shdict_node_t *sd) +{ + ngx_queue_t *queue, *q; + u_char *p; + + queue = ngx_http_lua_shdict_get_list_head(sd, sd->key_len); + + for (q = ngx_queue_head(queue); + q != ngx_queue_sentinel(queue); + q = ngx_queue_next(q)) + { + p = (u_char *) ngx_queue_data(q, + ngx_http_lua_shdict_list_node_t, + queue); + + ngx_slab_free_locked(ctx->shpool, p); + } +} + + +static ngx_inline ngx_http_lua_shdict_zset_t * +ngx_http_lua_shdict_get_rbtree(ngx_http_lua_shdict_node_t *sd, size_t len) +{ + return (ngx_http_lua_shdict_zset_t *) + ngx_align_ptr(((u_char *) &sd->data + len), NGX_ALIGNMENT); +} + +static ngx_inline void +ngx_http_lua_shdict_push_znode_value(lua_State *L, + ngx_http_lua_shdict_zset_node_t *zset_node) +{ + double num; + u_char c; + + if (zset_node->value.data) { + + switch (zset_node->value_type) { + + case SHDICT_TSTRING: + + lua_pushlstring(L, (char *) zset_node->value.data, + zset_node->value.len); + break; + + case SHDICT_TNUMBER: + + ngx_memcpy(&num, zset_node->value.data, sizeof(double)); + + lua_pushnumber(L, num); + break; + + case SHDICT_TBOOLEAN: + + c = *zset_node->value.data; + + lua_pushboolean(L, c ? 1 : 0); + break; + } + } else { + + lua_pushlightuserdata(L, NULL); + } +} + + +void +ngx_http_lua_shdict_zset_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_http_lua_shdict_zset_node_t *sdn, *sdnt; + + for ( ;; ) { + + sdn = (ngx_http_lua_shdict_zset_node_t *) &node->color; + sdnt = (ngx_http_lua_shdict_zset_node_t *) &temp->color; + + p = ngx_strcmp(sdn->data, sdnt->data) < 0 ? &temp->left : &temp->right; + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +static ngx_inline void +ngx_http_lua_shdict_free_rbtree(ngx_http_lua_shdict_ctx_t *ctx, + ngx_http_lua_shdict_node_t *sd) +{ + ngx_rbtree_node_t *node; + ngx_rbtree_node_t *tmp; + ngx_rbtree_node_t *sentinel; + ngx_http_lua_shdict_zset_t *zset; + ngx_http_lua_shdict_zset_node_t *zset_node; + + zset = ngx_http_lua_shdict_get_rbtree(sd, sd->key_len); + + node = zset->rbtree.root; + sentinel = zset->rbtree.sentinel; + + if (node != sentinel) { + + for (node = ngx_rbtree_min(node, sentinel); + node; + ngx_slab_free_locked(ctx->shpool, tmp)) + { + zset_node = (ngx_http_lua_shdict_zset_node_t *) &node->color; + + if (zset_node->value.data) { + ngx_slab_free_locked(ctx->shpool, zset_node->value.data); + } + + tmp = node; + + node = ngx_rbtree_next(&zset->rbtree, node); + } + } +} + ngx_int_t ngx_http_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data) @@ -250,12 +386,11 @@ ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, ngx_uint_t n) { ngx_time_t *tp; uint64_t now; - ngx_queue_t *q, *list_queue, *lq; + ngx_queue_t *q; int64_t ms; ngx_rbtree_node_t *node; ngx_http_lua_shdict_node_t *sd; int freed = 0; - ngx_http_lua_shdict_list_node_t *lnode; tp = ngx_timeofday(); @@ -290,17 +425,11 @@ ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, ngx_uint_t n) } if (sd->value_type == SHDICT_TLIST) { - list_queue = ngx_http_lua_shdict_get_list_head(sd, sd->key_len); - - for (lq = ngx_queue_head(list_queue); - lq != ngx_queue_sentinel(list_queue); - lq = ngx_queue_next(lq)) - { - lnode = ngx_queue_data(lq, ngx_http_lua_shdict_list_node_t, - queue); + ngx_http_lua_shdict_free_list(ctx, sd); + } - ngx_slab_free_locked(ctx->shpool, lnode); - } + if (sd->value_type == SHDICT_TZSET) { + ngx_http_lua_shdict_free_rbtree(ctx, sd); } ngx_queue_remove(q); @@ -330,7 +459,7 @@ ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L) lua_createtable(L, 0, lmcf->shdict_zones->nelts /* nrec */); /* ngx.shared */ - lua_createtable(L, 0 /* narr */, 18 /* nrec */); /* shared mt */ + lua_createtable(L, 0 /* narr */, 24 /* nrec */); /* shared mt */ lua_pushcfunction(L, ngx_http_lua_shdict_get); lua_setfield(L, -2, "get"); @@ -374,6 +503,24 @@ ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L) lua_pushcfunction(L, ngx_http_lua_shdict_llen); lua_setfield(L, -2, "llen"); + lua_pushcfunction(L, ngx_http_lua_shdict_zset); + lua_setfield(L, -2, "zset"); + + lua_pushcfunction(L, ngx_http_lua_shdict_zrem); + lua_setfield(L, -2, "zrem"); + + lua_pushcfunction(L, ngx_http_lua_shdict_zget); + lua_setfield(L, -2, "zget"); + + lua_pushcfunction(L, ngx_http_lua_shdict_zgetall); + lua_setfield(L, -2, "zgetall"); + + lua_pushcfunction(L, ngx_http_lua_shdict_zcard); + lua_setfield(L, -2, "zcard"); + + lua_pushcfunction(L, ngx_http_lua_shdict_zscan); + lua_setfield(L, -2, "zscan"); + lua_pushcfunction(L, ngx_http_lua_shdict_flush_all); lua_setfield(L, -2, "flush_all"); @@ -578,6 +725,14 @@ ngx_http_lua_shdict_get_helper(lua_State *L, int get_stale) lua_pushliteral(L, "value is a list"); return 2; + case SHDICT_TZSET: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value is a zset"); + return 2; + default: ngx_shmtx_unlock(&ctx->shpool->mutex); @@ -678,7 +833,7 @@ ngx_http_lua_shdict_flush_all(lua_State *L) static int ngx_http_lua_shdict_flush_expired(lua_State *L) { - ngx_queue_t *q, *prev, *list_queue, *lq; + ngx_queue_t *q, *prev; ngx_http_lua_shdict_node_t *sd; ngx_http_lua_shdict_ctx_t *ctx; ngx_shm_zone_t *zone; @@ -688,7 +843,6 @@ ngx_http_lua_shdict_flush_expired(lua_State *L) ngx_rbtree_node_t *node; uint64_t now; int n; - ngx_http_lua_shdict_list_node_t *lnode; n = lua_gettop(L); @@ -731,17 +885,11 @@ ngx_http_lua_shdict_flush_expired(lua_State *L) if (sd->expires != 0 && sd->expires <= now) { if (sd->value_type == SHDICT_TLIST) { - list_queue = ngx_http_lua_shdict_get_list_head(sd, sd->key_len); - - for (lq = ngx_queue_head(list_queue); - lq != ngx_queue_sentinel(list_queue); - lq = ngx_queue_next(lq)) - { - lnode = ngx_queue_data(lq, ngx_http_lua_shdict_list_node_t, - queue); + ngx_http_lua_shdict_free_list(ctx, sd); + } - ngx_slab_free_locked(ctx->shpool, lnode); - } + if (sd->value_type == SHDICT_TZSET) { + ngx_http_lua_shdict_free_rbtree(ctx, sd); } ngx_queue_remove(q); @@ -924,7 +1072,6 @@ ngx_http_lua_shdict_set_helper(lua_State *L, int flags) /* indicates whether to foricibly override other * valid entries */ int32_t user_flags = 0; - ngx_queue_t *queue, *q; n = lua_gettop(L); @@ -1073,7 +1220,8 @@ ngx_http_lua_shdict_set_helper(lua_State *L, int flags) if (value.data && value.len == (size_t) sd->value_len - && sd->value_type != SHDICT_TLIST) + && sd->value_type != SHDICT_TLIST + && sd->value_type != SHDICT_TZSET) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, @@ -1120,18 +1268,11 @@ ngx_http_lua_shdict_set_helper(lua_State *L, int flags) remove: if (sd->value_type == SHDICT_TLIST) { - queue = ngx_http_lua_shdict_get_list_head(sd, key.len); - - for (q = ngx_queue_head(queue); - q != ngx_queue_sentinel(queue); - q = ngx_queue_next(q)) - { - p = (u_char *) ngx_queue_data(q, - ngx_http_lua_shdict_list_node_t, - queue); + ngx_http_lua_shdict_free_list(ctx, sd); + } - ngx_slab_free_locked(ctx->shpool, p); - } + if (sd->value_type == SHDICT_TZSET) { + ngx_http_lua_shdict_free_rbtree(ctx, sd); } ngx_queue_remove(&sd->queue); @@ -1264,7 +1405,6 @@ ngx_http_lua_shdict_incr(lua_State *L) /* indicates whether to foricibly override other * valid entries */ int forcible = 0; - ngx_queue_t *queue, *q; n = lua_gettop(L); @@ -1342,7 +1482,8 @@ ngx_http_lua_shdict_incr(lua_State *L) /* found an expired item */ if ((size_t) sd->value_len == sizeof(double) - && sd->value_type != SHDICT_TLIST) + && sd->value_type != SHDICT_TLIST + && sd->value_type != SHDICT_TZSET) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict incr: found old entry and " @@ -1398,18 +1539,11 @@ ngx_http_lua_shdict_incr(lua_State *L) "NOT matched, removing it first"); if (sd->value_type == SHDICT_TLIST) { - queue = ngx_http_lua_shdict_get_list_head(sd, key.len); - - for (q = ngx_queue_head(queue); - q != ngx_queue_sentinel(queue); - q = ngx_queue_next(q)) - { - p = (u_char *) ngx_queue_data(q, - ngx_http_lua_shdict_list_node_t, - queue); + ngx_http_lua_shdict_free_list(ctx, sd); + } - ngx_slab_free_locked(ctx->shpool, p); - } + if (sd->value_type == SHDICT_TZSET) { + ngx_http_lua_shdict_free_rbtree(ctx, sd); } ngx_queue_remove(&sd->queue); @@ -1627,8 +1761,8 @@ ngx_http_lua_shdict_push_helper(lua_State *L, int flags) double num; ngx_rbtree_node_t *node; ngx_shm_zone_t *zone; - ngx_queue_t *queue, *q; ngx_http_lua_shdict_list_node_t *lnode; + ngx_queue_t *queue; n = lua_gettop(L); @@ -1711,6 +1845,10 @@ ngx_http_lua_shdict_push_helper(lua_State *L, int flags) "lua shared dict push: found old entry and value " "type not matched, remove it first"); + if (sd->value_type == SHDICT_TZSET) { + ngx_http_lua_shdict_free_rbtree(ctx, sd); + } + ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) @@ -1732,16 +1870,9 @@ ngx_http_lua_shdict_push_helper(lua_State *L, int flags) /* free list nodes */ - queue = ngx_http_lua_shdict_get_list_head(sd, key.len); + ngx_http_lua_shdict_free_list(ctx, sd); - for (q = ngx_queue_head(queue); - q != ngx_queue_sentinel(queue); - q = ngx_queue_next(q)) - { - /* TODO: reuse matched size list node */ - lnode = ngx_queue_data(q, ngx_http_lua_shdict_list_node_t, queue); - ngx_slab_free_locked(ctx->shpool, lnode); - } + queue = ngx_http_lua_shdict_get_list_head(sd, key.len); ngx_queue_init(queue); @@ -2167,107 +2298,123 @@ ngx_http_lua_shdict_llen(lua_State *L) } -ngx_shm_zone_t * -ngx_http_lua_find_zone(u_char *name_data, size_t name_len) +static int +ngx_http_lua_shdict_zset(lua_State *L) { - ngx_str_t *name; - ngx_uint_t i; + int n; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_http_lua_shdict_ctx_t *ctx; + ngx_http_lua_shdict_node_t *sd; + int value_type = 0; + ngx_str_t value; + ngx_str_t old_value; + double num; + u_char c; + ngx_rbtree_node_t *node = NULL; + ngx_rbtree_node_t *znode = NULL; + ngx_rbtree_node_t *sentinel; ngx_shm_zone_t *zone; - ngx_http_lua_shm_zone_ctx_t *ctx; - volatile ngx_list_part_t *part; + ngx_str_t zkey; + ngx_http_lua_shdict_zset_t *zset; + ngx_http_lua_shdict_zset_node_t *zset_node; + lua_Number exptime = 0; + ngx_time_t *tp; + u_char exists = 0; - part = &ngx_cycle->shared_memory.part; - zone = part->elts; + n = lua_gettop(L); - for (i = 0; /* void */ ; i++) { + if (n < 3 || n > 5) { + return luaL_error(L, "expecting 3, 4 or 5 arguments, " + "but only seen %d", n); + } - if (i >= part->nelts) { - if (part->next == NULL) { - break; - } + if (lua_type(L, 1) != LUA_TTABLE) { + return luaL_error(L, "bad \"zone\" argument"); + } - part = part->next; - zone = part->elts; - i = 0; - } + zone = ngx_http_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad \"zone\" argument"); + } - name = &zone[i].shm.name; + ctx = zone->data; - dd("name: [%.*s] %d", (int) name->len, name->data, (int) name->len); - dd("name2: [%.*s] %d", (int) name_len, name_data, (int) name_len); + if (lua_isnil(L, 2)) { + lua_pushnil(L); + lua_pushliteral(L, "nil key"); + return 2; + } - if (name->len == name_len - && ngx_strncmp(name->data, name_data, name_len) == 0) - { - ctx = (ngx_http_lua_shm_zone_ctx_t *) zone[i].data; - return &ctx->zone; - } + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty key"); + return 2; } - return NULL; -} + if (key.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "key too long"); + return 2; + } + hash = ngx_crc32_short(key.data, key.len); -#ifndef NGX_LUA_NO_FFI_API -int -ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, - size_t key_len, int value_type, u_char *str_value_buf, - size_t str_value_len, double num_value, int exptime, int user_flags, - char **errmsg, int *forcible) -{ - int i, n; - u_char c, *p; - uint32_t hash; - ngx_int_t rc; - ngx_time_t *tp; - ngx_queue_t *queue, *q; - ngx_rbtree_node_t *node; - ngx_http_lua_shdict_ctx_t *ctx; - ngx_http_lua_shdict_node_t *sd; + zkey.data = (u_char *) luaL_checklstring(L, 3, &zkey.len); - if (zone == NULL) { - return NGX_ERROR; + if (zkey.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty zkey"); + return 2; } - dd("exptime: %d", exptime); + if (zkey.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "zkey too long"); + return 2; + } - ctx = zone->data; + value.data = NULL; - *forcible = 0; + if (n > 3) { + value_type = lua_type(L, 4); - hash = ngx_crc32_short(key, key_len); + switch (value_type) { - switch (value_type) { + case SHDICT_TSTRING: + value.data = (u_char *) lua_tolstring(L, 4, &value.len); + break; - case SHDICT_TSTRING: - /* do nothing */ - break; + case SHDICT_TNUMBER: + value.len = sizeof(double); + num = lua_tonumber(L, 4); + value.data = (u_char *) # + break; - case SHDICT_TNUMBER: - dd("num value: %lf", num_value); - str_value_buf = (u_char *) &num_value; - str_value_len = sizeof(double); - break; + case SHDICT_TBOOLEAN: + value.len = sizeof(u_char); + c = lua_toboolean(L, 4) ? 1 : 0; + value.data = &c; + break; - case SHDICT_TBOOLEAN: - c = num_value ? 1 : 0; - str_value_buf = &c; - str_value_len = sizeof(u_char); - break; + case SHDICT_TNIL: + break; - case LUA_TNIL: - if (op & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) { - *errmsg = "attempt to add or replace nil values"; - return NGX_ERROR; + default: + lua_pushnil(L); + lua_pushliteral(L, "bad value type"); + return 2; } - str_value_buf = NULL; - str_value_len = 0; - break; - - default: - *errmsg = "unsupported value type"; - return NGX_ERROR; + if (n == 5) { + exptime = luaL_checknumber(L, 5); + if (exptime < 0) { + return luaL_error(L, "bad \"exptime\" argument"); + } + } } ngx_shmtx_lock(&ctx->shpool->mutex); @@ -2276,24 +2423,1153 @@ ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, ngx_http_lua_shdict_expire(ctx, 1); #endif - rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd); + rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); - dd("lookup returns %d", (int) rc); + dd("shdict lookup returned %d", (int) rc); - if (op & NGX_HTTP_LUA_SHDICT_REPLACE) { + if (rc == NGX_DONE) { - if (rc == NGX_DECLINED || rc == NGX_DONE) { - ngx_shmtx_unlock(&ctx->shpool->mutex); - *errmsg = "not found"; - return NGX_DECLINED; - } + /* exists but expired */ - /* rc == NGX_OK */ + if (sd->value_type != SHDICT_TZSET) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "lua shared dict zset: found old entry and value " + "type not matched, remove it first"); - goto replace; - } + if (sd->value_type == SHDICT_TLIST) { + ngx_http_lua_shdict_free_list(ctx, sd); + } - if (op & NGX_HTTP_LUA_SHDICT_ADD) { + ngx_queue_remove(&sd->queue); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + + dd("go to init_zset"); + goto init_zset; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "lua shared dict zset: found old entry and value " + "type matched, reusing it"); + + /* free rbtree */ + + ngx_http_lua_shdict_free_rbtree(ctx, sd); + + zset = ngx_http_lua_shdict_get_rbtree(sd, key.len); + + ngx_rbtree_init(&zset->rbtree, &zset->sentinel, + ngx_http_lua_shdict_zset_insert_value); + + if (exptime > 0) { + tp = ngx_timeofday(); + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + (uint64_t) (exptime * 1000); + + } else { + sd->expires = 0; + } + + sd->value_len = 0; + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + dd("go to add_node"); + goto add_node; + } + + /* exists and not expired */ + + if (rc == NGX_OK) { + + if (sd->value_type != SHDICT_TZSET) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value not a zset"); + return 2; + } + + zset = ngx_http_lua_shdict_get_rbtree(sd, key.len); + + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + + dd("go to add_node"); + goto add_node; + } + + /* rc == NGX_DECLINED, not found */ + +init_zset: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "lua shared dict zset: creating a new entry"); + + /* NOTICE: we assume the begin point aligned in slab, be careful */ + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_lua_shdict_node_t, data) + + key.len + + sizeof(ngx_http_lua_shdict_zset_t); + + dd("length before aligned: %d", n); + + n = (int) (uintptr_t) ngx_align_ptr(n, NGX_ALIGNMENT); + + dd("length after aligned: %d", n); + + node = ngx_slab_alloc_locked(ctx->shpool, n); + + if (node == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "no memory"); + return 2; + } + + sd = (ngx_http_lua_shdict_node_t *) &node->color; + + zset = ngx_http_lua_shdict_get_rbtree(sd, key.len); + + node->key = hash; + sd->key_len = (u_short) key.len; + + if (exptime > 0) { + tp = ngx_timeofday(); + sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + + (uint64_t) (exptime * 1000); + + } else { + sd->expires = 0; + } + + sd->value_len = 0; + + dd("setting value type to %d", (int) SHDICT_TZSET); + + sd->value_type = (uint8_t) SHDICT_TZSET; + + ngx_memcpy(sd->data, key.data, key.len); + + ngx_rbtree_init(&zset->rbtree, &zset->sentinel, + ngx_http_lua_shdict_zset_insert_value); + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + +add_node: + + /* search first */ + + znode = zset->rbtree.root; + sentinel = zset->rbtree.sentinel; + + while (znode != sentinel) { + + zset_node = (ngx_http_lua_shdict_zset_node_t *) &znode->color; + + rc = ngx_strcmp(zkey.data, zset_node->data); + + if (rc < 0) { + + znode = znode->left; + continue; + + } else if (rc > 0) { + + znode = znode->right; + continue; + + } + + /* found */ + + exists = 1; + break; + } + + if (exists == 0) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "lua shared dict zset: creating a new zset node"); + + /* NOTICE: we assume the begin point aligned in slab, be careful */ + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_lua_shdict_zset_node_t, data) + + zkey.len + 1 /* zero terminated string key */; + + dd("length before aligned: %d", n); + + n = (int) (uintptr_t) ngx_align_ptr(n, NGX_ALIGNMENT); + + dd("length after aligned: %d", n); + + znode = ngx_slab_calloc_locked(ctx->shpool, n); + + if (znode == NULL) { + + if (node != NULL && sd->value_len == 0) { + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_queue_remove(&sd->queue); + + ngx_slab_free_locked(ctx->shpool, node); + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "no memory"); + return 2; + } + } + + zset_node = (ngx_http_lua_shdict_zset_node_t *) &znode->color; + + old_value = zset_node->value; + + if (value.data) { + + dd("setting zset node value length to %d", (int) value.len); + + zset_node->value_type = value_type; + zset_node->value.len = value.len; + zset_node->value.data = ngx_slab_alloc_locked(ctx->shpool, + (uintptr_t) ngx_align_ptr(value.len, NGX_ALIGNMENT)); + + if (zset_node->value.data == NULL) { + + if (exists == 0) { + /* not replaced */ + ngx_slab_free_locked(ctx->shpool, znode); + } + + if (node != NULL && sd->value_len == 0) { + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_queue_remove(&sd->queue); + + ngx_slab_free_locked(ctx->shpool, node); + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 0); + lua_pushliteral(L, "no memory"); + return 2; + } + + ngx_memcpy(zset_node->value.data, value.data, value.len); + } else { + + ngx_str_null(&zset_node->value); + zset_node->value_type = 0; + } + + if (old_value.data) { + ngx_slab_free_locked(ctx->shpool, old_value.data); + } + + if (exists == 0) { + dd("setting zset length to %d", sd->value_len + 1); + sd->value_len = sd->value_len + 1; + } + + ngx_memcpy(zset_node->data, zkey.data, zkey.len); + zset_node->data[zkey.len] = 0; + + ngx_rbtree_insert(&zset->rbtree, znode); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnumber(L, sd->value_len); + + return 1; +} + + +static int +ngx_http_lua_shdict_zrem(lua_State *L) +{ + int n; + ngx_str_t name; + ngx_str_t key; + ngx_str_t zkey; + uint32_t hash; + ngx_int_t rc; + ngx_http_lua_shdict_ctx_t *ctx; + ngx_http_lua_shdict_node_t *sd; + ngx_rbtree_node_t *node; + ngx_rbtree_node_t *sentinel; + ngx_shm_zone_t *zone; + ngx_http_lua_shdict_zset_t *zset; + ngx_http_lua_shdict_zset_node_t *zset_node; + + n = lua_gettop(L); + + if (n != 3) { + return luaL_error(L, "expecting 3 arguments, " + "but only seen %d", n); + } + + if (lua_type(L, 1) != LUA_TTABLE) { + return luaL_error(L, "bad \"zone\" argument"); + } + + zone = ngx_http_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad \"zone\" argument"); + } + + ctx = zone->data; + name = ctx->name; + + if (lua_isnil(L, 2)) { + lua_pushnil(L); + lua_pushliteral(L, "nil key"); + return 2; + } + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty key"); + return 2; + } + + if (key.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "key too long"); + return 2; + } + + hash = ngx_crc32_short(key.data, key.len); + + zkey.data = (u_char *) luaL_checklstring(L, 3, &zkey.len); + + if (zkey.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty zkey"); + return 2; + } + + if (zkey.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "zkey too long"); + return 2; + } + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_http_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_pushnil(L); + return 1; + } + + /* rc == NGX_OK */ + + if (sd->value_type != SHDICT_TZSET) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value not a zset"); + return 2; + } + + if (sd->value_len <= 0) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad lua zset length found for key %s " + "in shared_dict %s: %lu", key.data, name.data, + (unsigned long) sd->value_len); + } + + zset = ngx_http_lua_shdict_get_rbtree(sd, key.len); + + node = zset->rbtree.root; + sentinel = zset->rbtree.sentinel; + + while (node != sentinel) { + + zset_node = (ngx_http_lua_shdict_zset_node_t *) &node->color; + + rc = ngx_strcmp(zkey.data, zset_node->data); + + if (rc < 0) { + + node = node->left; + continue; + + } else if (rc > 0) { + + node = node->right; + continue; + + } + + /* found */ + + ngx_http_lua_shdict_push_znode_value(L, zset_node); + + if (zset_node->value.data) { + ngx_slab_free_locked(ctx->shpool, zset_node->value.data); + } + + ngx_rbtree_delete(&zset->rbtree, node); + ngx_slab_free_locked(ctx->shpool, node); + + sd->value_len = sd->value_len - 1; + + goto ret; + } + + lua_pushlightuserdata(L, NULL); + +ret: + + dd("value len: %d", (int) sd->value_len); + + if (sd->value_len == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "lua shared dict zset: empty node after zrem, " + "remove it"); + + ngx_queue_remove(&sd->queue); + + node = (ngx_rbtree_node_t *) + ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(&ctx->sh->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + + } else { + ngx_queue_remove(&sd->queue); + ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue); + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return 1; +} + + +static int +ngx_http_lua_shdict_zcard(lua_State *L) +{ + int n; + ngx_str_t name; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_http_lua_shdict_ctx_t *ctx; + ngx_http_lua_shdict_node_t *sd; + ngx_shm_zone_t *zone; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, " + "but only seen %d", n); + } + + if (lua_type(L, 1) != LUA_TTABLE) { + return luaL_error(L, "bad \"zone\" argument"); + } + + zone = ngx_http_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad \"zone\" argument"); + } + + ctx = zone->data; + name = ctx->name; + + if (lua_isnil(L, 2)) { + lua_pushnil(L); + lua_pushliteral(L, "nil key"); + return 2; + } + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty key"); + return 2; + } + + if (key.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "key too long"); + return 2; + } + + hash = ngx_crc32_short(key.data, key.len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_http_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_pushnil(L); + return 1; + } + + /* rc == NGX_OK */ + + if (sd->value_type != SHDICT_TZSET) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value not a zset"); + return 2; + } + + if (sd->value_len <= 0) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad lua zgetall length found for key %s " + "in shared_dict %s: %lu", key.data, name.data, + (unsigned long) sd->value_len); + } + + lua_pushinteger(L, sd->value_len); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return 1; +} + + +static int +ngx_http_lua_shdict_zget(lua_State *L) +{ + int n; + ngx_str_t name; + ngx_str_t key; + ngx_str_t zkey; + uint32_t hash; + ngx_int_t rc; + ngx_http_lua_shdict_ctx_t *ctx; + ngx_http_lua_shdict_node_t *sd; + ngx_rbtree_node_t *node; + ngx_rbtree_node_t *sentinel; + ngx_shm_zone_t *zone; + ngx_http_lua_shdict_zset_t *zset; + ngx_http_lua_shdict_zset_node_t *zset_node; + + n = lua_gettop(L); + + if (n != 3) { + return luaL_error(L, "expecting 3 arguments, " + "but only seen %d", n); + } + + if (lua_type(L, 1) != LUA_TTABLE) { + return luaL_error(L, "bad \"zone\" argument"); + } + + zone = ngx_http_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad \"zone\" argument"); + } + + ctx = zone->data; + name = ctx->name; + + if (lua_isnil(L, 2)) { + lua_pushnil(L); + lua_pushliteral(L, "nil key"); + return 2; + } + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty key"); + return 2; + } + + if (key.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "key too long"); + return 2; + } + + hash = ngx_crc32_short(key.data, key.len); + + zkey.data = (u_char *) luaL_checklstring(L, 3, &zkey.len); + + if (zkey.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty zkey"); + return 2; + } + + if (zkey.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "zkey too long"); + return 2; + } + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_http_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_pushnil(L); + return 1; + } + + /* rc == NGX_OK */ + + if (sd->value_type != SHDICT_TZSET) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value not a zset"); + return 2; + } + + if (sd->value_len <= 0) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad lua zget length found for key %s " + "in shared_dict %s: %lu", key.data, name.data, + (unsigned long) sd->value_len); + } + + zset = ngx_http_lua_shdict_get_rbtree(sd, key.len); + + node = zset->rbtree.root; + sentinel = zset->rbtree.sentinel; + + while (node != sentinel) { + + zset_node = (ngx_http_lua_shdict_zset_node_t *) &node->color; + + rc = ngx_strcmp(zkey.data, zset_node->data); + + if (rc < 0) { + + node = node->left; + continue; + + } else if (rc > 0) { + + node = node->right; + continue; + + } + + /* push zkey */ + lua_pushlstring(L, (char *) zkey.data, zkey.len); + + /* push zvalue */ + ngx_http_lua_shdict_push_znode_value(L, zset_node); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return 2; + } + + lua_pushnil(L); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return 1; +} + + +static int +ngx_http_lua_shdict_zgetall(lua_State *L) +{ + int n; + ngx_str_t name; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_http_lua_shdict_ctx_t *ctx; + ngx_http_lua_shdict_node_t *sd; + ngx_rbtree_node_t *node; + ngx_rbtree_node_t *sentinel; + ngx_shm_zone_t *zone; + ngx_http_lua_shdict_zset_t *zset; + ngx_http_lua_shdict_zset_node_t *zset_node; + + n = lua_gettop(L); + + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, " + "but only seen %d", n); + } + + if (lua_type(L, 1) != LUA_TTABLE) { + return luaL_error(L, "bad \"zone\" argument"); + } + + zone = ngx_http_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad \"zone\" argument"); + } + + ctx = zone->data; + name = ctx->name; + + if (lua_isnil(L, 2)) { + lua_pushnil(L); + lua_pushliteral(L, "nil key"); + return 2; + } + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty key"); + return 2; + } + + if (key.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "key too long"); + return 2; + } + + hash = ngx_crc32_short(key.data, key.len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_http_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_pushnil(L); + return 1; + } + + /* rc == NGX_OK */ + + if (sd->value_type != SHDICT_TZSET) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value not a zset"); + return 2; + } + + if (sd->value_len <= 0) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad lua zgetall length found for key %s " + "in shared_dict %s: %lu", key.data, name.data, + (unsigned long) sd->value_len); + } + + zset = ngx_http_lua_shdict_get_rbtree(sd, key.len); + + node = zset->rbtree.root; + sentinel = zset->rbtree.sentinel; + + lua_createtable(L, sd->value_len, 0); + + if (node != sentinel) { + n = 1; + + for (node = ngx_rbtree_min(node, sentinel); + node; + node = ngx_rbtree_next(&zset->rbtree, node)) + { + zset_node = (ngx_http_lua_shdict_zset_node_t *) &node->color; + + lua_createtable(L, 2, 0); + + /* push zkey */ + lua_pushstring(L, (char *) zset_node->data); + lua_rawseti(L, -2, 1); + + /* push zvalue */ + ngx_http_lua_shdict_push_znode_value(L, zset_node); + lua_rawseti(L, -2, 2); + + lua_rawseti(L, -2, n++); + } + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return 1; +} + + +static int +ngx_http_lua_shdict_zscan(lua_State *L) +{ + int n; + ngx_str_t name; + ngx_str_t key; + uint32_t hash; + ngx_int_t rc; + ngx_http_lua_shdict_ctx_t *ctx; + ngx_http_lua_shdict_node_t *sd; + ngx_rbtree_node_t *node; + ngx_rbtree_node_t *tmp = NULL; + ngx_rbtree_node_t *sentinel; + ngx_shm_zone_t *zone; + ngx_http_lua_shdict_zset_t *zset; + ngx_http_lua_shdict_zset_node_t *zset_node; + ngx_str_t lbound; + u_char *err_msg; + size_t len; + + n = lua_gettop(L); + + if (n != 3 && n != 4) { + return luaL_error(L, "expecting 3 or 4 arguments, " + "but only seen %d", n); + } + + if (lua_type(L, 1) != LUA_TTABLE) { + return luaL_error(L, "bad \"zone\" argument"); + } + + zone = ngx_http_lua_shdict_get_zone(L, 1); + if (zone == NULL) { + return luaL_error(L, "bad \"zone\" argument"); + } + + if (lua_type(L, 3) != LUA_TFUNCTION) { + return luaL_error(L, "bad \"callback\" argument"); + } + + ctx = zone->data; + name = ctx->name; + + if (lua_isnil(L, 2)) { + lua_pushnil(L); + lua_pushliteral(L, "nil key"); + return 2; + } + + key.data = (u_char *) luaL_checklstring(L, 2, &key.len); + + if (key.len == 0) { + lua_pushnil(L); + lua_pushliteral(L, "empty key"); + return 2; + } + + if (key.len > 65535) { + lua_pushnil(L); + lua_pushliteral(L, "key too long"); + return 2; + } + + hash = ngx_crc32_short(key.data, key.len); + + if (n == 4) { + lbound.data = (u_char *) lua_tolstring(L, 4, &lbound.len); + } else { + lbound.data = NULL; + } + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_http_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); + + dd("shdict lookup returned %d", (int) rc); + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + lua_pushnil(L); + return 1; + } + + /* rc == NGX_OK */ + + if (sd->value_type != SHDICT_TZSET) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushnil(L); + lua_pushliteral(L, "value not a zset"); + return 2; + } + + if (sd->value_len <= 0) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return luaL_error(L, "bad lua zgetall length found for key %s " + "in shared_dict %s: %lu", key.data, name.data, + (unsigned long) sd->value_len); + } + + zset = ngx_http_lua_shdict_get_rbtree(sd, key.len); + + node = zset->rbtree.root; + sentinel = zset->rbtree.sentinel; + + if (node != sentinel) { + + n = lua_gettop(L); + + if (lbound.data != NULL) { + + while (node != sentinel) { + + zset_node = (ngx_http_lua_shdict_zset_node_t *) &node->color; + + rc = ngx_strncmp(lbound.data, zset_node->data, lbound.len); + + if (rc <= 0) { + + if (rc == 0) { + tmp = node; + } + + node = node->left; + + continue; + + } else if (rc > 0) { + + node = node->right; + continue; + + } + } + + if (tmp != NULL) { + node = tmp; + } + } else { + + node = ngx_rbtree_min(node, sentinel); + } + + if (node != sentinel) { + + for (; node; node = ngx_rbtree_next(&zset->rbtree, node)) + { + lua_pushvalue(L, 3); + + zset_node = (ngx_http_lua_shdict_zset_node_t *) &node->color; + + /* push zkey */ + lua_pushstring(L, (char *) zset_node->data); + + /* push zvalue */ + ngx_http_lua_shdict_push_znode_value(L, zset_node); + + if (lua_pcall(L, 2, 2, 0) != 0) { + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + /* error occurred when calling user code */ + err_msg = (u_char *) lua_tolstring(L, -1, &len); + + if (err_msg == NULL) { + err_msg = (u_char *) "unknown reason"; + len = sizeof("unknown reason") - 1; + } + + return luaL_error(L, "user callback error " + "shared_dict %s: %s", name.data, err_msg); + } + + if (lua_isboolean(L, -1)) { + + if (lua_toboolean(L, -1)) { + + /* stop on true */ + break; + } + } + + lua_settop(L, n); + } + } + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + lua_pushboolean(L, 1); + + return 1; +} + + +ngx_shm_zone_t * +ngx_http_lua_find_zone(u_char *name_data, size_t name_len) +{ + ngx_str_t *name; + ngx_uint_t i; + ngx_shm_zone_t *zone; + ngx_http_lua_shm_zone_ctx_t *ctx; + volatile ngx_list_part_t *part; + + part = &ngx_cycle->shared_memory.part; + zone = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + zone = part->elts; + i = 0; + } + + name = &zone[i].shm.name; + + dd("name: [%.*s] %d", (int) name->len, name->data, (int) name->len); + dd("name2: [%.*s] %d", (int) name_len, name_data, (int) name_len); + + if (name->len == name_len + && ngx_strncmp(name->data, name_data, name_len) == 0) + { + ctx = (ngx_http_lua_shm_zone_ctx_t *) zone[i].data; + return &ctx->zone; + } + } + + return NULL; +} + + +#ifndef NGX_LUA_NO_FFI_API +int +ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, + size_t key_len, int value_type, u_char *str_value_buf, + size_t str_value_len, double num_value, int exptime, int user_flags, + char **errmsg, int *forcible) +{ + int i, n; + u_char c, *p; + uint32_t hash; + ngx_int_t rc; + ngx_time_t *tp; + ngx_rbtree_node_t *node; + ngx_http_lua_shdict_ctx_t *ctx; + ngx_http_lua_shdict_node_t *sd; + + if (zone == NULL) { + return NGX_ERROR; + } + + dd("exptime: %d", exptime); + + ctx = zone->data; + + *forcible = 0; + + hash = ngx_crc32_short(key, key_len); + + switch (value_type) { + + case SHDICT_TSTRING: + /* do nothing */ + break; + + case SHDICT_TNUMBER: + dd("num value: %lf", num_value); + str_value_buf = (u_char *) &num_value; + str_value_len = sizeof(double); + break; + + case SHDICT_TBOOLEAN: + c = num_value ? 1 : 0; + str_value_buf = &c; + str_value_len = sizeof(u_char); + break; + + case LUA_TNIL: + if (op & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) { + *errmsg = "attempt to add or replace nil values"; + return NGX_ERROR; + } + + str_value_buf = NULL; + str_value_len = 0; + break; + + default: + *errmsg = "unsupported value type"; + return NGX_ERROR; + } + + ngx_shmtx_lock(&ctx->shpool->mutex); + +#if 1 + ngx_http_lua_shdict_expire(ctx, 1); +#endif + + rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd); + + dd("lookup returns %d", (int) rc); + + if (op & NGX_HTTP_LUA_SHDICT_REPLACE) { + + if (rc == NGX_DECLINED || rc == NGX_DONE) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + *errmsg = "not found"; + return NGX_DECLINED; + } + + /* rc == NGX_OK */ + + goto replace; + } + + if (op & NGX_HTTP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); @@ -2324,7 +3600,8 @@ ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, if (str_value_buf && str_value_len == (size_t) sd->value_len - && sd->value_type != SHDICT_TLIST) + && sd->value_type != SHDICT_TLIST + && sd->value_type != SHDICT_TZSET) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, @@ -2368,18 +3645,11 @@ ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, remove: if (sd->value_type == SHDICT_TLIST) { - queue = ngx_http_lua_shdict_get_list_head(sd, key_len); - - for (q = ngx_queue_head(queue); - q != ngx_queue_sentinel(queue); - q = ngx_queue_next(q)) - { - p = (u_char *) ngx_queue_data(q, - ngx_http_lua_shdict_list_node_t, - queue); + ngx_http_lua_shdict_free_list(ctx, sd); + } - ngx_slab_free_locked(ctx->shpool, p); - } + if (sd->value_type == SHDICT_TZSET) { + ngx_http_lua_shdict_free_rbtree(ctx, sd); } ngx_queue_remove(&sd->queue); @@ -2593,6 +3863,13 @@ ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key, *err = "value is a list"; return NGX_ERROR; + case SHDICT_TZSET: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + *err = "value is a zset"; + return NGX_ERROR; + default: ngx_shmtx_unlock(&ctx->shpool->mutex); @@ -2634,7 +3911,6 @@ ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key, double num; ngx_rbtree_node_t *node; u_char *p; - ngx_queue_t *queue, *q; if (zone == NULL) { return NGX_ERROR; @@ -2676,7 +3952,8 @@ ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key, /* found an expired item */ if ((size_t) sd->value_len == sizeof(double) - && sd->value_type != SHDICT_TLIST) + && sd->value_type != SHDICT_TLIST + && sd->value_type != SHDICT_TZSET) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict incr: found old entry and " @@ -2729,17 +4006,11 @@ ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key, "NOT matched, removing it first"); if (sd->value_type == SHDICT_TLIST) { - queue = ngx_http_lua_shdict_get_list_head(sd, key_len); - - for (q = ngx_queue_head(queue); - q != ngx_queue_sentinel(queue); - q = ngx_queue_next(q)) - { - p = (u_char *) ngx_queue_data(q, ngx_http_lua_shdict_list_node_t, - queue); + ngx_http_lua_shdict_free_list(ctx, sd); + } - ngx_slab_free_locked(ctx->shpool, p); - } + if (sd->value_type == SHDICT_TZSET) { + ngx_http_lua_shdict_free_rbtree(ctx, sd); } ngx_queue_remove(&sd->queue); diff --git a/src/ngx_http_lua_shdict.h b/src/ngx_http_lua_shdict.h index 90a0099f5d..35cc74da45 100644 --- a/src/ngx_http_lua_shdict.h +++ b/src/ngx_http_lua_shdict.h @@ -31,6 +31,18 @@ typedef struct { } ngx_http_lua_shdict_list_node_t; +typedef struct { + u_char color; + uint8_t value_type; + ngx_str_t value; + u_char data[1]; +} ngx_http_lua_shdict_zset_node_t; + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; +} ngx_http_lua_shdict_zset_t; + typedef struct { ngx_rbtree_t rbtree; ngx_rbtree_node_t sentinel; diff --git a/t/062-count.t b/t/062-count.t index b4d9d1c945..54e1cf2e45 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -283,7 +283,7 @@ n = 5 --- request GET /test --- response_body -n = 18 +n = 24 --- no_error_log [error] diff --git a/t/155-shdict-zset.t b/t/155-shdict-zset.t new file mode 100644 index 0000000000..f220f70524 --- /dev/null +++ b/t/155-shdict-zset.t @@ -0,0 +1,272 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: +use Test::Nginx::Socket::Lua; + +#worker_connections(1014); +#master_process_enabled(1); +#log_level('warn'); + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 3 + 0); + +#no_diff(); +no_long_string(); +#master_on(); +#workers(2); + +run_tests(); + +__DATA__ + +=== TEST 1: zset & zget & zrem +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua_block { + local dogs = ngx.shared.dogs + + local len, err = dogs:zset("foo", "bar", "hello") + if len then + ngx.say("zset ", len) + else + ngx.say("szet err: ", err) + end + + local len, err = dogs:zset("foo", "foo", 999) + if len then + ngx.say("zset ", len) + else + ngx.say("szet err: ", err) + end + + local zkey, val = dogs:zget("foo", "bar") + ngx.say(zkey, " ", val) + + local zkey, val = dogs:zget("foo", "foo") + ngx.say(zkey, " ", val) + + local val, err = dogs:zrem("foo", "bar") + if val then + ngx.say(val) + else + ngx.say("zrem err: ", err) + end + + local val, err = dogs:zrem("foo", "foo") + if val then + ngx.say(val) + else + ngx.say("zrem err: ", err) + end + + dogs:delete("foo") + } + } +--- request +GET /test +--- response_body +zset 1 +zset 2 +bar hello +foo 999 +hello +999 +--- no_error_log +[error] + + +=== TEST 2: exptime +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua_block { + local dogs = ngx.shared.dogs + + local len, err = dogs:zset("foo", "bar", "hello", 1) + if len then + ngx.say("zset ", len) + else + ngx.say("zset err: ", err) + end + + local zkey, val = dogs:zget("foo", "bar") + ngx.say(zkey, " ", val) + + ngx.sleep(2) + + local zkey, val = dogs:zget("foo", "bar") + ngx.say(zkey) + + local len, err = dogs:zset("foo", "bar", "hello2") + if len then + ngx.say("zset ", len) + else + ngx.say("zset err: ", err) + end + + local zkey, val = dogs:zget("foo", "bar") + ngx.say(zkey, " ", val) + + local val, err = dogs:zrem("foo", "bar") + if val then + ngx.say(val) + else + ngx.say("zrem err: ", err) + end + + dogs:delete("foo") + } + } +--- request +GET /test +--- response_body +zset 1 +bar hello +nil +zset 1 +bar hello2 +hello2 +--- no_error_log +[error] + + +=== TEST 3: zset & zgetall +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua_block { + local dogs = ngx.shared.dogs + + local vals = { + { "a", 1 }, { "b", 2 }, { "c", 3 }, { "d", 4 }, { "e", 5 } + } + + for _,v in ipairs(vals) do + local len, err = dogs:zset("foo", unpack(v)) + if not len then + ngx.say("zset err: ", err) + end + end + + ngx.say(dogs:zcard("foo")) + + local v = dogs:zgetall("foo") + for _,i in ipairs(v) do + ngx.say(unpack(i)) + end + + for _,i in pairs(vals) do + local zkey = unpack(i) + ngx.print(dogs:zrem("foo", zkey)) + end + ngx.say() + ngx.say(dogs:zcard("foo")) + + dogs:delete("foo") + } + } +--- request +GET /test +--- response_body +5 +a1 +b2 +c3 +d4 +e5 +12345 +nil +--- no_error_log +[error] + + +=== TEST 4: zset & zscan +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua_block { + local dogs = ngx.shared.dogs + + local vals = { + { "a", 1 }, { "b", 2 }, { "c", 3 }, { "d", 4 }, { "e", 5 } + } + + for _,v in ipairs(vals) do + local len, err = dogs:zset("foo", unpack(v)) + if not len then + ngx.say("zset err: ", err) + end + end + + ngx.say(dogs:zcard("foo")) + + dogs:zscan("foo", function(k,v) + ngx.say(k, v) + end) + + dogs:delete("foo") + } + } +--- request +GET /test +--- response_body +5 +a1 +b2 +c3 +d4 +e5 +--- no_error_log +[error] + + +=== TEST 5: zset & zscan (range) +--- http_config + lua_shared_dict dogs 1m; +--- config + location = /test { + content_by_lua_block { + local dogs = ngx.shared.dogs + + local vals = { + { "a", 1 }, + { "aa", 11 }, + { "b", 2 }, + { "bb", 22 }, + { "aaa", 111 }, + { "aab", 112 }, + { "x", 0 } + } + + for _,v in ipairs(vals) do + local len, err = dogs:zset("foo", unpack(v)) + if not len then + ngx.say("zset err: ", err) + end + end + + ngx.say(dogs:zcard("foo")) + + dogs:zscan("foo", function(k,v) + if k:sub(1,2) ~= "aa" then + return true + end + ngx.say(k, v) + end, "aa") + + dogs:delete("foo") + } + } +--- request +GET /test +--- response_body +7 +aa11 +aaa111 +aab112 +--- no_error_log +[error]