Supervised multi-database managers

Copy Markdown View Source

Tursox.Manager is optional and caller-supervised. It has no module-global registry or singleton; start multiple managers by pid or caller-selected names. Tenant IDs remain ordinary terms and are never converted to atoms.

{:ok, manager} = Tursox.Manager.start_link(name: MyApp.TenantDatabases, max_databases: 100)
{:ok, pool} = Tursox.Manager.open(manager, tenant_id, "data/tenant.db", pool_size: 4)

open/4, lookup/2, fetch/2, list/1, and close/3 are serialized by one manager, making duplicate-ID, canonical-path, and capacity decisions atomic before native allocation. List entries contain only ID, canonical path or :memory, journal mode, pool size, pool pid, and persistence status. Database options, SQL, parameters, rows, and secrets are excluded.

Each entry owns one database and shared-handle pool. Closing removes it from lookup before draining, so no new work is admitted. force: true is bounded and kills the pool when requested; Turso 0.7.2 cannot interrupt an already-running native future, so an existing caller may still need to unwind after force close.

A crashed persistent entry waits for its old DBConnection pool to terminate, closes the old database handle, then reopens the same canonical path. Data is preserved. Memory entries are removed after a crash because their contents cannot be recovered; callers may explicitly reopen a fresh one. Whole-manager restart does not auto-open a durable catalog.

Manager lifecycle telemetry uses [:tursox, :manager, event] and contains safe ID/path plus result classes only. Multiple managers may use overlapping tenant IDs without interference.