Skip to content

Make Pool.close() wait until all checked out connections are released #299

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

Merged
merged 1 commit into from
May 31, 2018

Conversation

elprans
Copy link
Member

@elprans elprans commented May 30, 2018

Currently, pool.close(), despite the "graceful" designation, closes
all connections immediately regardless of whether they are acquired.

With this change, pool will wait for connections to actually be released
before closing.

WARNING: This is a potentially incompatible behavior change, as sloppily
written code which does not release acquired connections will now cause
pool.close() to hang forever.

Also, when conn.close() or conn.terminate() are called directly
on an acquired connection, the associated pool item is released
immediately.

Closes: #290

@elprans elprans requested a review from 1st1 May 30, 2018 22:23
def _abort(self):
# Put the connection into the aborted state.
self._aborted = True
self._protocol.abort()
Copy link
Member

Choose a reason for hiding this comment

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

I'd also add self._protocol = None to catch any use of connection after _abort().

self._clean_tasks()
if self._proxy is not None:
# Connection is a member of a pool, but is getting
# aborted directly. Notify the pool about the fact.
Copy link
Member

Choose a reason for hiding this comment

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

"is getting aborted directly." -- outdated comment?

asyncpg/pool.py Outdated
@@ -163,31 +164,29 @@ def __init__(self, pool, *, connect_args, connect_kwargs,
# we close it. A new connection will be created
# when `acquire` is called again.
try:
proxy._detach()
# Use `close` to close the connection gracefully.
# An exception in `setup` isn't necessarily caused
# by an IO or a protocol error.
await self._con.close()
Copy link
Member

Choose a reason for hiding this comment

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

Add a comment that con.close() will cleanup its holder too.

asyncpg/pool.py Outdated
assert self._in_use
self._in_use = False
self._timeout = None
assert self._in_use is not None
Copy link
Member

Choose a reason for hiding this comment

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

Let's raise a proper exception here.

asyncpg/pool.py Outdated
self._timeout = None

if self._con._protocol.queries_count >= self._max_queries:
await self._con.close(timeout=timeout)
Copy link
Member

Choose a reason for hiding this comment

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

Should we add an early return here?

try:
    await self._con.close(..)
finally:
    self._release()
    return

asyncpg/pool.py Outdated
self._con.terminate()
finally:
self._con = None

def _maybe_cancel_inactive_callback(self):
if self._inactive_callback is not None:
self._inactive_callback.cancel()
self._inactive_callback = None

def _deactivate_connection(self):
Copy link
Member

Choose a reason for hiding this comment

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

I'd rename this to _deactivate_inactive_connection()

asyncpg/pool.py Outdated
self._con.terminate()
assert self._in_use is None
if self._con is not None:
self._con.terminate()
Copy link
Member

Choose a reason for hiding this comment

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

Add a comment that we terminate the connection instead of just closing it because it's not in use.

asyncpg/pool.py Outdated
self._in_use = None

# Let go of the connection proxy.
if self._proxy is not None:
Copy link
Member

Choose a reason for hiding this comment

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

Let's make sure we set and unset self._proxy and self._con consistently.

Copy link
Member Author

Choose a reason for hiding this comment

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

Connection and proxy lifetimes are not the same. A proxy only lives while the connection is acquired. I'll clarify with a comment.

asyncpg/pool.py Outdated
con = connection._detach()
self._check_init()

con = connection._con
con._on_release()
Copy link
Member

Choose a reason for hiding this comment

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

connection._con._on_release() to make it easier for the reader (o/w I need to track if con is used in the code below)

asyncpg/pool.py Outdated
con._on_release()
ch = connection._holder
Copy link
Member

Choose a reason for hiding this comment

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

Can you rename ch to holder?

asyncpg/pool.py Outdated
# operations on it will fail.
if self._proxy is not None:
if self._proxy._con is not None:
self._proxy._detach()
Copy link
Member

Choose a reason for hiding this comment

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

Just make _proxy._detach() idempotent.

Currently, `pool.close()`, despite the "graceful" designation, closes
all connections immediately regardless of whether they are acquired.

With this change, pool will wait for connections to actually be released
before closing.

WARNING: This is a potentially incompatible behavior change, as sloppily
written code which does not release acquired connections will now cause
`pool.close()` to hang forever.

Also, when `conn.close()` or `conn.terminate()` are called directly
on an acquired connection, the associated pool item is released
immediately.

Closes: #290
@elprans elprans merged commit 7a0585a into master May 31, 2018
@elprans elprans deleted the pool branch May 31, 2018 21:50
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.

Pool.close() does not close the pool gracefully
2 participants