Skip to content

Conversation

@VegetarianOrc
Copy link
Contributor

What was changed

  • Update to latest Nexus API
  • Implement task cancellation for nexus tasks (not to be confused with operation cancellation)

Why?

Nexus operation handlers should be able to handle the case where a call start_operation or call to cancel_operation is interrupted. The most common occurrence of this is a timeout where the call fails to return in time. The new cancellation objects attached to the Nexus operation context allows both sync and async functions to exit early when appropriate.

These changes rely on nexus-rpc/sdk-python#31 and is the Temporal specific implementation of nexus-rpc/sdk-python#26.

Checklist

  1. How was this tested:
  • Existing Nexus tests were updated to support the new Nexus API.
  • Tests in test/nexus/test_workflow_caller_errors.py were updated to cover:
    • StartOperation cancel (sync & async flavors)
    • CancelOperation cancel
  1. Any docs updates needed?

Copy link
Member

@cretz cretz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking

class _NexusTaskCancellation(nexusrpc.handler.OperationTaskCancellation):
def __init__(self):
self._thread_evt = threading.Event()
self._aysnc_evt = asyncio.Event()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self._aysnc_evt = asyncio.Event()
self._async_evt = asyncio.Event()

async def wait_until_cancelled(self) -> None:
await self._aysnc_evt.wait()

def cancel(self, reason: Optional[str] = None) -> bool:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably no need to return a never-read value from an effectively module-private method, but meh

async def wait_until_cancelled(self) -> None:
await self._aysnc_evt.wait()

def cancel(self, reason: Optional[str] = None) -> bool:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a possibility of reason being none? Even if there is (some newer proto value on the server that we don't have an entry for yet), is there any value in defaulting this parameter (or the one on the cancel call that calls this)?

await self._aysnc_evt.wait()

def cancel(self, reason: Optional[str] = None) -> bool:
if self._thread_evt.is_set():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No real value in Python in double-checked in and out of lock, can just rely on the in-lock check (arguably lock not as valuable in Python as it is in other langs, could just change top of cancellation_reason to have an if self._thread_evt.is_set() instead of the lock, but it's fine)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants