Skip to content

Commit d8530e8

Browse files
Prevent connection pool replacement race
When pool creation races for the same host, a slower attempt can overwrite a pool that another thread already published and close connections with in-flight requests. Capture the previous pool before connection setup, then compare that state under the session lock before publishing the new pool. If another thread changed the pool, discard the stale pool instead of replacing the current one. Keep pool removals behind the same lock so the check observes all writers. Fixes: #317
1 parent d83adab commit d8530e8

1 file changed

Lines changed: 19 additions & 3 deletions

File tree

cassandra/cluster.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3241,6 +3241,9 @@ def add_or_renew_pool(self, host, is_host_addition):
32413241
return None
32423242

32433243
def run_add_or_renew_pool():
3244+
with self._lock:
3245+
previous = self._pools.get(host)
3246+
32443247
try:
32453248
new_pool = HostConnection(host, distance, self)
32463249
except AuthenticationFailed as auth_exc:
@@ -3256,7 +3259,6 @@ def run_add_or_renew_pool():
32563259
host, conn_exc, is_host_addition, expect_host_to_be_down=True)
32573260
return False
32583261

3259-
previous = self._pools.get(host)
32603262
with self._lock:
32613263
while new_pool._keyspace != self.keyspace:
32623264
self._lock.release()
@@ -3276,7 +3278,20 @@ def callback(pool, errors):
32763278
self._lock.acquire()
32773279
return False
32783280
self._lock.acquire()
3279-
self._pools[host] = new_pool
3281+
3282+
pool_unchanged = self._pools.get(host) is previous
3283+
if not pool_unchanged:
3284+
# Another concurrent add_or_renew_pool changed this host
3285+
# while we were creating ours. Don't replace the existing
3286+
# pool because doing so would kill in-flight queries.
3287+
log.debug("Pool for host %s was already replaced by another "
3288+
"thread, discarding new pool", host)
3289+
else:
3290+
self._pools[host] = new_pool
3291+
3292+
if not pool_unchanged:
3293+
new_pool.shutdown()
3294+
return True
32803295

32813296
log.debug("Added pool for host %s to session", host)
32823297
if previous:
@@ -3287,7 +3302,8 @@ def callback(pool, errors):
32873302
return self.submit(run_add_or_renew_pool)
32883303

32893304
def remove_pool(self, host):
3290-
pool = self._pools.pop(host, None)
3305+
with self._lock:
3306+
pool = self._pools.pop(host, None)
32913307
if pool:
32923308
log.debug("Removed connection pool for %r", host)
32933309
return self.submit(pool.shutdown)

0 commit comments

Comments
 (0)