smr/src/lua/cache.lua

96 lines
2.3 KiB
Lua

--[[
Implements a simple in-memory cache. The cache has no upper size limit, and
may cause out-of-memory errors. When this happens, the OS will kill the kore
worker process, and the kore parent process will restart with a fresh, empty
cache
]]
local sql = require("lsqlite3")
local queries = require("queries")
local util = require("util")
local ret = {}
local stmnt_cache, stmnt_insert_cache, stmnt_dirty_cache
local oldconfigure = configure
function configure(...)
local cache = util.sqlassert(sql.open_memory())
--A cache table to store rendered pages that do not need to be
--rerendered. In theory this could OOM the program eventually and start
--swapping to disk. TODO: fixme
assert(cache:exec([[
CREATE TABLE IF NOT EXISTS cache (
path TEXT PRIMARY KEY,
data BLOB,
updated INTEGER,
dirty INTEGER
);
]]))
stmnt_cache = assert(cache:prepare([[
SELECT data
FROM cache
WHERE
path = :path AND
((dirty = 0) OR (strftime('%s','now') - updated) < 20)
;
]]))
stmnt_insert_cache = assert(cache:prepare([[
INSERT OR REPLACE INTO cache (
path, data, updated, dirty
) VALUES (
:path, :data, strftime('%s','now'), 0
);
]]))
stmnt_dirty_cache = assert(cache:prepare([[
UPDATE OR IGNORE cache
SET dirty = 1
WHERE path = :path;
]]))
return oldconfigure(...)
end
--Render a page, with cacheing. If you need to dirty a cache, call dirty_cache()
function ret.render(pagename,callback)
stmnt_cache:bind_names{path=pagename}
local err = util.do_sql(stmnt_cache)
if err == sql.DONE then
stmnt_cache:reset()
--page is not cached
elseif err == sql.ROW then
local data = stmnt_cache:get_values()
stmnt_cache:reset()
return data[1]
else --sql.ERROR or sql.MISUSE
error("Failed to check cache for page " .. pagename)
end
--We didn't have the paged cached, render it
local text = callback()
--And save the data back into the cache
stmnt_insert_cache:bind_names{
path=pagename,
data=text,
}
err = util.do_sql(stmnt_insert_cache)
if err == sql.ERROR or err == sql.MISUSE then
error("Failed to update cache for page " .. pagename)
end
stmnt_insert_cache:reset()
return text
end
function ret.dirty(url)
stmnt_dirty_cache:bind_names{
path = url
}
util.do_sql(stmnt_dirty_cache)
stmnt_dirty_cache:reset()
end
function ret.close()
end
return ret