diff options
Diffstat (limited to 'plugins/mod_storage_internal.lua')
-rw-r--r-- | plugins/mod_storage_internal.lua | 242 |
1 files changed, 167 insertions, 75 deletions
diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index fa87e495..deab7dfd 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -1,17 +1,20 @@ -local cache = require "util.cache"; -local datamanager = require "core.storagemanager".olddm; -local array = require "util.array"; -local datetime = require "util.datetime"; -local st = require "util.stanza"; -local now = require "util.time".now; -local id = require "util.id".medium; -local jid_join = require "util.jid".join; -local set = require "util.set"; +local cache = require "prosody.util.cache"; +local datamanager = require "prosody.core.storagemanager".olddm; +local array = require "prosody.util.array"; +local datetime = require "prosody.util.datetime"; +local st = require "prosody.util.stanza"; +local now = require "prosody.util.time".now; +local id = require "prosody.util.id".medium; +local jid_join = require "prosody.util.jid".join; +local set = require "prosody.util.set"; +local it = require "prosody.util.iterators"; local host = module.host; -local archive_item_limit = module:get_option_number("storage_archive_item_limit", 10000); -local archive_item_count_cache = cache.new(module:get_option("storage_archive_item_limit_cache_size", 1000)); +local archive_item_limit = module:get_option_integer("storage_archive_item_limit", 10000, 0); +local archive_item_count_cache = cache.new(module:get_option_integer("storage_archive_item_limit_cache_size", 1000, 1)); + +local use_shift = module:get_option_boolean("storage_archive_experimental_fast_delete", false); local driver = {}; @@ -121,100 +124,148 @@ function archive:append(username, key, value, when, with) return key; end +local function binary_search(haystack, test, min, max) + if min == nil then + min = 1; + end + if max == nil then + max = #haystack; + end + + local floor = math.floor; + while min < max do + local mid = floor((max + min) / 2); + + local result = test(haystack[mid]); + if result < 0 then + max = mid; + elseif result > 0 then + min = mid + 1; + else + return mid, haystack[mid]; + end + end + + return min, nil; +end + function archive:find(username, query) - local items, err = datamanager.list_load(username, host, self.store); - if not items then + local list, err = datamanager.list_open(username, host, self.store); + if not list then if err then - return items, err; + return list, err; elseif query then if query.before or query.after then return nil, "item-not-found"; end if query.total then - return function () end, 0; + return function() + end, 0; end end - return function () end; + return function() + end; + end + + local i = 0; + local iter = function() + i = i + 1; + return list[i] end - local count = nil; - local i, last_key = 0; + if query then - items = array(items); + if query.reverse then + i = #list + 1 + iter = function() + i = i - 1 + return list[i] + end + query.before, query.after = query.after, query.before; + end if query.key then - items:filter(function (item) + iter = it.filter(function(item) return item.key == query.key; - end); + end, iter); end if query.ids then local ids = set.new(query.ids); - items:filter(function (item) + iter = it.filter(function(item) return ids:contains(item.key); - end); + end, iter); end if query.with then - items:filter(function (item) + iter = it.filter(function(item) return item.with == query.with; - end); + end, iter); end if query.start then - items:filter(function (item) - local when = item.when or datetime.parse(item.attr.stamp); - return when >= query.start; - end); + if not query.reverse then + local wi, exact = binary_search(list, function(item) + local when = item.when or datetime.parse(item.attr.stamp); + return query.start - when; + end); + if exact then + i = wi - 1; + elseif wi then + i = wi; + end + else + iter = it.filter(function(item) + local when = item.when or datetime.parse(item.attr.stamp); + return when >= query.start; + end, iter); + end end if query["end"] then - items:filter(function (item) - local when = item.when or datetime.parse(item.attr.stamp); - return when <= query["end"]; - end); - end - if query.total then - count = #items; - end - if query.reverse then - items:reverse(); - if query.before then - local found = false; - for j = 1, #items do - if (items[j].key or tostring(j)) == query.before then - found = true; - i = j; - break; - end - end - if not found then - return nil, "item-not-found"; + if query.reverse then + local wi = binary_search(list, function(item) + local when = item.when or datetime.parse(item.attr.stamp); + return query["end"] - when; + end); + if wi then + i = wi + 1; end + else + iter = it.filter(function(item) + local when = item.when or datetime.parse(item.attr.stamp); + return when <= query["end"]; + end, iter); end - last_key = query.after; - elseif query.after then + end + if query.after then local found = false; - for j = 1, #items do - if (items[j].key or tostring(j)) == query.after then - found = true; - i = j; - break; + iter = it.filter(function(item) + local found_after = found; + if item.key == query.after then + found = true end - end - if not found then - return nil, "item-not-found"; - end - last_key = query.before; - elseif query.before then - last_key = query.before; + return found_after; + end, iter); + end + if query.before then + local found = false; + iter = it.filter(function(item) + if item.key == query.before then + found = true + end + return not found; + end, iter); end - if query.limit and #items - i > query.limit then - items[i+query.limit+1] = nil; + if query.limit then + iter = it.head(query.limit, iter); end end - return function () - i = i + 1; - local item = items[i]; - if not item or (last_key and item.key == last_key) then - return; + + return function() + local item = iter(); + if item == nil then + if list.close then + list:close(); + end + return end - local key = item.key or tostring(i); - local when = item.when or datetime.parse(item.attr.stamp); + local key = item.key; + local when = item.when or item.attr and datetime.parse(item.attr.stamp); local with = item.with; item.key, item.when, item.with = nil, nil, nil; item.attr.stamp = nil; @@ -222,7 +273,7 @@ function archive:find(username, query) item.attr.stamp_legacy = nil; item = st.deserialize(item); return key, item, when, with; - end, count; + end end function archive:get(username, wanted_key) @@ -297,12 +348,53 @@ function archive:users() return datamanager.users(host, self.store, "list"); end +function archive:trim(username, to_when) + local cache_key = jid_join(username, host, self.store); + local list, err = datamanager.list_open(username, host, self.store); + if not list then + if err == nil then + module:log("debug", "store already empty, can't trim"); + return 0; + end + return list, err; + end + + -- shortcut: check if the last item should be trimmed, if so, drop the whole archive + local last = list[#list].when or datetime.parse(list[#list].attr.stamp); + if last <= to_when then + if list.close then + list:close() + end + return datamanager.list_store(username, host, self.store, nil); + end + + -- luacheck: ignore 211/exact + local i, exact = binary_search(list, function(item) + local when = item.when or datetime.parse(item.attr.stamp); + return to_when - when; + end); + if list.close then + list:close() + end + -- TODO if exact then ... off by one? + if i == 1 then return 0; end + local ok, err = datamanager.list_shift(username, host, self.store, i); + if not ok then return ok, err; end + archive_item_count_cache:set(cache_key, nil); -- TODO calculate how many items are left + return i-1; +end + function archive:delete(username, query) local cache_key = jid_join(username, host, self.store); if not query or next(query) == nil then - archive_item_count_cache:set(cache_key, nil); + archive_item_count_cache:set(cache_key, nil); -- nil because we don't check if the following succeeds return datamanager.list_store(username, host, self.store, nil); end + + if use_shift and next(query) == "end" and next(query, "end") == nil then + return self:trim(username, query["end"]); + end + local items, err = datamanager.list_load(username, host, self.store); if not items then if err then |