vmagamedov / grpclib

Pure-Python gRPC implementation for asyncio
http://grpclib.readthedocs.io
BSD 3-Clause "New" or "Revised" License
936 stars 92 forks source link

Is there a way to wait until the channel is ready? #135

Closed ozangungor12 closed 2 years ago

ozangungor12 commented 3 years ago

Is there an implementation of channel_ready() functionality in grpclib?

In gRPC AsyncIO API, this creates a blocking co-routine until the channel is available to connect.

async with aio.insecure_channel(('127.0.0.1', 50051)) as channel:
        await channel.channel_ready()

https://grpc.github.io/grpc/python/_modules/grpc/aio/_base_channel.html#Channel.channel_ready

Thanks!

vmagamedov commented 3 years ago

In grpclib connection is established lazily so the only way to connect is to make a request. There is also an undocumented method await channel.__connect__(), which can be used to explicitly create a connection before making a request, but I can't guarantee that this method wouldn't change in the future.

ozangungor12 commented 3 years ago

Thank you for the answer @vmagamedov. So, is there a "best practice" you can suggest within grpclib, for channel connection request not to crash when server is not yet ready/available?

vmagamedov commented 3 years ago

Essentially there should be a loop with retries. We can retry connection attempts by using undocumented Channel.__connect__ method, see #128. There is also another way: we can use health check requests to check that server is ready to serve requests (if server implements health checks protocol):

from grpclib.health.v1.health_pb2 import HealthCheckRequest
from grpclib.health.v1.health_pb2 import HealthCheckResponse
from grpclib.health.v1.health_grpc import HealthStub

...

health = HealthStub(channel)
while True:
    try:
        response = await health.Check(HealthCheckRequest())
    except (ConnectionError, GRPCError):
        pass
    else:
        if response.status is HealthCheckResponse.SERVING:
            log.info('Connected and ready')
            break
        else:
            log.info('Connected but not ready')
    log.info('Waiting for connection retry')
    await asyncio.sleep(1)

This example can be further improved to limit number of retries, to specify request timeout etc.