Skip to content

[Bug]: MilvusClient.close() breaks other clients due to implicit connection sharing #3132

@jac0626

Description

@jac0626

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Description:

Multiple MilvusClient instances connecting to the same URI implicitly share the same underlying connection without user awareness. When one client calls close(), all other clients sharing that connection become unusable.

Expected Behavior:

Each MilvusClient instance should be independent. Closing one client should not affect other clients.

Actual Behavior:

Closing any MilvusClient instance disconnects all other clients that share the same (uri, db_name, auth) combination.

Root Cause:

In pymilvus/milvus_client/_utils.py, connections are reused based on a generated alias:

# Line 49-54
not_empty = [v for v in [use_async_fmt, uri, db_name, auth_fmt, loop_id_fmt] if v]
using = "-".join(not_empty)

if connections.has_connection(using):
    return using  # Reuses existing connection

When MilvusClient.close() is called, it removes the shared connection:

# pymilvus/milvus_client/milvus_client.py Line 849-850
def close(self):
    connections.remove_connection(self._using)

There is no reference counting, so the connection is removed even if other clients are still using it.

Reproduction:

from pymilvus import MilvusClient
from pymilvus.orm.connections import connections

URI = "http://localhost:19530"

client1 = MilvusClient(uri=URI)
client2 = MilvusClient(uri=URI)

print(f"Same alias: {client1._using == client2._using}")  # True
print(f"Connections count: {len(connections._alias_handlers)}")  # 1

client1.close()

client2.list_collections()  # ConnectionNotExistException!

Suggested Fix:

Option 1: Add reference counting to Connections class

class Connections:
    def __init__(self):
        self._alias_handlers = {}
        self._ref_counts = {}  # Track how many clients use each connection
    
    def disconnect(self, alias):
        self._ref_counts[alias] -= 1
        if self._ref_counts[alias] == 0:
            self._alias_handlers.pop(alias).close()

Option 2: using connection pool to manage connections instead managed by user

Expected Behavior

No response

Steps/Code To Reproduce behavior

Environment details

Anything else?

No response

Metadata

Metadata

Assignees

Labels

kind/bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions