Comparison

plugins/mod_storage_memory.lua @ 9293:0a751835627d

mod_storage_memory: Import from prosody-modules 4c3230c22c18
author Matthew Wild <mwild1@gmail.com>
date Wed, 12 Sep 2018 13:44:03 +0100
child 9340:368b092bf4bf
comparison
equal deleted inserted replaced
9292:d5f798efb1ba 9293:0a751835627d
1 local serialize = require "util.serialization".serialize;
2 local envload = require "util.envload".envload;
3 local st = require "util.stanza";
4 local is_stanza = st.is_stanza or function (s) return getmetatable(s) == st.stanza_mt end
5
6 local auto_purge_enabled = module:get_option_boolean("storage_memory_temporary", false);
7 local auto_purge_stores = module:get_option_set("storage_memory_temporary_stores", {});
8
9 local memory = setmetatable({}, {
10 __index = function(t, k)
11 local store = module:shared(k)
12 t[k] = store;
13 return store;
14 end
15 });
16
17 local function NULL() return nil end
18
19 local function _purge_store(self, username)
20 self.store[username or NULL] = nil;
21 return true;
22 end
23
24 local keyval_store = {};
25 keyval_store.__index = keyval_store;
26
27 function keyval_store:get(username)
28 return (self.store[username or NULL] or NULL)();
29 end
30
31 function keyval_store:set(username, data)
32 if data ~= nil then
33 data = envload("return "..serialize(data), "@data", {});
34 end
35 self.store[username or NULL] = data;
36 return true;
37 end
38
39 keyval_store.purge = _purge_store;
40
41 local archive_store = {};
42 archive_store.__index = archive_store;
43
44 function archive_store:append(username, key, value, when, with)
45 if type(when) ~= "number" then
46 when, with, value = value, when, with;
47 end
48 if is_stanza(value) then
49 value = st.preserialize(value);
50 value = envload("return xml"..serialize(value), "@stanza", { xml = st.deserialize })
51 else
52 value = envload("return "..serialize(value), "@data", {});
53 end
54 local a = self.store[username or NULL];
55 if not a then
56 a = {};
57 self.store[username or NULL] = a;
58 end
59 local i = #a+1;
60 local v = { key = key, when = when, with = with, value = value };
61 if not key then
62 key = tostring(a):match"%x+$"..tostring(v):match"%x+$";
63 v.key = key;
64 end
65 if a[key] then
66 table.remove(a, a[key]);
67 end
68 a[i] = v;
69 a[key] = i;
70 return key;
71 end
72
73 local function archive_iter (a, start, stop, step, limit, when_start, when_end, match_with)
74 local item, when, with;
75 local count = 0;
76 coroutine.yield(true); -- Ready
77 for i = start, stop, step do
78 item = a[i];
79 when, with = item.when, item.with;
80 if when >= when_start and when_end >= when and (not match_with or match_with == with) then
81 coroutine.yield(item.key, item.value(), when, with);
82 count = count + 1;
83 if limit and count >= limit then return end
84 end
85 end
86 end
87
88 function archive_store:find(username, query)
89 local a = self.store[username or NULL] or {};
90 local start, stop, step = 1, #a, 1;
91 local qstart, qend, qwith = -math.huge, math.huge;
92 local limit;
93 if query then
94 module:log("debug", "query included")
95 if query.reverse then
96 start, stop, step = stop, start, -1;
97 if query.before then
98 start = a[query.before];
99 end
100 elseif query.after then
101 start = a[query.after];
102 end
103 limit = query.limit;
104 qstart = query.start or qstart;
105 qend = query["end"] or qend;
106 qwith = query.with;
107 end
108 if not start then return nil, "invalid-key"; end
109 local iter = coroutine.wrap(archive_iter);
110 iter(a, start, stop, step, limit, qstart, qend, qwith);
111 return iter;
112 end
113
114 function archive_store:delete(username, query)
115 if not query or next(query) == nil then
116 self.store[username or NULL] = nil;
117 return true;
118 end
119 local old = self.store[username or NULL];
120 if not old then return true; end
121 local qstart = query.start or -math.huge;
122 local qend = query["end"] or math.huge;
123 local qwith = query.with;
124 local new = {};
125 self.store[username or NULL] = new;
126 local t;
127 for i = 1, #old do
128 i = old[i];
129 t = i.when;
130 if not(qstart >= t and qend <= t and (not qwith or i.with == qwith)) then
131 self:append(username, i.key, i.value, t, i.with);
132 end
133 end
134 if #new == 0 then
135 self.store[username or NULL] = nil;
136 end
137 return true;
138 end
139
140 archive_store.purge = _purge_store;
141
142 local stores = {
143 keyval = keyval_store;
144 archive = archive_store;
145 }
146
147 local driver = {};
148
149 function driver:open(store, typ) -- luacheck: ignore 212/self
150 local store_mt = stores[typ or "keyval"];
151 if store_mt then
152 return setmetatable({ store = memory[store] }, store_mt);
153 end
154 return nil, "unsupported-store";
155 end
156
157 if auto_purge_enabled then
158 module:hook("resource-unbind", function (event)
159 local user_bare_jid = event.session.username.."@"..event.session.host;
160 if not prosody.bare_sessions[user_bare_jid] then -- User went offline
161 module:log("debug", "Clearing store for offline user %s", user_bare_jid);
162 local f, s, v;
163 if auto_purge_stores:empty() then
164 f, s, v = pairs(memory);
165 else
166 f, s, v = auto_purge_stores:items();
167 end
168
169 for store_name in f, s, v do
170 if memory[store_name] then
171 memory[store_name][event.session.username] = nil;
172 end
173 end
174 end
175 end);
176 end
177
178 module:provides("storage", driver);