fix store sessions in a signed cookie, not a second SQLite pool

The server opened two independent connection pools on the one SQLite file: the repository's, and Dream.sql_pool for Dream.sql_sessions. SQLite has a single writer lock, so two pools contending on the same file risk SQLITE_BUSY under load. Cookie sessions need no store. The session rides in a signed, encrypted cookie and survives a restart whenever HITO_SECRET is set, which the sql back end also required. The dream_session table stays in the schema so an existing file keeps its rows; it is otherwise unused.

Commit
451260bd549ddebe7c22daa4a8e18100083c97fc
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
bin/main.ml
index 6bc072b5..301ddd1e 100644..100644
@@ -67,8 +67,12 @@
67 67 Lwt_main.run
68 68 (Seed.single_user repo ~password:seed_password ~csv_path:seed_csv);
69 69 let handlers = Handlers.make ~repo () in
70 Added: (* Sessions live in a signed, encrypted cookie, not in SQLite. The
71 Added: repository already holds the one connection pool for this file; a
72 Added: second pool for sessions would contend with it for SQLite's single
73 Added: writer lock. Cookie sessions need no store, and they survive a restart
74 Added: whenever HITO_SECRET is set. *)
70 75 Dream.run ~interface:"localhost" ~port
71 76 ~error_handler:Handlers.error_handler
72 Removed: @@ Dream.logger @@ Dream.set_secret secret @@ Dream.sql_pool db_uri
73 Removed: @@ Dream.sql_sessions
77 Added: @@ Dream.logger @@ Dream.set_secret secret @@ Dream.cookie_sessions
74 78 @@ Dream.router (Handlers.routes handlers)
lib/persistence/migration_data.ml
index 56eb9ed2..9ed13f15 100644..100644
@@ -51,7 +51,9 @@
51 51 )|};
52 52 {|CREATE INDEX IF NOT EXISTS workout_by_trainee
53 53 ON workout (trainee_id, seq DESC)|};
54 Removed: (* The table Dream's sql_sessions back end expects. *)
54 Added: (* Retained from when Dream's sql_sessions back end stored sessions
55 Added: here. Sessions now live in a signed cookie. Kept so an existing
56 Added: file keeps its rows; otherwise unused. *)
55 57 {|CREATE TABLE IF NOT EXISTS dream_session (
56 58 id TEXT PRIMARY KEY,
57 59 label TEXT NOT NULL,
lib/persistence/migrations.mli
index 76d1f7ae..1c7b8877 100644..100644
@@ -6,7 +6,9 @@
6 6 Every statement still uses [IF NOT EXISTS], so a database created by an
7 7 earlier, ledger-less build stays safe.
8 8
9 Removed: Includes the [dream_session] table {!Dream.sql_sessions} expects. *)
9 Added: Includes a [dream_session] table from when sessions were stored in SQLite.
10 Added: Sessions now live in a signed cookie; the table is retained so an existing
11 Added: file keeps its rows, and is otherwise unused. *)
10 12
11 13 type migration = { version : int; name : string; statements : string list }
12 14 (** One migration: a version, a human name, and the ordered statements it runs.
lib/web/handlers.mli
index bb7e80a0..22274afc 100644..100644
@@ -17,8 +17,8 @@
17 17
18 18 val routes : t -> Dream.route list
19 19 (** The route table. Session and CSRF middleware are applied per handler. Wrap
20 Removed: with {!Dream.sql_sessions} or {!Dream.memory_sessions} and a secret at the
21 Removed: top level. *)
20 Added: with a session middleware such as {!Dream.cookie_sessions} or
21 Added: {!Dream.memory_sessions} and a secret at the top level. *)
22 22
23 23 val error_handler : Dream.error_handler
24 24 (** Renders a branded page for every failure Dream routes here, including