Propagate unexpected `@pub` subscriber errors
Give each background subscriber runner its own teardown event and suppress only `ContextCancelled` relayed by the root actor after that portal's explicit cancellation begins. Let generic remote errors, foreign cancellation and cancellation before teardown escape the local task nursery so the test cannot pass after a subscriber fails unexpectedly. Review: PR #484 (GitHub Copilot and OpenCode) https://github.com/goodboy/tractor/pull/484#discussion_r3858426546 (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))drop_ria_nursery
parent
dd3e7482bf
commit
1d59f1963c
|
|
@ -194,14 +194,18 @@ def test_multi_actor_subs_arbiter_pub(
|
||||||
)
|
)
|
||||||
name = 'streamer'
|
name = 'streamer'
|
||||||
|
|
||||||
|
root_uid = tractor.current_actor().aid.uid
|
||||||
|
|
||||||
# spawn the two subscriber actors as daemons and run
|
# spawn the two subscriber actors as daemons and run
|
||||||
# `subs()` on each as a background task (was the legacy
|
# `subs()` on each as a background task (was the legacy
|
||||||
# `run_in_actor()`); keep the portals for the explicit
|
# `run_in_actor()`); keep the portals for the explicit
|
||||||
# `cancel_actor()` teardown below. Each runner swallows
|
# `cancel_actor()` teardown below. Each runner swallows
|
||||||
# the teardown error that `cancel_actor()` relays.
|
# only the cancellation relayed after its own teardown
|
||||||
|
# starts; every earlier or unrelated failure propagates.
|
||||||
async def _run_subs(
|
async def _run_subs(
|
||||||
portal: tractor.Portal,
|
portal: tractor.Portal,
|
||||||
which: list[str],
|
which: list[str],
|
||||||
|
teardown_started: trio.Event,
|
||||||
) -> None:
|
) -> None:
|
||||||
try:
|
try:
|
||||||
await portal.run(
|
await portal.run(
|
||||||
|
|
@ -209,11 +213,16 @@ def test_multi_actor_subs_arbiter_pub(
|
||||||
which=which,
|
which=which,
|
||||||
pub_actor_name=name,
|
pub_actor_name=name,
|
||||||
)
|
)
|
||||||
except (
|
except tractor.ContextCancelled as ctxc:
|
||||||
tractor.RemoteActorError,
|
if not (
|
||||||
tractor.ContextCancelled,
|
teardown_started.is_set()
|
||||||
|
and
|
||||||
|
ctxc.canceller == root_uid
|
||||||
):
|
):
|
||||||
pass # expected once we `cancel_actor()` below
|
raise
|
||||||
|
|
||||||
|
even_teardown_started = trio.Event()
|
||||||
|
odd_teardown_started = trio.Event()
|
||||||
|
|
||||||
even_portal = await an.start_actor(
|
even_portal = await an.start_actor(
|
||||||
'evens',
|
'evens',
|
||||||
|
|
@ -223,8 +232,18 @@ def test_multi_actor_subs_arbiter_pub(
|
||||||
'odds',
|
'odds',
|
||||||
enable_modules=[__name__],
|
enable_modules=[__name__],
|
||||||
)
|
)
|
||||||
tn.start_soon(_run_subs, even_portal, ['even'])
|
tn.start_soon(
|
||||||
tn.start_soon(_run_subs, odd_portal, ['odd'])
|
_run_subs,
|
||||||
|
even_portal,
|
||||||
|
['even'],
|
||||||
|
even_teardown_started,
|
||||||
|
)
|
||||||
|
tn.start_soon(
|
||||||
|
_run_subs,
|
||||||
|
odd_portal,
|
||||||
|
['odd'],
|
||||||
|
odd_teardown_started,
|
||||||
|
)
|
||||||
|
|
||||||
async with tractor.wait_for_actor('evens'):
|
async with tractor.wait_for_actor('evens'):
|
||||||
# block until 2nd actor is initialized
|
# block until 2nd actor is initialized
|
||||||
|
|
@ -263,12 +282,14 @@ def test_multi_actor_subs_arbiter_pub(
|
||||||
# await even_portal.result()
|
# await even_portal.result()
|
||||||
|
|
||||||
await trio.sleep(0.5)
|
await trio.sleep(0.5)
|
||||||
|
even_teardown_started.set()
|
||||||
await even_portal.cancel_actor()
|
await even_portal.cancel_actor()
|
||||||
await trio.sleep(1)
|
await trio.sleep(1)
|
||||||
|
|
||||||
if pub_actor == 'arbiter':
|
if pub_actor == 'arbiter':
|
||||||
assert 'even' not in get_topics()
|
assert 'even' not in get_topics()
|
||||||
|
|
||||||
|
odd_teardown_started.set()
|
||||||
await odd_portal.cancel_actor()
|
await odd_portal.cancel_actor()
|
||||||
|
|
||||||
if pub_actor == 'arbiter':
|
if pub_actor == 'arbiter':
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue