summaryrefslogtreecommitdiffstats
path: root/server/resty/session/storage/shm.lua
blob: 6f81435ee8533a6ebdf28139ee893f0af8809a46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
local lock         = require "resty.lock"

local setmetatable = setmetatable
local tonumber     = tonumber
local concat       = table.concat
local var          = ngx.var
local shared       = ngx.shared

local function enabled(value)
    if value == nil then return nil end
    return value == true or (value == "1" or value == "true" or value == "on")
end

local function ifnil(value, default)
    if value == nil then
        return default
    end

    return enabled(value)
end

local defaults   = {
    store        = var.session_shm_store                       or "sessions",
    uselocking   = enabled(var.session_shm_uselocking          or true),
    lock         = {
        exptime  = tonumber(var.session_shm_lock_exptime,  10) or 30,
        timeout  = tonumber(var.session_shm_lock_timeout,  10) or 5,
        step     = tonumber(var.session_shm_lock_step,     10) or 0.001,
        ratio    = tonumber(var.session_shm_lock_ratio,    10) or 2,
        max_step = tonumber(var.session_shm_lock_max_step, 10) or 0.5,
    }
}

local storage = {}

storage.__index = storage

function storage.new(session)
    local config = session.shm            or defaults
    local store  = config.store           or defaults.store
    local locking = ifnil(config.uselocking, defaults.uselocking)

    local self = {
        store      = shared[store],
        uselocking = locking,
    }

    if locking then
        local lock_opts = config.lock                      or defaults.lock
        local opts      = {
            exptime     = tonumber(lock_opts.exptime,  10) or defaults.exptime,
            timeout     = tonumber(lock_opts.timeout,  10) or defaults.timeout,
            step        = tonumber(lock_opts.step,     10) or defaults.step,
            ratio       = tonumber(lock_opts.ratio,    10) or defaults.ratio,
            max_step    = tonumber(lock_opts.max_step, 10) or defaults.max_step,
        }
        self.lock = lock:new(store, opts)
    end

    return setmetatable(self, storage)
end

function storage:open(id, keep_lock)
    if self.uselocking then
        local ok, err = self.lock:lock(concat{ id, ".lock" })
        if not ok then
            return nil, err
        end
    end

    local data, err = self.store:get(id)

    if self.uselocking and (err or not data or not keep_lock) then
        self.lock:unlock()
    end

    return data, err
end

function storage:start(id)
    if self.uselocking then
        return self.lock:lock(concat{ id, ".lock" })
    end

    return true
end

function storage:save(id, ttl, data, close)
    local ok, err = self.store:set(id, data, ttl)
    if close and self.uselocking then
        self.lock:unlock()
    end

    return ok, err
end

function storage:close()
    if self.uselocking then
        self.lock:unlock()
    end

    return true
end

function storage:destroy(id)
    self.store:delete(id)

    if self.uselocking then
        self.lock:unlock()
    end

    return true
end

function storage:ttl(id, lifetime, close)
    local ok, err = self.store:expire(id, lifetime)

    if close and self.uselocking then
        self.lock:unlock()
    end

    return ok, err
end

return storage