Spike: SchemaWatcher keeps the _internal catalog current off the request path

Per-database schema_watch modes (owned / external / immutable):
- write path reads PRAGMA schema_version after every write task and
  rebuilds that one database's catalog before the write returns
- external files are polled with a stat() fingerprint prefilter
  (racily-clean fingerprints re-verified), PRAGMA schema_version only on
  change, rebuild only when the version moved or the inode changed
- immutable databases are scanned once
- remove_database deletes its catalog rows explicitly; startup deletes
  rows for databases that are not attached (no per-refresh full scans)
- catalog reads use one short-lived connection per database inside one
  read transaction; shared-cache memory databases go through their
  write thread
- replaced/deleted files invalidate cached connections (generation)
- fingerprints persisted in catalog_databases.fingerprint (migration
  0002) so restarts skip unchanged files
- new setting schema_watch_interval_ms (default 1000, 0 = no polling)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwkLo2rJWidpkDVT3HkvNv
This commit is contained in:
Claude 2026-10-04 03:18:16 +00:00
commit 7a3c534ced
No known key found for this signature in database
12 changed files with 1727 additions and 158 deletions

View file

@ -563,6 +563,33 @@ You can do that like so::
datasette mydatabase.db --setting base_url /tools/datasette/
.. _setting_schema_watch_interval_ms:
schema_watch_interval_ms
~~~~~~~~~~~~~~~~~~~~~~~~
.. [[[cog
setting_default(cog, "schema_watch_interval_ms")
.. ]]]
Default: ``1000``
.. [[[end]]]
How often, in milliseconds, Datasette checks database files that other processes may change for schema changes, so that the ``_internal`` catalog tables stay current. Each check is a ``stat()`` of the database file and its ``-wal`` and ``-journal`` files; a database is only opened if those changed.
Databases passed on the command line are checked this way by default. Databases added by plugins using ``datasette.add_database()`` are ``owned``: Datasette detects their schema changes when it writes to them and never polls them. Immutable databases are scanned once at startup. You can override the mode for a database in ``datasette.yaml``:
.. code-block:: yaml
databases:
mydatabase:
schema_watch: owned # or external, or immutable
Set this to ``0`` to turn polling off::
datasette mydatabase.db --setting schema_watch_interval_ms 0
.. _setting_secret:
Configuring the secret