Compare commits
No commits in common. "9201a2ed5307dedddf4fa13b0d27fb770aa20483" and "5cd190c5c197cf935bbd32fc852ed5809a64a619" have entirely different histories.
9201a2ed53
...
5cd190c5c1
|
|
@ -179,56 +179,6 @@ racing the 0.5s bound could drop an `errors` entry
|
||||||
`Portal._final_result_msg`/ctx queue state instead of
|
`Portal._final_result_msg`/ctx queue state instead of
|
||||||
time-bounding.
|
time-bounding.
|
||||||
|
|
||||||
## Step-B outcome (2026-07-02, done in tree)
|
|
||||||
|
|
||||||
Step A landed as `5cd190c5` (code) + `99310269` (docs).
|
|
||||||
Step B implemented on top (uncommitted):
|
|
||||||
|
|
||||||
- `._ria_nursery` is GONE — the inner
|
|
||||||
`async with (collapse_eg(), trio.open_nursery() as
|
|
||||||
ria_nursery)` layer in
|
|
||||||
`_open_and_supervise_one_cancels_all_nursery` is deleted;
|
|
||||||
`da_nursery` is now the single nursery for ALL subactors.
|
|
||||||
- `ActorNursery.__init__` drops the `ria_nursery` param +
|
|
||||||
the `self._ria_nursery` attr; `start_actor()` drops its
|
|
||||||
`nursery=` escape-hatch param (uses `self._da_nursery`
|
|
||||||
directly).
|
|
||||||
- `._cancel_after_result_on_exit` STAYS — it's the
|
|
||||||
ria-child discriminator for `_reap_ria_portals()`.
|
|
||||||
|
|
||||||
Deliberately NOT done (deferred to its own higher-risk PR,
|
|
||||||
flagged with a TODO at the outer `except`): merging the two
|
|
||||||
error handlers into one. Rationale — collapsing the empty
|
|
||||||
nursery is provably behavior-preserving (a zero-task
|
|
||||||
`trio.open_nursery()` only adds a checkpoint), whereas the
|
|
||||||
inner `except BaseException` (swallow-into-`errors`) and
|
|
||||||
outer `except (...)` (re-raise, safety-net for the inner
|
|
||||||
handler's own non-shielded awaits) have DIFFERENT
|
|
||||||
semantics; merging changes error/cancel propagation and
|
|
||||||
wants isolated review + its own gate. Both handlers are
|
|
||||||
kept, now nested directly under the single nursery.
|
|
||||||
|
|
||||||
Why the collapse is safe: post-step-A NOTHING spawns into
|
|
||||||
`ria_nursery` (its only reader, `run_in_actor`'s
|
|
||||||
`nursery=self._ria_nursery`, was removed in A; the stored
|
|
||||||
attr was never read again). So the layer was pure dead
|
|
||||||
weight.
|
|
||||||
|
|
||||||
Gate (trio backend, all 0-failure):
|
|
||||||
- targeted set (`test_cancellation test_spawning test_local
|
|
||||||
test_rpc test_to_actor`) = 49 passed, 1 xfailed.
|
|
||||||
- tail set (`test_reg_err_types remote_exc_relay
|
|
||||||
resource_cache ringbuf root_infect_asyncio root_runtime
|
|
||||||
runtime shm task_broadcasting trioisms trionics/`) = 63
|
|
||||||
passed, 1 skipped, 5 xfailed.
|
|
||||||
- full-suite head ~73% (subdirs + `test_2way`..`test_pubsub`)
|
|
||||||
= 303 passed before the known-flaky `test_dynamic_pub_sub`
|
|
||||||
TooSlowError stall (pre-existing; same hang in the step-A
|
|
||||||
full run). Suite ran slow this session (~13min vs 555s
|
|
||||||
cold, likely thermal from back-to-back runs), never
|
|
||||||
completing within an 800s bound — but split across the
|
|
||||||
above three runs EVERY module passed under step B.
|
|
||||||
|
|
||||||
## Verification gate
|
## Verification gate
|
||||||
|
|
||||||
- `tests/test_cancellation.py test_spawning.py test_local.py
|
- `tests/test_cancellation.py test_spawning.py test_local.py
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
---
|
|
||||||
model: claude-opus-4-8
|
|
||||||
service: claude
|
|
||||||
session: c7860302-6a33-4d92-821c-0807d68be464
|
|
||||||
timestamp: 2026-07-02T17:22:33Z
|
|
||||||
git_ref: 5cd190c5
|
|
||||||
scope: code
|
|
||||||
substantive: true
|
|
||||||
raw_file: 20260702T172233Z_5cd190c5_prompt_io.raw.md
|
|
||||||
---
|
|
||||||
|
|
||||||
## Prompt
|
|
||||||
|
|
||||||
Branched session (`to_actor_subpkg_POST_WEEKLY_CUTOFF_2OPUS`,
|
|
||||||
now on Opus 4.8) continuing the `._ria_nursery` removal after
|
|
||||||
step A committed + pushed (`5cd190c5`/`99310269`):
|
|
||||||
|
|
||||||
> ok continue where your superior left off please
|
|
||||||
|
|
||||||
then, after the commit-split discussion:
|
|
||||||
|
|
||||||
> yup committed and pushed so take a look ma fren.
|
|
||||||
|
|
||||||
i.e. green-lighting step-B prep against the committed step-A
|
|
||||||
tree.
|
|
||||||
|
|
||||||
## Response summary
|
|
||||||
|
|
||||||
Step B of the `._ria_nursery` removal (issue #477): delete
|
|
||||||
the now-vestigial secondary nursery,
|
|
||||||
|
|
||||||
- collapse the inner `async with (collapse_eg(),
|
|
||||||
trio.open_nursery() as ria_nursery)` layer in
|
|
||||||
`_open_and_supervise_one_cancels_all_nursery` — `da_nursery`
|
|
||||||
becomes the single nursery for ALL subactors.
|
|
||||||
- `ActorNursery.__init__` drops the `ria_nursery` param + the
|
|
||||||
`self._ria_nursery` attr; `start_actor()` drops its
|
|
||||||
`nursery=` escape-hatch param.
|
|
||||||
- `._cancel_after_result_on_exit` kept (ria-child
|
|
||||||
discriminator for `_reap_ria_portals()`).
|
|
||||||
|
|
||||||
Verified behavior-preserving via a first-principles argument
|
|
||||||
(zero-task nursery = a bare checkpoint) + the targeted gate
|
|
||||||
(`test_cancellation test_spawning test_local test_rpc
|
|
||||||
test_to_actor` = 49 passed, 1 xfailed on trio). The two
|
|
||||||
error handlers were deliberately NOT merged — that changes
|
|
||||||
propagation semantics and is deferred to its own PR (TODO
|
|
||||||
left at the outer `except`).
|
|
||||||
|
|
||||||
## Files changed
|
|
||||||
|
|
||||||
- `tractor/runtime/_supervise.py` — collapse the ria nursery
|
|
||||||
layer + drop the ctor/`start_actor` params + refresh the
|
|
||||||
now-stale nursery comments
|
|
||||||
|
|
||||||
## Human edits
|
|
||||||
|
|
||||||
None yet — committed via the drafted
|
|
||||||
`.claude/git_commit_msg_ria_step_b.md` (user-driven
|
|
||||||
`git commit --edit`).
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
---
|
|
||||||
model: claude-opus-4-8
|
|
||||||
service: claude
|
|
||||||
timestamp: 2026-07-02T17:22:33Z
|
|
||||||
git_ref: 5cd190c5
|
|
||||||
diff_cmd: git diff 5cd190c5..wkt/to_actor_subpkg
|
|
||||||
---
|
|
||||||
|
|
||||||
# Raw AI output (diff-ref mode)
|
|
||||||
|
|
||||||
Step-B code lives on `wkt/to_actor_subpkg` after `5cd190c5`;
|
|
||||||
per diff-ref mode the verbatim content is reachable via the
|
|
||||||
pointer below.
|
|
||||||
|
|
||||||
## Generated files
|
|
||||||
|
|
||||||
> `git diff 5cd190c5..wkt/to_actor_subpkg -- tractor/runtime/_supervise.py`
|
|
||||||
|
|
||||||
- `ActorNursery.__init__`: `ria_nursery` param removed;
|
|
||||||
`self._ria_nursery = ria_nursery` block deleted;
|
|
||||||
`_cancel_after_result_on_exit` comment refreshed.
|
|
||||||
- `start_actor()`: `nursery=` param removed; body uses
|
|
||||||
`self._da_nursery.start(...)` directly.
|
|
||||||
- `_open_and_supervise_one_cancels_all_nursery()`: the inner
|
|
||||||
`async with (collapse_eg(), trio.open_nursery() as
|
|
||||||
ria_nursery)` layer removed; `an = ActorNursery(actor,
|
|
||||||
da_nursery, errors)` constructed once under the single
|
|
||||||
`da_nursery`; the inner-try body de-indented one level;
|
|
||||||
both error handlers retained; the da-nursery lead comment
|
|
||||||
and the outer-`except` TODO refreshed to describe the
|
|
||||||
single-nursery reality + flag the (deferred) handler-merge.
|
|
||||||
|
|
||||||
> `git diff 5cd190c5..wkt/to_actor_subpkg -- ai/conc-anal/ria_nursery_removal_plan.md`
|
|
||||||
|
|
||||||
Added a "Step-B outcome" section (collapse rationale,
|
|
||||||
handler-merge deferral, safety argument, gate result).
|
|
||||||
|
|
||||||
## Test runs (verbatim)
|
|
||||||
|
|
||||||
```
|
|
||||||
targeted gate (trio):
|
|
||||||
tests/test_cancellation.py tests/test_spawning.py
|
|
||||||
tests/test_local.py tests/test_rpc.py tests/test_to_actor.py
|
|
||||||
-> 49 passed, 1 xfailed in 88.62s
|
|
||||||
|
|
||||||
signature checks:
|
|
||||||
ActorNursery.__init__ params: ['self', 'actor', 'da_nursery', 'errors']
|
|
||||||
start_actor has nursery param: False
|
|
||||||
|
|
||||||
full-suite gate: in progress (see stepB_full.log)
|
|
||||||
```
|
|
||||||
|
|
@ -199,6 +199,7 @@ class ActorNursery:
|
||||||
self,
|
self,
|
||||||
# TODO: maybe def these as fields of a struct looking type?
|
# TODO: maybe def these as fields of a struct looking type?
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
|
ria_nursery: trio.Nursery,
|
||||||
da_nursery: trio.Nursery,
|
da_nursery: trio.Nursery,
|
||||||
errors: dict[tuple[str, str], BaseException],
|
errors: dict[tuple[str, str], BaseException],
|
||||||
|
|
||||||
|
|
@ -232,12 +233,14 @@ class ActorNursery:
|
||||||
# and syncing purposes to any actor opened nurseries.
|
# and syncing purposes to any actor opened nurseries.
|
||||||
self._implicit_runtime_started: bool = False
|
self._implicit_runtime_started: bool = False
|
||||||
|
|
||||||
|
# TODO: remove the `.run_in_actor()` API and thus this 2ndary
|
||||||
|
# nursery when that API get's moved outside this primitive!
|
||||||
|
self._ria_nursery = ria_nursery
|
||||||
|
|
||||||
# TODO, factor this into a .hilevel api!
|
# TODO, factor this into a .hilevel api!
|
||||||
#
|
#
|
||||||
# portals spawned with ``run_in_actor()`` are
|
# portals spawned with ``run_in_actor()`` are
|
||||||
# cancelled when their "main" result arrives. Reaped by
|
# cancelled when their "main" result arrives
|
||||||
# `_reap_ria_portals()` at nursery-block exit now that
|
|
||||||
# the 2ndary `._ria_nursery` is gone (see issue #477).
|
|
||||||
self._cancel_after_result_on_exit: set = set()
|
self._cancel_after_result_on_exit: set = set()
|
||||||
|
|
||||||
# trio.Nursery-like cancel (request) statuses
|
# trio.Nursery-like cancel (request) statuses
|
||||||
|
|
@ -295,6 +298,11 @@ class ActorNursery:
|
||||||
debug_mode: bool|None = None,
|
debug_mode: bool|None = None,
|
||||||
infect_asyncio: bool = False,
|
infect_asyncio: bool = False,
|
||||||
inherit_parent_main: bool = True,
|
inherit_parent_main: bool = True,
|
||||||
|
|
||||||
|
# TODO: ideally we can rm this once we no longer have
|
||||||
|
# a `._ria_nursery` since the dependent APIs have been
|
||||||
|
# removed!
|
||||||
|
nursery: trio.Nursery|None = None,
|
||||||
proc_kwargs: dict[str, typing.Any] | None = None,
|
proc_kwargs: dict[str, typing.Any] | None = None,
|
||||||
|
|
||||||
) -> Portal:
|
) -> Portal:
|
||||||
|
|
@ -356,8 +364,10 @@ class ActorNursery:
|
||||||
|
|
||||||
# start a task to spawn a process
|
# start a task to spawn a process
|
||||||
# blocks until process has been started and a portal setup
|
# blocks until process has been started and a portal setup
|
||||||
|
nursery: trio.Nursery = nursery or self._da_nursery
|
||||||
|
|
||||||
# XXX: the type ignore is actually due to a `mypy` bug
|
# XXX: the type ignore is actually due to a `mypy` bug
|
||||||
return await self._da_nursery.start( # type: ignore
|
return await nursery.start( # type: ignore
|
||||||
partial(
|
partial(
|
||||||
_spawn.new_proc,
|
_spawn.new_proc,
|
||||||
name,
|
name,
|
||||||
|
|
@ -629,22 +639,37 @@ async def _open_and_supervise_one_cancels_all_nursery(
|
||||||
# the collection of errors retreived from spawned sub-actors
|
# the collection of errors retreived from spawned sub-actors
|
||||||
errors: dict[tuple[str, str], BaseException] = {}
|
errors: dict[tuple[str, str], BaseException] = {}
|
||||||
|
|
||||||
# The single "daemon actor" nursery into which ALL subactors
|
# This is the outermost level "deamon actor" nursery. It is awaited
|
||||||
# are spawned — both `.start_actor()` daemons AND
|
# **after** the below inner "run in actor nursery". This allows for
|
||||||
# `.run_in_actor()` one-shots. The latter's result-reaping now
|
# handling errors that are generated by the inner nursery in
|
||||||
# runs via `_reap_ria_portals()` at block-exit rather than a
|
# a supervisor strategy **before** blocking indefinitely to wait for
|
||||||
# 2ndary `._ria_nursery` (see the #477 removal); errors from
|
# actors spawned in "daemon mode" (aka started using
|
||||||
# this nursery bubble up to the caller.
|
# `ActorNursery.start_actor()`).
|
||||||
|
|
||||||
|
# errors from this daemon actor nursery bubble up to caller
|
||||||
async with (
|
async with (
|
||||||
collapse_eg(),
|
collapse_eg(),
|
||||||
trio.open_nursery() as da_nursery,
|
trio.open_nursery() as da_nursery,
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
# This is the inner level "run in actor" nursery. It is
|
||||||
|
# awaited first since actors spawned in this way (using
|
||||||
|
# `ActorNusery.run_in_actor()`) are expected to only
|
||||||
|
# return a single result and then complete (i.e. be canclled
|
||||||
|
# gracefully). Errors collected from these actors are
|
||||||
|
# immediately raised for handling by a supervisor strategy.
|
||||||
|
# As such if the strategy propagates any error(s) upwards
|
||||||
|
# the above "daemon actor" nursery will be notified.
|
||||||
|
async with (
|
||||||
|
collapse_eg(),
|
||||||
|
trio.open_nursery() as ria_nursery,
|
||||||
):
|
):
|
||||||
an = ActorNursery(
|
an = ActorNursery(
|
||||||
actor,
|
actor,
|
||||||
|
ria_nursery,
|
||||||
da_nursery,
|
da_nursery,
|
||||||
errors
|
errors
|
||||||
)
|
)
|
||||||
try:
|
|
||||||
try:
|
try:
|
||||||
# spawning of actors happens in the caller's scope
|
# spawning of actors happens in the caller's scope
|
||||||
# after we yield upwards
|
# after we yield upwards
|
||||||
|
|
@ -760,14 +785,15 @@ async def _open_and_supervise_one_cancels_all_nursery(
|
||||||
ria_children=ria_children,
|
ria_children=ria_children,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Safety-net handler for anything escaping the inner
|
# ria_nursery scope end
|
||||||
# `except BaseException` above (e.g. a `trio.Cancelled`
|
|
||||||
# during its non-shielded debugger-wait, or a failure
|
# TODO: this is the handler around the ``.run_in_actor()``
|
||||||
# inside the shielded teardown itself). Post-#477 the
|
# nursery. Ideally we can drop this entirely in the future as
|
||||||
# 2ndary `._ria_nursery` (and its dedicated handler) is
|
# the whole ``.run_in_actor()`` API should be built "on top of"
|
||||||
# gone; this now guards the single daemon nursery scope.
|
# this lower level spawn-request-cancel "daemon actor" API where
|
||||||
# TODO: can this be merged into the inner handler now that
|
# a local in-actor task nursery is used with one-to-one task
|
||||||
# there's only one nursery? (higher-risk, own PR)
|
# + `await Portal.run()` calls and the results/errors are
|
||||||
|
# handled directly (inline) and errors by the local nursery.
|
||||||
except (
|
except (
|
||||||
Exception,
|
Exception,
|
||||||
BaseExceptionGroup,
|
BaseExceptionGroup,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue