PyMilvus Documentation

repository·master·Indexed 23 days ago

https://github.com/milvus-io/pymilvus

The official Python SDK for Milvus, a vector database. PyMilvus enables developers to perform vector searches, manage collections, and handle data ingestion. It features a ConnectionManager for lifecycle management, support for Milvus global clusters with automatic topology discovery, and an AsyncMilvusClient for asynchronous workflows.

Tokens
13.2K
Snippets
26
Records
65
Agent score
79%

What's inside PyMilvus

  1. How MilvusClient connection management works

    master

    In newer versions of the SDK, MilvusClient uses a ConnectionManager instead of the legacy connections singleton. The ConnectionManager handles connection lifecycle (create, release, close), health checks, and automatic recovery when encountering UNAVAILABLE errors.

    It employs a Strategy pattern to distinguish between two types of connection behaviors:

    1. RegularStrategy: Used for direct connections to a specific endpoint. It always attempts recovery on UNAVAILABLE errors.
    2. GlobalStrategy: Used for global clusters. It performs topology discovery, routes to a primary endpoint, and runs background refreshes to keep the topology up to date.

    Note: The legacy ORM continues to use the connections singleton until it is deprecated.

  2. Understand System Name Semantics ($score, $id)

    master

    In the context of L2_RERANK stages, Milvus provides special system names:

    • Readable system inputs: $id, $score (used to access the original ID and the initial search score/distance).
    • Writable system outputs: $score (used to overwrite the search score with a new calculated value).

    Important Rules:

    1. Do not request $score in output_fields: It is handled automatically. The final reranked value is returned in the standard distance or score field of the search result.
    2. Temporary variables must NOT use the $ prefix: If you want to create a temporary variable, use a standard name. Using a $ prefix for a temporary variable in an L2 Search is invalid.

    Valid Example:

    # Using $score to read and then overwriting it
    chain.map("tmp_val", fn.decay(col("ts"), ...))
        .map("$score", fn.num_combine(col("$score"), col("tmp_val"), mode="sum"))
    
    # Using a custom name for a temporary variable
    chain.map("my_temp_score", fn.num_combine(col("$score"), col("ts"), mode="sum"))
  3. Use Function Chain API for search reranking

    master

    The Function Chain API allows you to describe score adjustment and rerank operations (like numeric combination, decay, model reranking, and sorting) as a typed, ordered plan. This plan is passed to the search method via the function_chains parameter.

    Key Concepts:

    • Stage: For ordinary L2 search, use FunctionChainStage.L2_RERANK.
    • Builder Pattern: Use a fluent interface to chain operations like .map(), .sort(), and .limit().
    • Arguments: Use col(name) to reference collection fields, temporary variables, or system values like $score.
    • System Values: $score is a special system value representing the current search score, passed as a typed column reference.
    • Temporary Variables: You can create temporary values by mapping an expression to a custom name (e.g., "freshness") and then referencing that name in subsequent steps of the chain.
    from pymilvus import FunctionChain, FunctionChainStage, MilvusClient
    from pymilvus.function_chain import col, fn
    
    client = MilvusClient(uri="http://localhost:19530")
    
    # Define a chain that combines scores and sorts
    chain = (
        FunctionChain(stage=FunctionChainStage.L2_RERANK, name="l2_score_plus_ts")
        .map("$score", fn.num_combine(col("$score"), col("ts"), mode="sum"))
        .sort(col("$score"), desc=True)
    )
    
    # Execute search with the chain
    res = client.search(
        collection_name="my_collection",
        data=[query_vector],
        anns_field="vector",
        search_params={},
        output_fields=["doctype"],
        function_chains=[chain],
    )
  4. Connection health checks on idle connections

    master

    To ensure reliability, the connection manager performs a health check if a connection has been idle for more than 30 seconds. The check involves:

    1. Verifying channel.check_connectivity_state() is not SHUTDOWN.
    2. Calling handler.get_server_version(timeout=5.0).

    If the connection is found to be unhealthy, the manager triggers the _recover() flow to refresh the internal connection.

  5. Understand MilvusClient connection sharing behavior

    master

    A MilvusClient holds an alias to a Milvus server connection. Understanding how these connections are shared is critical for resource management and avoiding runtime errors.

    Connection Sharing Rules

    • Shared Connections: MilvusClient objects with the same uri and authentication reuse the same underlying connection to the Milvus server. This applies even if they use different db_name values; each client maintains its own database context, but they share the connection alias.
    • Unique Connections: MilvusClient objects with different uri or different authentication credentials do not share connections.

    The Impact of close()

    Because multiple clients may share the same connection alias, calling .close() on one MilvusClient instance will close the underlying connection for all other client instances sharing that same alias. This will cause subsequent calls on the other clients to fail with an exception.

    import threading
    from pymilvus import MilvusClient
    
    URI = "http://localhost:19530"
    TEST_DB = "test_DB"
    
    # Example: Sharing connections across different databases
    c = MilvusClient(uri=URI)
    c.create_database(TEST_DB)
    c_testdb = MilvusClient(uri=URI, db_name=TEST_DB)
    
    # c and c_testdb share the same connection (same alias), but use different databases
    print(f"alias for c:        {c._using}, results of c.list_collections: {c.list_collections()}")
    print(f"alias for c_testdb: {c_testdb._using}, results of c_testdb.list_collections: {c_testdb.list_collections()}")
    
    # Closing c_testdb affects c because they share the same connection
    c_testdb.close()
    try:
        print(f"results of c.list_collections: {c.list_collections()}")
    except Exception as ex:
        print(f"c is also affected because they share the same connection, exception: {ex}")
  6. How Global Cluster topology discovery works

    master

    When a MilvusClient is initialized with a global endpoint (detected by the presence of global-cluster in the URI), the following lifecycle occurs:

    1. Detection: The SDK identifies the URI as a global endpoint.
    2. Topology Fetch: The SDK calls a REST API (GET https://<global-endpoint>/global-cluster/topology) using your existing token to retrieve the cluster layout.
    3. Primary Selection: The SDK parses the response to find the cluster with capability: 3 (which represents PRIMARY / read + write).
    4. Connection: The SDK establishes a standard gRPC connection to that primary cluster's specific endpoint.
    5. Background Refresh: A background thread starts to periodically refresh the topology (every 5 minutes) or react to connection errors to ensure resilience if the primary cluster changes.
  7. ConnectionManager and AsyncConnectionManager integration

    master

    The SDK provides two primary managers to handle connection lifecycles:

    • ConnectionManager (Sync): Uses a split-lock pattern. It releases the lock before performing network I/O (like topology fetching) to avoid blocking, then re-acquires the lock to verify the handler hasn't changed before proceeding with recovery.
    • AsyncConnectionManager (Async): Uses an asyncio.Lock throughout the entire operation. It runs potentially blocking operations (like on_unavailable()) via run_in_executor to prevent blocking the event loop. This serializes recovery to prevent double-recovery attempts.
  8. Use the Function Chain API for advanced search processing

    master

    The Function Chain API allows you to perform complex operations on search results (like score manipulation, sorting, and limiting) directly within the Milvus server. This is achieved by chaining operators like map, sort, and limit onto a FunctionChain object, which is then passed to a SearchRequest.

    Key Concepts

    • Operators: Operations like map, sort, and limit that transform the result set.
    • Column References: Use the col() helper to reference fields. Use col("$score") to reference the system-generated score.
    • Function Factories: Use the fn module to create expressions for mathematical or logical operations (e.g., num_combine, decay, round_decimal).
    • Chaining: The API uses a fluent builder pattern where method calls are chained to define the sequence of operations.

    Constraints and Error Handling

    • Ranker Conflict: You cannot use ranker (or function_score) and function_chains together in the same request. If you use function chains, the ranking logic is handled within the chain.
    • Hybrid Search: Currently, function_chains is not supported for hybrid search.
    • Error Types: Input validation errors (like invalid column names or unsupported types) will raise a ParamError.
  9. Understand the PyMilvus repository structure

    master

    The PyMilvus repository is organized into several key directories that define its functionality and testing suites:

    • pymilvus/: The core source code directory.
    • docs/: Design and planning documentation (e.g., docs/plans).
    • examples/: Runnable Python scripts demonstrating how to use various PyMilvus interfaces.
    • tests/unit/: Deterministic unit tests.
    • tests/integration/lite/: Integration tests specifically for Milvus Lite.
    • tests/benchmark/: Benchmark testing suite.
    • pyproject.toml: Package metadata, runtime dependencies, optional dependency groups, and tool configurations.
    • uv.lock: Lock file for development dependencies managed by uv.
  10. How connection recovery works in Milvus

    master

    The ConnectionManager automatically handles connection failures, specifically for UNAVAILABLE errors and STREAMING_CODE_REPLICATE_VIOLATION exceptions.

    When a retryable error is detected, the manager follows a build fresh -> validate -> atomically swap -> retire old sequence:

    1. Build: A new connection (channel, stub, and auth/db metadata) is created in local state.
    2. Validate: The replacement connection is validated (e.g., by waiting for channel readiness).
    3. Swap: The internal connection reference within the existing handler is atomically swapped. This ensures the handler object identity remains the same for the user, but the underlying connection is updated.
    4. Retire: The old connection is retired.

    If validation of the new connection fails, the recovery process raises an error and leaves the current connection intact.

  11. Automatic Topology Refresh and Error Handling

    master

    To maintain connection resilience, PyMilvus manages topology updates in the background:

    • Fixed Interval: The topology is refreshed every 5 minutes.
    • Event-Driven Refresh: If a connection error occurs with the status UNAVAILABLE (server unreachable), the SDK triggers an immediate topology refresh to see if a new primary has been assigned.
    • Error Handling: If the topology cannot be fetched after retries, or if no primary cluster is found in the response, a MilvusException is raised.

    Note on Limitations:

    • There is currently no support for automatic failover; if the primary is unavailable, operations will fail.
    • All operations are routed to the primary; there is no automatic routing of read operations to secondary clusters.
  12. Best practices for managing MilvusClient connections

    master

    To ensure stable and performant connection management in PyMilvus, follow these best practices:

    When using default behavior (shared connections):

    • Do not close shared clients: Since multiple MilvusClient objects might share the same connection, calling .close() on one can break all others. Avoid closing individual clients if they were initialized with the same uri and authentication unless you are certain no other part of your application is using that connection.

    When using customized aliases (unique connections):

    • Explicitly close clients: Ensure you call .close() when a client with a custom alias is no longer needed to free up resources.
    • Check dependencies: Before closing a client, ensure no other active components require that specific connection.
    • Reuse connections: Avoid creating short-lived connections. It is more efficient to reuse existing MilvusClient instances whenever possible.