mirror of
https://github.com/simonw/datasette.git
synced 2026-09-03 07:04:16 +02:00
Fold the wrapper-reorder release notes into the lifecycle docs
Rolled down from the stack's docs-only tip PR: the lifecycle section now states that startup completes before plugin asgi_wrapper middleware sees any request, and the RELEASE_NOTES_DRAFT_05.md scratch file is gone - its content lands in the changelog in the tasks-endpoint PR at the top of the stack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012U7coQfVu8nK2R4q2mCULA
This commit is contained in:
parent
130f9ac4be
commit
89ad91da27
2 changed files with 1 additions and 25 deletions
|
|
@ -1,24 +0,0 @@
|
|||
# Release notes draft — ticket 05 (wrapper reorder)
|
||||
|
||||
Scratch file: content to be folded into `docs/changelog.rst` by the final
|
||||
docs PR in this stack, which also deletes this file. Not part of the shipped
|
||||
docs on its own.
|
||||
|
||||
## Plugin hooks
|
||||
|
||||
- Plugin `asgi_wrapper` middleware now always runs **after** Datasette
|
||||
startup has completed. Wrappers can rely on startup hooks — including
|
||||
internal-database migrations run by other plugins' `startup()` hooks —
|
||||
having already executed before their code sees an `http` or `websocket`
|
||||
ASGI scope. This applies on every deployment path: behind a real ASGI
|
||||
lifespan-aware server, and on the first-request fallback used by bare
|
||||
`app()` embedding and test clients that never send lifespan events.
|
||||
- Short-circuiting wrappers — ones that return a response without calling
|
||||
the wrapped application, such as an auth plugin returning a 401/403 or a
|
||||
CORS plugin answering a preflight request — no longer defer startup
|
||||
indefinitely. Startup now runs unconditionally before any wrapper sees
|
||||
the scope, so it can no longer be skipped by requests that never reach
|
||||
the inner app.
|
||||
- `lifespan` scopes are unaffected by this change and continue to flow
|
||||
through plugin `asgi_wrapper` middleware exactly as before, so plugins
|
||||
that inspect or wrap lifespan events keep working unmodified.
|
||||
|
|
@ -1426,7 +1426,7 @@ Three trigger paths
|
|||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- **``datasette serve`` (CLI)** — startup and ``uvicorn.Server.serve()`` both run inside a single ``asyncio.run()`` call, so there is exactly one event loop for the whole life of the process.
|
||||
- **ASGI lifespan** — ``Datasette.app()`` wires startup and background-task launch into the ``on_startup`` list, and shutdown into the ``on_shutdown`` list, of an internal ``AsgiLifespan`` wrapper. A spec-compliant ASGI server (uvicorn, hypercorn, and others) sends the ``lifespan.startup`` message and waits for ``lifespan.startup.complete`` before delivering any ``http`` or ``websocket`` scope, so startup — including every plugin's own internal-database migrations — is guaranteed to have finished before any request reaches Datasette. If a ``startup`` hook raises, ``AsgiLifespan`` sends ``lifespan.startup.failed`` with the exception message instead of hanging or crashing ambiguously, so the host can abort the boot cleanly.
|
||||
- **ASGI lifespan** — ``Datasette.app()`` wires startup and background-task launch into the ``on_startup`` list, and shutdown into the ``on_shutdown`` list, of an internal ``AsgiLifespan`` wrapper. A spec-compliant ASGI server (uvicorn, hypercorn, and others) sends the ``lifespan.startup`` message and waits for ``lifespan.startup.complete`` before delivering any ``http`` or ``websocket`` scope, so startup — including every plugin's own internal-database migrations — is guaranteed to have finished before any request reaches Datasette, including requests seen by plugin :ref:`asgi_wrapper <plugin_asgi_wrapper>` middleware. If a ``startup`` hook raises, ``AsgiLifespan`` sends ``lifespan.startup.failed`` with the exception message instead of hanging or crashing ambiguously, so the host can abort the boot cleanly.
|
||||
- **First-request fallback** — an internal ``AsgiRunOnFirstRequest`` wrapper runs the same startup work as a safety net for hosts that never send ASGI lifespan events at all: some ASGI mounts, a bare ``app()`` embedded inside another framework, and :ref:`datasette.client <internals_datasette_client>` / test clients, which drive requests directly over ``httpx.ASGITransport`` without ever emitting ``lifespan.startup``. It runs startup exactly once, the first time any non-lifespan scope arrives, guarded by a lock so that concurrent early requests can't run it twice.
|
||||
|
||||
All three paths call the same idempotent internal methods, so it is safe for more than one of them to fire — lifespan startup completing and then a first request arriving afterwards is a no-op the second time. A host that never sends lifespan events and never goes through the CLI degrades to first-request timing: startup runs on the first request instead of before it, exactly as Datasette always worked prior to this lifecycle guarantee. This is a deliberate fallback rather than a regression — see :ref:`datasette_add_background_task` for how to opt out of launching background tasks (the ``--get`` CLI path) or drive startup and launch explicitly (tests, headless embedders).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue