aboutsummaryrefslogtreecommitdiffstats
path: root/msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/vendor/shcache.lua
diff options
context:
space:
mode:
Diffstat (limited to 'msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/vendor/shcache.lua')
-rw-r--r--msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/vendor/shcache.lua440
1 files changed, 0 insertions, 440 deletions
diff --git a/msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/vendor/shcache.lua b/msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/vendor/shcache.lua
deleted file mode 100644
index 32a9b6c..0000000
--- a/msb-core/openresty-ext/src/assembly/resources/openresty/nginx/luaext/vendor/shcache.lua
+++ /dev/null
@@ -1,440 +0,0 @@
--- Copyright (C) 2013 Matthieu Tourne
--- @author Matthieu Tourne <matthieu@cloudflare.com>
-
--- small overlay over shdict, smart cache load mechanism
-
-local M = {}
-
-local resty_lock = require("resty.lock")
-
-local DEBUG = false
-
--- defaults in secs
-local DEFAULT_POSITIVE_TTL = 10 -- cache for, successful lookup
-local DEFAULT_NEGATIVE_TTL = 2 -- cache for, failed lookup
-local DEFAULT_ACTUALIZE_TTL = 2 -- stale data, actualize data for
-
--- default lock options, in secs
-local function _get_default_lock_options()
- return {
- exptime = 1, -- max wait if failing to call unlock()
- timeout = 0.5, -- max waiting time of lock()
- max_step = 0.1, -- max sleeping interval
- }
-end
-
-local function prequire(m)
- local ok, err_or_module = pcall(require, m)
- if not ok then
- return nil, err_or_module
- end
- return err_or_module
-end
-
-local conf = prequire("conf")
-if conf then
- DEFAULT_NEGATIVE_TTL = conf.DEFAULT_NEGATIVE_TTL or DEFAULT_NEGATIVE_TTL
- DEFAULT_ACTUALIZE_TTL = conf.DEFAULT_ACTUALIZE_TTL or DEFAULT_ACTUALIZE_TTL
-end
-
-local band = bit.band
-local bor = bit.bor
-local st_format = string.format
-
--- there are only really 5 states total
- -- is_stale is_neg is_from_cache
-local MISS_STATE = 0 -- 0 0 0
-local HIT_POSITIVE_STATE = 1 -- 0 0 1
-local HIT_NEGATIVE_STATE = 3 -- 0 1 1
-local STALE_POSITIVE_STATE = 5 -- 1 0 1
-
--- stale negative doesn't really make sense, use HIT_NEGATIVE instead
--- local STALE_NEGATIVE_STATE = 7 -- 1 1 1
-
--- xor to set
-local NEGATIVE_FLAG = 2
-local STALE_FLAG = 4
-
-local STATES = {
- [MISS_STATE] = 'MISS',
- [HIT_POSITIVE_STATE] = 'HIT',
- [HIT_NEGATIVE_STATE] = 'HIT_NEGATIVE',
- [STALE_POSITIVE_STATE] = 'STALE',
- -- [STALE_NEGATIVE_STATE] = 'STALE_NEGATIVE',
-}
-
-local function get_status(flags)
- return STATES[flags] or st_format('UNDEF (0x%x)', flags)
-end
-
-local EMPTY_DATA = '_EMPTY_'
-
--- install debug functions
-if DEBUG then
- local resty_lock_lock = resty_lock.lock
-
- resty_lock.lock = function (...)
- local _, key = unpack({...})
- print("lock key: ", tostring(key))
- return resty_lock_lock(...)
- end
-
- local resty_lock_unlock = resty_lock.unlock
-
- resty_lock.unlock = function (...)
- print("unlock")
- return resty_lock_unlock(...)
- end
-end
-
-
--- store the object in the context
--- useful for debugging and tracking cache status
-local function _store_object(self, name)
- if DEBUG then
- print('storing shcache: ', name, ' into ngx.ctx')
- end
-
- local ngx_ctx = ngx.ctx
-
- if not ngx_ctx.shcache then
- ngx_ctx.shcache = {}
- end
- ngx_ctx.shcache[name] = self
-end
-
-local obj_mt = {
- __index = M,
-}
-
--- default function for callbacks.encode / decode.
-local function _identity(data)
- return data
-end
-
--- shdict: ngx.shared.DICT, created by the lua_shared_dict directive
--- callbacks: see shcache state machine for user defined functions
--- * callbacks.external_lookup is required
--- * callbacks.encode : optional encoding before saving to shmem
--- * callbacks.decode : optional decoding when retreiving from shmem
--- opts:
--- * opts.positive_ttl : save a valid external loookup for, in seconds
--- * opts.positive_ttl : save a invalid loookup for, in seconds
--- * opts.actualize_ttl : re-actualize a stale record for, in seconds
--- * opts.lock_options : set option to lock see : http://github.com/agentzh/lua-resty-lock
--- for more details.
--- * opts.locks_shdict : specificy the name of the shdict containing the locks
--- (useful if you might have locks key collisions)
--- uses "locks" by default.
--- * opts.name : if shcache object is named, it will automatically
--- register itself in ngx.ctx.shcache (useful for logging).
-local function new(self, shdict, callbacks, opts)
- if not shdict then
- return nil, "shdict does not exist"
- end
-
- -- check that callbacks.external_lookup is set
- if not callbacks or not callbacks.external_lookup then
- return nil, "no external_lookup function defined"
- end
-
- if not callbacks.encode then
- callbacks.encode = _identity
- end
-
- if not callbacks.decode then
- callbacks.decode = _identity
- end
-
- local opts = opts or {}
-
- -- merge default lock options with the ones passed to new()
- local lock_options = _get_default_lock_options()
- if opts.lock_options then
- for k, v in pairs(opts.lock_options) do
- lock_options[k] = v
- end
- end
-
- local name = opts.name
-
- local obj = {
- shdict = shdict,
- callbacks = callbacks,
-
- positive_ttl = opts.positive_ttl or DEFAULT_POSITIVE_TTL,
- negative_ttl = opts.negative_ttl or DEFAULT_NEGATIVE_TTL,
-
- -- ttl to actualize stale data to
- actualize_ttl = opts.actualize_ttl or DEFAULT_ACTUALIZE_TTL,
-
- lock_options = lock_options,
-
- locks_shdict = opts.lock_shdict or "locks",
-
- -- STATUS --
-
- from_cache = false,
- cache_status = 'UNDEF',
- cache_state = MISS_STATE,
- lock_status = 'NO_LOCK',
-
- -- shdict:set() pushed out another value
- forcible_set = false,
-
- -- cache hit on second attempt (post lock)
- hit2 = false,
-
- name = name,
- }
-
- local locks = ngx.shared[obj.locks_shdict]
-
- -- check for existence, locks is not directly used
- if not locks then
- ngx.log(ngx.CRIT, 'shared mem locks is missing.\n',
- '## add to you lua conf: lua_shared_dict locks 5M; ##')
- return nil
- end
-
- local self = setmetatable(obj, obj_mt)
-
- -- if the shcache object is named
- -- keep track of the object in the context
- -- (useful for gathering stats at log phase)
- if name then
- _store_object(self, name)
- end
-
- return self
-end
-M.new = new
-
--- acquire a lock
-local function _get_lock(self)
- local lock = self.lock
- if not lock then
- lock = resty_lock:new(self.locks_shdict, self.lock_options)
- self.lock = lock
- end
- return lock
-end
-
--- remove the lock if there is any
-local function _unlock(self)
- local lock = self.lock
- if lock then
- local ok, err = lock:unlock()
- if not ok then
- ngx.log(ngx.ERR, "failed to unlock :" , err)
- end
- self.lock = nil
- end
-end
-
-local function _return(self, data, flags)
- -- make sure we remove the locks if any before returning data
- _unlock(self)
-
- -- set cache status
- local cache_status = get_status(self.cache_state)
-
- if cache_status == 'MISS' and not data then
- cache_status = 'NO_DATA'
- end
-
- self.cache_status = cache_status
-
- return data, self.from_cache
-end
-
-local function _set(self, ...)
- if DEBUG then
- local key, data, ttl, flags = unpack({...})
- print("saving key: ", key, ", for: ", ttl)
- end
-
- local ok, err, forcible = self.shdict:set(...)
-
- self.forcible_set = forcible
-
- if not ok then
- local key, data, ttl, flags = unpack({...})
- ngx.log(ngx.ERR, 'failed to set key: ', key, ', err: ', err)
- end
-
- return ok
-end
-
--- check if the data returned by :get() is considered empty
-local function _is_empty(data, flags)
- return flags and band(flags, NEGATIVE_FLAG) and data == EMPTY_DATA
-end
-
--- save positive, encode the data if needed before :set()
-local function _save_positive(self, key, data)
- if DEBUG then
- print("key: ", key, ". save positive, ttl: ", self.positive_ttl)
- end
- data = self.callbacks.encode(data)
- return _set(self, key, data, self.positive_ttl, HIT_POSITIVE_STATE)
-end
-
--- save negative, no encoding required (no data actually saved)
-local function _save_negative(self, key)
- if DEBUG then
- print("key: ", key, ". save negative, ttl: ", self.negative_ttl)
- end
- return _set(self, key, EMPTY_DATA, self.negative_ttl, HIT_NEGATIVE_STATE)
-end
-
--- save actualize, will boost a stale record to a live one
-local function _save_actualize(self, key, data, flags)
- local new_flags = bor(flags, STALE_FLAG)
-
- if DEBUG then
- print("key: ", key, ". save actualize, ttl: ", self.actualize_ttl,
- ". new state: ", get_status(new_flags))
- end
-
- _set(self, key, data, self.actualize_ttl, new_flags)
- return new_flags
-end
-
-local function _process_cached_data(self, data, flags)
- if DEBUG then
- print("data: ", data, st_format(", flags: %x", flags))
- end
-
- self.cache_state = flags
- self.from_cache = true
-
- if _is_empty(data, flags) then
- -- empty cached data
- return nil
- else
- return self.callbacks.decode(data)
- end
-end
-
--- wrapper to get data from the shdict
-local function _get(self, key)
- -- always call get_stale() as it does not free element
- -- like get does on each call
- local data, flags, stale = self.shdict:get_stale(key)
-
- if data and stale then
- if DEBUG then
- print("found stale data for key : ", key)
- end
-
- self.stale_data = { data, flags }
-
- return nil, nil
- end
-
- return data, flags
-end
-
-local function _get_stale(self)
- local stale_data = self.stale_data
- if stale_data then
- return unpack(stale_data)
- end
-
- return nil, nil
-end
-
-local function load(self, key)
- -- start: check for existing cache
- local data, flags = _get(self, key)
-
- -- hit: process_cache_hit
- if data then
- data = _process_cached_data(self, data, flags)
- return _return(self, data)
- end
-
- -- miss: set lock
-
- -- lock: set a lock before performing external lookup
- local lock = _get_lock(self)
- local elapsed, err = lock:lock(key)
-
- if not elapsed then
- -- failed to acquire lock, still proceed normally to external_lookup
- -- unlock() might fail.
- ngx.log(ngx.ERR, "failed to acquire the lock: ", err)
- self.lock_status = 'ERROR'
- -- _unlock won't try to unlock() without a valid lock
- self.lock = nil
- else
- -- lock acquired successfuly
-
- if elapsed > 0 then
-
- -- elapsed > 0 => waited lock (other thread might have :set() the data)
- -- (more likely to get a HIT on cache_load 2)
- self.lock_status = 'WAITED'
-
- else
-
- -- elapsed == 0 => immediate lock
- -- it is less likely to get a HIT on cache_load 2
- -- but still perform it (race condition cases)
- self.lock_status = 'IMMEDIATE'
- end
-
- -- perform cache_load 2
- data, flags = _get(self, key)
- if data then
- -- hit2 : process cache hit
-
- self.hit2 = true
-
- -- unlock before de-serializing cached data
- _unlock(self)
- data = _process_cached_data(self, data, flags)
- return _return(self, data)
- end
-
- -- continue to external lookup
- end
-
- -- perform external lookup
- data, err = self.callbacks.external_lookup()
-
- if data then
- -- succ: save positive and return the data
-
- _save_positive(self, key, data)
- return _return(self, data)
- else
- ngx.log(ngx.WARN, 'external lookup failed: ', err)
- end
-
- -- external lookup failed
- -- attempt to load stale data
- data, flags = _get_stale(self)
- if data and not _is_empty(data, flags) then
- -- hit_stale + valid (positive) data
-
- flags = _save_actualize(self, key, data, flags)
- -- unlock before de-serializing data
- _unlock(self)
- data = _process_cached_data(self, data, flags)
- return _return(self, data)
- end
-
- if DEBUG and data then
- -- there is data, but it failed _is_empty() => stale negative data
- print('STALE_NEGATIVE data => cache as a new HIT_NEGATIVE')
- end
-
- -- nothing has worked, save negative and return empty
- _save_negative(self, key)
- return _return(self, nil)
-end
-M.load = load
-
-return M \ No newline at end of file