Skip to content
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

Rework how services are started/stopped #24

Merged
merged 1 commit into from
Apr 24, 2024

Conversation

davidbrochart
Copy link
Collaborator

@davidbrochart davidbrochart commented Mar 26, 2024

Description

pycrdt-websocket provides these 4 services:

  • WebsocketServer: a WebSocket server.
  • WebsocketProvider: a WebSocker provider (a client to a WebsocketServer).
  • YStore: a store for persisting Y updates.
  • YRoom: a room where clients collaborate on a Y document.

These services are asynchronous, and have to be started (for initialization) and stopped (for teardown). For instance, initialization could include connecting to a database, and teardown could disconnect from the database.
They provide 2 common APIs to be started and stopped:

  • an async context manager.
  • individual start() and stop() async methods.

The async context manager API looks like this:

async def main():
    async with WebsocketServer() as websocket_server:
        ...

And the start/stop API looks like this (using AnyIO):

async def main():
    websocket_server = WebsocketServer()
    async with create_task_group() as tg:
        await tg.start(websocket_server.start)
        ...
        await websocket_server.stop()

In an environment where it is not possible to use AnyIO task groups, once can use asyncio like this:

async def main():
    websocket_server = WebsocketServer()
    task = asyncio.create_task(websocket_server.start())
    await websocket_server.started.wait()
    ...
    await websocket_server.stop()

While it is advised to use the async context manager API, it is not always possible to do so, hence the manual start/stop methods.

Improvements

The way services are started and stopped now uses an anyio.Lock, instead of a boolean attribute _starting, to handle concurrent calls. Also, __aenter__() calls start() under the hood, and __aexit__() calls stop(), instead of copying code. This ensures that both ways are equivalent. The start() method is passed a parameter from_context_manager=True if called from __aenter__(), in order to not create a new task group, since it was already created in __aenter__().

Tests have also been reworked, so that both APIs (context manager and start/stop methods) of every service are tested. Also, since #22 both asyncio and trio backends are tested.

Breaking changes

This PR has the following breaking changes:

  • the stop() methods are now async. It didn't make a lot of sense to have start() async and not stop(), and teardown could have async logic in general.
  • WebsocketServer.delete_room() is now async, since it may stop a room, and stopping a room is async.

@davidbrochart davidbrochart marked this pull request as draft March 26, 2024 13:28
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.

1 participant