MagicStack / asyncpg

A fast PostgreSQL Database Client Library for Python/asyncio.
Apache License 2.0
6.88k stars 399 forks source link

Fix timeouterror on multihost config #1022

Closed JMarkin closed 11 months ago

JMarkin commented 1 year ago

Hello,

Problem: If I try to connect with dsn like "postgresql://postgres@host1,host2,host3:5432/postgres". If I have timeouterror on host1 asyncpg always skips host2 and host3.

For example:

  1. I try to connect to with dsn, timeout 60
  2. host1 doesn't break connection but doesn't resolve connection without oserror
  3. breaks me by asyncio.TimeoutError on 60 sec
  4. in code timeout=time.monotinic() - before in this case always be >=60
  5. result: host2 and host3 always skip

In this PR I try to fix with problem

elprans commented 11 months ago

Thanks for the PR! The fix in #1087 is more comprehensive, so closing this in favor of that.

inikolaev commented 4 months ago

@elprans I think your fix does not really address the problem raised here. I had ran into a similar issue about a year ago and I described it in a discussion, but never followed up.

The problem is that the connection timeout configuration sets the overall connection timeout.

Consider this case: we have two hosts, we have configured the timeout of 10 seconds and first host does not respond within 10 seconds so the timeout context manager fires up. In such case we never make an attempt to connect with the second host.

I believe we might need a separate setting here, like one introduced in this PR, that would allow to configure both the overall connect timeout and per host connect timeout.

I have just tested the new version and I'm still running into the same issue. I found a peculiar case though: if I set timeout to 80 seconds, my example works, but it takes 75 seconds to succeed. It looks like 75 seconds is some internal timeout somewhere that kicks-in earlier and gives a chance to try to connect with another host.

I did the same for psycopg2 which handles this case and what they do is apply the same timeout out for each host. So if I set timeout to 2 seconds and both hosts unavailable the overall timeout will be 4 seconds. We could employ the same strategy with asyncpg - at least it would be consistent with psycopg2.