changeset 2023:98b4794b72e4

mod_mam: Include an in-memory fallback driver
author Kim Alvefur <zash@zash.se>
date Tue, 19 Jan 2016 17:47:47 +0100
parents 77b9c7e5fd63
children 6f4dcc723a60
files mod_mam/README.markdown mod_mam/fallback_archive.lib.lua mod_mam/mod_mam.lua
diffstat 3 files changed, 91 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/mod_mam/README.markdown	Tue Jan 19 17:47:05 2016 +0100
+++ b/mod_mam/README.markdown	Tue Jan 19 17:47:47 2016 +0100
@@ -58,6 +58,9 @@
 }
 ```
 
+If no archive-capable storage backend can be opened then an in-memory
+one will be used as fallback.
+
 Query size limits
 -----------------
 
@@ -116,12 +119,10 @@
 
   ------- ---------------
   trunk   Works
-  0.10    Works [^2]
-  0.9     Unsupported
+  0.10    Works
+  0.9     Works
   0.8     Does not work
   ------- ---------------
 
 [^1]: Might be changed to "mam" at some point
 
-[^2]: requires a storage driver with archive support, eg
-    mod\_storage\_sql in 0.10
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_mam/fallback_archive.lib.lua	Tue Jan 19 17:47:47 2016 +0100
@@ -0,0 +1,84 @@
+-- luacheck: ignore 212/self
+
+local uuid = require "util.uuid".generate;
+local store = module:shared("archive");
+local archive_store = { _provided_by = "mam"; name = "fallback"; };
+
+function archive_store:append(username, key, value, when, with)
+	local archive = store[username];
+	if not archive then
+		archive = { [0] = 0 };
+		store[username] = archive;
+	end
+	local index = (archive[0] or #archive)+1;
+	local item = { key = key, when = when, with = with, value = value };
+	if not key or archive[key] then
+		key = uuid();
+		item.key = key;
+	end
+	archive[index] = item;
+	archive[key] = index;
+	archive[0] = index;
+	return key;
+end
+
+function archive_store:find(username, query)
+	local archive = store[username] or {};
+	local start, stop, step = 1, archive[0] or #archive, 1;
+	local qstart, qend, qwith = -math.huge, math.huge;
+	local limit;
+
+	if query then
+		if query.reverse then
+			start, stop, step = stop, start, -1;
+			if query.before and archive[query.before] then
+				start = archive[query.before] - 1;
+			end
+		elseif query.after and archive[query.after] then
+			start = archive[query.after] + 1;
+		end
+		qwith = query.with;
+		limit = query.limit;
+		qstart = query.start or qstart;
+		qend = query["end"] or qend;
+	end
+
+	return function ()
+		if limit and limit <= 0 then return end
+		for i = start, stop, step do
+			local item = archive[i];
+			if (not qwith or qwith == item.with) and item.when >= qstart and item.when <= qend then
+				if limit then limit = limit - 1; end
+				start = i + step; -- Start on next item
+				return item.key, item.value, item.when, item.with;
+			end
+		end
+	end
+end
+
+function archive_store:delete(username, query)
+	if not query or next(query) == nil then
+		-- no specifics, delete everything
+		store[username] = nil;
+		return true;
+	end
+	local archive = store[username];
+	if not archive then return true; end -- no messages, nothing to delete
+
+	local start, stop, step = 1, archive[0] or #archive, 1;
+	local qstart = query.start or -math.huge;
+	local qend = query["end"] or math.huge;
+	local qwith = query.with;
+	store[username] = nil;
+	for i = 1, #archive do
+		local item = archive[i];
+		local when, with = item.when, item.when;
+		-- Add things that don't match the query
+		if not ((not qwith or qwith == item.with) and item.when >= qstart and item.when <= qend) then
+			self:append(username, item.key, item.value, when, with);
+		end
+	end
+	return true;
+end
+
+return archive_store;
--- a/mod_mam/mod_mam.lua	Tue Jan 19 17:47:05 2016 +0100
+++ b/mod_mam/mod_mam.lua	Tue Jan 19 17:47:47 2016 +0100
@@ -39,10 +39,8 @@
 local archive = assert(module:open_store(archive_store, "archive"));
 
 if archive.name == "null" then
-	module:log("debug", "Attempt to open archive storage returned null driver");
-	module:log("error", "Unable to open archive storage, no archive capable storage driver enabled?");
-	module:log("info", "See https://prosody.im/doc/storage and https://prosody.im/doc/archiving for more information");
-	return;
+	module:log("info", "Using in-memory fallback archive driver");
+	archive = module:require "fallback_archive";
 elseif not archive.find then
 	module:log("debug", "Attempt to open archive storage returned a valid driver but it does not seem to implement the storage API");
 	module:log("error", "mod_%s does not support archiving", archive._provided_by or archive.name and "storage_"..archive.name.."(?)" or "<unknown>");