Skip to content

Commit 5de87f5

Browse files
committed
Don't close active connections if connection opener fails
Especially in concurrent environments, this could lead to all sorts of unspecific errors.
1 parent 98341c7 commit 5de87f5

File tree

2 files changed

+23
-15
lines changed

2 files changed

+23
-15
lines changed

neo4j/io/__init__.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,7 @@ def time_remaining():
695695
try:
696696
connection = self.opener(address, timeout)
697697
except ServiceUnavailable:
698-
self.remove(address)
698+
self.deactivate(address)
699699
raise
700700
else:
701701
connection.pool = self
@@ -779,31 +779,24 @@ def deactivate(self, address):
779779
conn.close()
780780
except OSError:
781781
pass
782-
if not connections:
783-
self.remove(address)
782+
if not self.connections[address]:
783+
del self.connections[address]
784784

785785
def on_write_failure(self, address):
786786
raise WriteServiceUnavailable("No write service available for pool {}".format(self))
787787

788-
def remove(self, address):
789-
""" Remove an address from the connection pool, if present, closing
790-
all connections to that address.
791-
"""
792-
with self.lock:
793-
for connection in self.connections.pop(address, ()):
794-
try:
795-
connection.close()
796-
except OSError:
797-
pass
798-
799788
def close(self):
800789
""" Close all connections and empty the pool.
801790
This method is thread safe.
802791
"""
803792
try:
804793
with self.lock:
805794
for address in list(self.connections):
806-
self.remove(address)
795+
for connection in self.connections.pop(address, ()):
796+
try:
797+
connection.close()
798+
except OSError:
799+
pass
807800
except TypeError:
808801
pass
809802

tests/unit/io/test_neo4j_pool.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
RoutingConfig,
3636
WorkspaceConfig
3737
)
38+
from neo4j.exceptions import (
39+
ServiceUnavailable,
40+
SessionExpired
41+
)
3842
from neo4j.io import Neo4jPool
3943

4044

@@ -226,3 +230,14 @@ def test_release_does_not_resets_defunct_connections(opener):
226230
cx1.defunct.assert_called_once()
227231
cx1.is_reset_mock.asset_not_called()
228232
cx1.reset.asset_not_called()
233+
234+
235+
def test_failing_opener_leaves_connections_in_use_alone(opener):
236+
pool = Neo4jPool(opener, PoolConfig(), WorkspaceConfig(), ROUTER_ADDRESS)
237+
cx1 = pool.acquire(READ_ACCESS, 30, "test_db", None)
238+
239+
opener.side_effect = ServiceUnavailable("Server overloaded")
240+
with pytest.raises((ServiceUnavailable, SessionExpired)):
241+
pool.acquire(READ_ACCESS, 30, "test_db", None)
242+
243+
assert not cx1.closed()

0 commit comments

Comments
 (0)