-
Notifications
You must be signed in to change notification settings - Fork 14
Make find_actor() delete stale sockaddr entries from registrar on OSError
#366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
03f458a
149b800
a0d3741
0dfa6f4
528012f
403c217
d929fb7
850219f
85457cb
b557ec2
e71eec0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,7 +60,7 @@ | |
| async def get_registry( | ||
| addr: UnwrappedAddress|None = None, | ||
| ) -> AsyncGenerator[ | ||
| Portal | LocalPortal | None, | ||
| Portal|LocalPortal|None, | ||
| None, | ||
| ]: | ||
| ''' | ||
|
|
@@ -153,29 +153,34 @@ async def query_actor( | |
| regaddr: UnwrappedAddress|None = None, | ||
|
|
||
| ) -> AsyncGenerator[ | ||
| UnwrappedAddress|None, | ||
| tuple[UnwrappedAddress|None, Portal|None], | ||
| None, | ||
| ]: | ||
|
Comment on lines
155
to
158
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Updated the docstring to document the new
|
||
| ''' | ||
| Lookup a transport address (by actor name) via querying a registrar | ||
| listening @ `regaddr`. | ||
|
|
||
| Returns the transport protocol (socket) address or `None` if no | ||
| entry under that name exists. | ||
| Yields a `tuple` of `(addr, reg_portal)` where, | ||
| - `addr` is the transport protocol (socket) address or `None` if | ||
| no entry under that name exists, | ||
| - `reg_portal` is the `Portal` to the registrar used for the | ||
| lookup (or `None` when the peer was found locally via | ||
| `get_peer_by_name()`). | ||
|
Comment on lines
155
to
+169
|
||
|
|
||
| ''' | ||
| actor: Actor = current_actor() | ||
| if ( | ||
| name == 'registrar' | ||
| and actor.is_registrar | ||
| and | ||
| actor.is_registrar | ||
|
Comment on lines
174
to
+176
|
||
| ): | ||
| raise RuntimeError( | ||
| 'The current actor IS the registry!?' | ||
| ) | ||
|
|
||
| maybe_peers: list[Channel]|None = get_peer_by_name(name) | ||
| if maybe_peers: | ||
| yield maybe_peers[0].raddr | ||
| yield maybe_peers[0].raddr, None | ||
| return | ||
|
|
||
| reg_portal: Portal | ||
|
|
@@ -188,8 +193,7 @@ async def query_actor( | |
| 'find_actor', | ||
| name=name, | ||
| ) | ||
| yield addr | ||
|
|
||
| yield addr, reg_portal | ||
|
|
||
| @acm | ||
| async def maybe_open_portal( | ||
|
|
@@ -204,15 +208,49 @@ async def maybe_open_portal( | |
| async with query_actor( | ||
| name=name, | ||
| regaddr=addr, | ||
| ) as addr: | ||
| pass | ||
| ) as (addr, reg_portal): | ||
| if not addr: | ||
| yield None | ||
| return | ||
|
|
||
| if addr: | ||
| async with _connect_chan(addr) as chan: | ||
| async with open_portal(chan) as portal: | ||
| yield portal | ||
| else: | ||
| yield None | ||
| try: | ||
| async with _connect_chan(addr) as chan: | ||
| async with open_portal(chan) as portal: | ||
| yield portal | ||
|
|
||
| # most likely we were unable to connect the | ||
| # transport and there is likely a stale entry in | ||
| # the registry actor's table, thus we need to | ||
| # instruct it to clear that stale entry and then | ||
| # more silently (pretend there was no reason but | ||
| # to) indicate that the target actor can't be | ||
| # contacted at that addr. | ||
| except OSError: | ||
| # NOTE: ensure we delete the stale entry | ||
| # from the registrar actor when available. | ||
| if reg_portal is not None: | ||
| uid: tuple[str, str]|None = await reg_portal.run_from_ns( | ||
| 'self', | ||
| 'delete_addr', | ||
| addr=addr, | ||
| ) | ||
| if uid: | ||
| log.warning( | ||
| f'Deleted stale registry entry !\n' | ||
| f'addr: {addr!r}\n' | ||
| f'uid: {uid!r}\n' | ||
| ) | ||
| else: | ||
| log.warning( | ||
| f'No registry entry found for addr: {addr!r}' | ||
| ) | ||
| else: | ||
| log.warning( | ||
| f'Connection to {addr!r} failed' | ||
| f' and no registry portal available' | ||
| f' to delete stale entry.' | ||
| ) | ||
| yield None | ||
|
|
||
|
|
||
| @acm | ||
|
|
@@ -280,7 +318,7 @@ async def find_actor( | |
| if not any(portals): | ||
| if raise_on_none: | ||
| raise RuntimeError( | ||
| f'No actor "{name}" found registered @ {registry_addrs}' | ||
| f'No actor {name!r} found registered @ {registry_addrs!r}' | ||
| ) | ||
| yield None | ||
| return | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -68,6 +68,7 @@ | |||||||||||||||||||||||||
| from types import ModuleType | ||||||||||||||||||||||||||
| import warnings | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| from bidict import bidict | ||||||||||||||||||||||||||
| import trio | ||||||||||||||||||||||||||
| from trio._core import _run as trio_runtime | ||||||||||||||||||||||||||
| from trio import ( | ||||||||||||||||||||||||||
|
|
@@ -1920,10 +1921,10 @@ def __init__( | |||||||||||||||||||||||||
| **kwargs, | ||||||||||||||||||||||||||
| ) -> None: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| self._registry: dict[ | ||||||||||||||||||||||||||
| self._registry: bidict[ | ||||||||||||||||||||||||||
| tuple[str, str], | ||||||||||||||||||||||||||
| UnwrappedAddress, | ||||||||||||||||||||||||||
| ] = {} | ||||||||||||||||||||||||||
| ] = bidict({}) | ||||||||||||||||||||||||||
| self._waiters: dict[ | ||||||||||||||||||||||||||
| str, | ||||||||||||||||||||||||||
| # either an event to sync to receiving an actor uid (which | ||||||||||||||||||||||||||
|
|
@@ -2012,7 +2013,13 @@ async def register_actor( | |||||||||||||||||||||||||
| # should never be 0-dynamic-os-alloc | ||||||||||||||||||||||||||
| await debug.pause() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| self._registry[uid] = addr | ||||||||||||||||||||||||||
| # XXX NOTE, value must also be hashable AND since | ||||||||||||||||||||||||||
| # `._registry` is a `bidict` values must be unique; use | ||||||||||||||||||||||||||
| # `.forceput()` to replace any prior (stale) entries | ||||||||||||||||||||||||||
| # that might map a different uid to the same addr (e.g. | ||||||||||||||||||||||||||
| # after an unclean shutdown or actor-restart reusing | ||||||||||||||||||||||||||
| # the same address). | ||||||||||||||||||||||||||
| self._registry.forceput(uid, tuple(addr)) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # pop and signal all waiter events | ||||||||||||||||||||||||||
| events = self._waiters.pop(name, []) | ||||||||||||||||||||||||||
|
|
@@ -2029,4 +2036,29 @@ async def unregister_actor( | |||||||||||||||||||||||||
| uid = (str(uid[0]), str(uid[1])) | ||||||||||||||||||||||||||
| entry: tuple = self._registry.pop(uid, None) | ||||||||||||||||||||||||||
| if entry is None: | ||||||||||||||||||||||||||
| log.warning(f'Request to de-register {uid} failed?') | ||||||||||||||||||||||||||
| log.warning( | ||||||||||||||||||||||||||
| f'Request to de-register {uid!r} failed?' | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| async def delete_addr( | ||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||
| addr: tuple[str, int|str], | ||||||||||||||||||||||||||
| ) -> tuple[str, str]|None: | ||||||||||||||||||||||||||
| # NOTE: `addr` arrives as a `list` over IPC | ||||||||||||||||||||||||||
| # (msgpack deserializes tuples -> lists) so | ||||||||||||||||||||||||||
| # coerce to `tuple` for the bidict hash lookup. | ||||||||||||||||||||||||||
| uid: tuple | None = self._registry.inverse.pop( | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
| addr: tuple[str, int|str], | |
| ) -> tuple[str, str]|None: | |
| # NOTE: `addr` arrives as a `list` over IPC | |
| # (msgpack deserializes tuples -> lists) so | |
| # coerce to `tuple` for the bidict hash lookup. | |
| uid: tuple | None = self._registry.inverse.pop( | |
| addr: tuple[str, int | str] | list[str | int], | |
| ) -> tuple[str, str] | None: | |
| # NOTE: `addr` arrives as a `list` over IPC | |
| # (msgpack deserializes tuples -> lists) so | |
| # coerce to `tuple` for the bidict hash lookup. | |
| uid: tuple[str, str] | None = self._registry.inverse.pop( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 response authored by
claude-code
Good catch — tightened uid annotation to tuple[str, str]|None and broadened addr param type to tuple[str, int|str]|list[str|int] to document the msgpack list-over-IPC reality (runtime coercion via tuple(addr) was already in place from b557ec20).
📎 fixed in
e71eec07
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point - added explicit
registry_addrs=[reg_addr]to both theopen_nursery()andfind_actor()calls so the test is guaranteed to use the remote registrar daemon started by thedaemonfixture.