ngx.shared.DICT.set

ngx.shared.DICT.set

原文: ngx.shared.DICT.setnode

syntax: success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags)

context: init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, 
         header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, 
         balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, 
         ssl_session_store_by_lua*

無條件地將鍵值對設置到 ngx.shared.DICT 中。返回三個值:nginx

  • success:布爾值,指示鍵值對是否成功設置到共享內存中
  • err:文本錯誤信息,可能爲 "no memory"
  • forcible:布爾值,指示當共享內存中內存不足時是不是經過強制移除有效項來將當前的鍵值對設置到共享內存中

要插入的 valua 參數能夠爲 Lua boolean,number,string,或者 nil。它們的值類型也將保存到共享內存中,之後能夠經過 get 方法獲取到一樣的數據類型。git

可選地 exptime 參數指定了插入的鍵值對的過時時間(以秒爲單位)。時間分辨率爲 0.001 秒。若是 exptime 值爲 0(默認),則該插入的項將永不過時。github

可選的 flags 參數指定了要保存的項相關聯的用戶標誌值。之後能夠經過 value 獲取它。用戶標誌在內部做爲一個無符號 32 位整數值保存。默認爲 0。算法

當爲當前的 key-value 項分配內存失敗時,set 方法將會嘗試按照最近最少使用(即 LRU 算法)來移除共享內存中存在的項。注意,LRU 優於過時時間。若是已經移除數十個存在的項,而且剩餘內存仍然不足(因爲 lua_shared_dict 指定的總內存限制或者內存分片),則 err 錯誤描述信息爲 "no memory",而 success 將爲 false。session

若是該方法經過 LRU 算法強制將共享內存中其餘未過時的項移除來成功保存當前的項,則 forcible 返回值將爲 true。若是不是經過強制移除其餘有效項來成功保存當前項,則 forcible 返回值爲 false。fetch

該方法的第一個參數必須爲字典自己,以下:ui

local cats = ngx.shared.cats
local succ, err, forcible = cats.set(cats, "Marry", "it is a nice cat!")

或者:lua

local cats = ngx.shared.cats
local succ, err, forcible = cats:set("Marry", "it is a nice cat!")

set 源碼實現

local function shdict_set(zone, key, value, exptime, flags)
    return shdict_store(zone, 0, key, value, exptime, flags)
end

shdict_store

local tostring = tostring
local type = type
local error = error

local function check_zone(zone)
    if not zone or type(zone) ~= "table" then
        error("bad \"zone\" argument", 2)
    end

    zone = zone[1]
    if type(zone) ~= "userdata" then
        error("bad \"zone\" argument", 2)
    end

    return zone
end

local function shdict_store(zone, op, key, value, exptime, flags)
    zone = check_zone(zone)
    
    if not exptime then
        exptime = 0
    elseif exptime < 0 then
        error('bad "exptime" argument', 2)
    end
    
    if not flags then
        flags = 0
    end
    
    if key == nil then
        return nil, "nil key"
    end
    
    if type(key) ~= "string" then
        key = tostring(key)
    end
    
    local key_len = #key
    if key_len == 0 then
        return nil, "empty key"
    end
    if key_len > 65535 then
        return nil, "key too long"
    end
    
    local str_val_buf
    local str_val_len = 0
    local num_val = 0
    local valtyp = type(value)
    
    -- print("value type: ", valtyp)
    -- print("exptime: ", exptime)

    if valtyp == "string" then
        valtyp = 4  -- LUA_TSTRING
        str_val_buf = value
        str_val_len = #value
        
    elseif valtyp == "number" then
        valtyp = 3  -- LUA_TNUMBER
        num_val = value
        
    elseif value == nil then
        valtyp = 0  - LUA_TNIL
        
    elseif valtyp == "boolean" then
        valtyp = 1  -- LUA_TBOOLEAN
        num_val = value and 1 or 0
        
    else
        return nil, "bad value type"
    end
    
    local rc = C.ngx_http_lua_ffi_shdict_store(zone, op, key, key_len, 
                                               valtyp, str_val_buf, 
                                               str_val_len, num_val,
                                               exptime * 1000, flags, errmsg,
                                               forcible)
                                               
    -- print("rc == ", rc)
    
    if rc == 0 then -- NGX_OK
        return true, nil, forcible[0] == 1
    end
    
    -- NGX_DECLIEND or NGX_ERROR
    return false, ffi_str(errmsg[0]), forcible[0] == 1
end

ngx_http_lua_ffi_shdict_store

#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, long 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;
    
    if (zone == NULL) {
        return NGX_ERROR;
    }
    
    dd("exptime: %ld", 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 returnd %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);
            *errmsg = "exists";
            return NGX_DECLINED;
        }
        
        if (rc == NGX_DONE) {
            /* exists but expired */
            
            dd("go to replace");
            goto replace;
        }
        
        /* rc == NGX_DECLINED */
        
        dd("dd to insert");
        goto insert;
    }
    
    if (rc == NGX_OK || rc == NGX_DONE) {
        
        if (value_type == LUA_TNIL) {
            goto remove;
        }
        
replace:
    
        if (str_value_buf 
            && str_value_len == (size_t) sd->value_len
            && sd->value_type != SHDICT_TLIST)
        {
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, 
                           "lua shared dict set: found old entry and value "
                           "size matched, reusing it");
                           
            ngx_queue_remove(&sd->queue);
            ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
            
            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;
                              
            } else {
                sd->expires = 0;
            }
            
            sd->user_flags = user_flags;
            
            sd->value_len = (uint32_t) str_value_len;
            
            dd("setting value type to %d", value_type);
            
            sd->value_type = (uint8_t) value_type;
            
            p = ngx_copy(sd->data, key, key_len);
            ngx_memcpy(p, str_value_buf, str_value_len);
            
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            
            return NGX_OK;
        }
        
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
                       "lua shared dict set: found old entry but value size "
                       "NOT matched, removing it first");
                       
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_slab_free_locked(ctx->shpool, p);
            }
        }
        
        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);
        
    }
    
insert:
    
    /* rc == NGX_DECLINED or value size unmatch */
    
    if (str_value_buf == NULL) {
        ngx_shmtx_unlock(&ctx->shpool->mutex);
        return NGX_OK;
    }
    
    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
                   "lua shared dict set: creating a new entry");

    n = offsetof(ngx_rbtree_node_t, color) 
        + offsetof(ngx_http_lua_shdict_node_t, data)
        + key_len 
        + str_value_len;
    
    node = ngx_slab_alloc_locked(ctx->shpool, n);
    
    if (node == NULL) {
        
        if (op & NGX_HTTP_LUA_SHDICT_SAFE_STORE) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            
            *errmsg = "no memory";
            return NGX_ERROR;
        }
        
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0, 
                       "lua shared dict set: overriding non-expired items "
                       "due to memory shortage for entry \"%*s\"", key_len,
                       key);
                       
        for (i = 0; i < 30; i++) {
            if (ngx_http_lua_shdict_expire(ctx, 0) == 0) {
                /* 沒有項過時,即移除失敗 */
                break;
            }
            
            *forcible = 1;
            
            node = ngx_slab_alloc_locked(ctx->shpool, n);
            if (node != NULL) {
                goto allocated;
            }
        } 
        
        ngx_shmtx_unlock(&ctx->shpool->mutex);

        *errmsg = "no memory";
        return NGX_ERROR;
    }
    
allocated:
    
    sd = (ngx_http_lua_shdict_node_t *) &node->color;
    
    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;
                      
    } else {
        sd->expires = 0;
    }
    
    sd->user_flags = user_flags;
    sd->value_len = (uint32_t) str_value_len;
    dd("setting value type to %d", value_type);
    sd->value_type = (uint8_t) value_type;
    
    p = ngx_copy(sd->data, key, key_len);
    ngx_memcpy(p, str_value_buf, str_value_len);
    
    ngx_rbtree_insert(&ctx->sh->rbtree, node);
    ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);
    ngx_shmtx_unlock(&ctx->shpool->mutex);

    return NGX_OK;
}
相關文章
相關標籤/搜索