Documentation

Document various methods in db.lua and cache.lua
This commit is contained in:
Robin Malley 2023-06-04 21:33:02 +00:00
parent e430d9b512
commit 677f05d69c
2 changed files with 190 additions and 34 deletions

View File

@ -1,8 +1,11 @@
--[[
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
--[[ md
@name lua/cache
Implements a simple in memory read through 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")
@ -15,6 +18,68 @@ local ret = {}
local stmnt_cache, stmnt_insert_cache, stmnt_dirty_cache
--[[ cat
@name lua/cache
<h3>Schema for Cache</h3>
<p>The cache mechanism is a in-memeory sqlite3 database behind the scenes, it
can ensure consistency and atomic updates & dirtying, though it doesn't today.</p>
<table>
<caption>cache</caption>
<tr>
<th>Attributes</th>
<th>Field</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td>Primary Key</td>
<td>path</td>
<td>TEXT</td>
<td>
The logical path this text was rendered at,
before browser-specific headers (like accept-encoding)
are applied
</td>
</tr>
<tr>
<td></td>
<td>data</td>
<td>BLOB</td>
<td>
The returned result from the function passed into
cache.render(), the result must be a string, and
may contain nulls.
</td>
</tr>
<tr>
<td></td>
<td>updated</td>
<td>INTEGER</td>
<td>
The time this item was rendered at, can be used to set
a minimum update frequency. This is used so that web
scrapers don't constantly trigger re-renders of the
index page.
</td>
</tr>
<tr>
<td></td>
<td>dirty</td>
<td>INTEGER</td>
<td>
Does this page need to be re-rendered the next time it
is called? For example, an author's story could have
multiple hits, which would require rerendering their
author page to show the new hit count, but we don't
actually need to do it until someone requests the
author page. In this case, we keep the old page around
to save time trying to clear it and potentially hit
sqlite's garbage collector.
</td>
</tr>
</table>
]]
local oldconfigure = configure
function configure(...)
ret.cache = db.sqlassert(sql.open_memory())-- Expose db for testing
@ -52,7 +117,40 @@ function configure(...)
return oldconfigure(...)
end
--Render a page, with cacheing. If you need to dirty a cache, call dirty_cache()
--[[ md
@name lua/cache
### cache.render
Render a page with cacheing.
The callback will be called with no arguments, and must return a string.
Parameters:
0. pagename - {{lua/string}} - A logical string to associate with this
rendered page, this must be passed exactly into render() in order
to (potentially) retrive the cached page.
0. callback - {{lua/function}} - A function that may be called,
if it is called, it is called with no arguments, and must return a string.
The returned string may have embedded nulls.
Returns:
0. {{lua/string}} - Either the return of the passed function, or the cached
string.
Example:
cache = require("cache")
func = function()
print("Called")
return "Hello, world!"
end
print(cache.render("/test",func)) -- prints "Called", then "Hello, world!"
print(cache.render("/test",func)) -- prints "Hello, world!"
print(cache.render("/test",func)) -- prints "Hello, world!"
]]
function ret.render(pagename,callback)
stmnt_cache:bind_names{path=pagename}
local err = db.do_sql(stmnt_cache)
@ -81,8 +179,37 @@ function ret.render(pagename,callback)
return text
end
-- Dirty a cached page, causing it to be re-rendered the next time it's
-- requested. Doesn't actually delete it or anything, just sets it's dirty bit
--[[ md
@name lua/cache
### cache.dirty
Dirty a cached page, causing it to be re-rendered the next time it's
requested. Doesn't actually delete it or free memory, just sets its dirty bit.
If the page does not exists or has not been rendered yet, this function does
not error.
Parameters:
0. url - {{lua/string}} - `pagename` from the render function, the logical
string associcated with this rendered page.
No returns
Example:
cache = require("cache")
func = function()
print("Called")
return "Hello, world!"
end
print(cache.render("/test",func)) -- prints "Called", then "Hello, world!"
print(cache.render("/test",func)) -- prints "Hello, world!")
cache.dirty("/test")
print(cache.render("/test",func)) -- prints "Called", then "Hello, world!"
print(cache.render("/test",func)) -- prints "Hello, world!"
]]
function ret.dirty(url)
stmnt_dirty_cache:bind_names{
path = url

View File

@ -1,6 +1,10 @@
--[[ md
@name lua/db
## Overview
Does most of the database interaction.
Creates default empty database during configure()
Notably, holds a connection to the open sqlite3 database in .conn
]]
local sql = require("lsqlite3")
@ -11,7 +15,9 @@ local config = require("config")
local db = {}
--[[ md
@name lua/db/sqlassert
@name lua/db
### db.sqlassert
Runs an sql query and receives the 3 arguments back, prints a nice error
message on fail, and returns true on success.
@ -36,8 +42,8 @@ that error checking and assignment can all be done on a single line.
Example:
> db = require("db")
> query = db.sqlassert(db.conn:parepare("SELECT 'Hello, world!'"))
db = require("db")
query = db.sqlassert(db.conn:parepare("SELECT 'Hello, world!'"))
]]
function db.sqlassert(r, errcode, err)
if not r then
@ -51,7 +57,9 @@ function db.sqlassert(r, errcode, err)
end
--[[ md
@name lua/db/do_sql
@name lua/db
### db.do_sql
Continuously tries to perform an sql statement until it goes through. This function may call {{lua/coroutine/yield}}
@ -65,12 +73,12 @@ Returns:
Example:
> sql = require("lsqlite3")
> configure = function(...) end -- Mock smr environment
> db = require("db")
> configure()
> query = db.conn:prepare("SELECT 'Hello, world!';")
> assert(db.do_sql(query))
sql = require("lsqlite3")
configure = function(...) end -- Mock smr environment
db = require("db")
configure()
query = db.conn:prepare("SELECT 'Hello, world!';")
assert(db.do_sql(query))
]]
function db.do_sql(stmnt)
if not stmnt then error("No statement",2) end
@ -88,7 +96,9 @@ function db.do_sql(stmnt)
end
--[[ md
@name lua/db/sql_rows
@name lua/db
### db.sql_rows
Provides an iterator that loops over results in an sql statement or throws an
error, then resets the statement after the loop is done.
@ -107,12 +117,11 @@ Returns:
Example:
> db = require("db")
> query = db.conn:prepare("SELECT 'Hello, world!';")
> for row in db.sql_rows(query) do
> print(row)
> end
Hello, world!
db = require("db")
query = db.conn:prepare("SELECT 'Hello, world!';")
for row in db.sql_rows(query) do
print(row) -- prints 'Hello, world!'
end
]]
function db.sql_rows(stmnt)
@ -141,14 +150,24 @@ function db.sql_rows(stmnt)
end
end
--[[
@name lua/db/sqlbind
--[[ md
@name lua/db
Binds an argument to as statement with nice error reporting on failure. Approximatly the same as {{lsqlite/stmt/bind_names}}, but with better error reporting.
stmnt :: sql.stmnt - the prepared sql statemnet
call :: string - a string "bind" or "bind_blob"
position :: number - the argument position to bind to
data :: string - The data to bind
### db.sqlbind
Binds an argument to a prepared statement,
with nice error reporting on failure.
Wraps {{lsqlite/stmnt/bind_name}}
with better error reporting.
Parameters:
0. stmnt - {{lsqlite/stmnt}} - The prepared statement from {{sqlite/db/prepare}}
0. call - {{lua/string}} - Literal string, options are `bind` for most types, or `bind_blob` for strings that may contain embedded nulls
0. position - {{lua/number}} - The argument position to bind to, does not support named parameters
0. data - Any - the data to bind
Returns nothing
]]
function db.sqlbind(stmnt,call,position,data)
assert(call == "bind" or call == "bind_blob","Bad bind call, call was:" .. call)
@ -166,8 +185,6 @@ function db.sqlbind(stmnt,call,position,data)
end
end
local oldconfigure = configure
db.conn = db.sqlassert(sql.open(config.db))
function configure(...)
@ -191,6 +208,18 @@ function configure(...)
return oldconfigure(...)
end
--[[ md
### db.close()
Closes the database connection. Not called during normal operation, used to
assist in unit testing.
No parameters
No returns
]]
function db.close()
db.conn:close()
end