Multitenancy is a software architecture where a single instance of a system serves multiple customers, or tenants, while ensuring data isolation between them for privacy and security.

This page shows you how to implement multitenancy in Pinecone using a serverless index with one namespace per tenant.

How it works

In Pinecone, an index is the highest-level organizational unit of data, where you define the dimension of vectors to be stored in the index and the measure of similarity to be used when querying the index.

Within an index, records are stored in namespaces, and all upserts, queries, and other data plane operations always target one namespace.

This structure makes it easy to implement multitenancy. For example, for an AI-powered SaaS application where you need to isolate the data of each customer, you would assign each customer to a namespace and target their writes and queries to that namespace (diagram above).

In cases where you have different workload patterns (e.g., RAG and semantic search), you would use a different index for each workload, with one namespace per customer in each index:

1. Create a serverless index

Based on a breakthrough architecture, serverless indexes scale automatically based on usage, and you pay only for the amount of data stored and operations performed. Combined with the isolation of tenant data using namespaces (next step), serverless indexes are ideal for multitenant use cases.

To create a serverless index, use the spec parameter to define the cloud and region where the index should be deployed. For Python, you also need to import the ServerlessSpec class.

from pinecone import Pinecone, ServerlessSpec

pc = Pinecone(api_key="YOUR_API_KEY")

pc.create_index(
  name="multitenant-app",
  dimension=8,
  metric="cosine",
  spec=ServerlessSpec(
    cloud="aws",
    region="us-east-1"
  )
)

2. Isolate tenant data

In a multitenant solution, you need to isolate data between tenants. To achieve this in Pinecone, use one namespace per tenant. In the serverless architecture, each namespace is stored separately, so this approach ensures physical isolation of each tenant’s data.

To create a namespace for a tenant, specify the namespace parameter when first upserting the tenant’s records. For example, the following code upserts records for tenant1 and tenant2 into the multitenant-app index:

from pinecone import Pinecone

pc = Pinecone(api_key="YOUR_API_KEY")
index = pc.Index("multitenant-app")

index.upsert(
  vectors=[
    {"id": "A", "values": [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]},
    {"id": "B", "values": [0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]},
    {"id": "C", "values": [0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3]},
    {"id": "D", "values": [0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4]}
  ],
  namespace="tenant1"
)

index.upsert(
  vectors=[
    {"id": "E", "values": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]},
    {"id": "F", "values": [0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6]},
    {"id": "G", "values": [0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7]},
    {"id": "H", "values": [0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8]}
  ],
  namespace="tenant2"
)

When upserting additional records for a tenant, or when updating or deleting records for a tenant, specify the tenant’s namespace. For example, the following code updates the dense vector value of record A in tenant1:

from pinecone import Pinecone

pc = Pinecone(api_key="YOUR_API_KEY")
index = pc.Index("multitenant-app")

index.update(id="A", values=[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], namespace="tenant1")

3. Query tenant data

In a multitenant solution, you need to ensure that the queries of one tenant do not affect the experience of other tenants/customers. To achieve this in Pinecone, target each tenant’s queries at the namespace for the tenant.

For example, the following code queries only tenant2 for the 3 vectors that are most similar to an example query vector:

from pinecone import Pinecone

pc = Pinecone(api_key="YOUR_API_KEY")
index = pc.Index("multitenant-app")

query_results = index.query(
    namespace="tenant2",
    vector=[0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7],
    top_k=3,
    include_values=True
)

print(query_results)

# Returns:
# {'matches': [{'id': 'F',
#               'score': 1.00000012,
#               'values': [0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6]},
#              {'id': 'G',
#               'score': 1.0,
#               'values': [0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7]},
#              {'id': 'E',
#               'score': 1.0,
#               'values': [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]}],
#  'namespace': 'tenant2',
#  'usage': {'read_units': 6}}

4. Offboard a tenant

In a multitenant solution, you also need it to be quick and easy to offboard a tenant and delete all of its records. To achieve this in Pinecone, you just delete the namespace for the specific tenant.

For example, the following code deletes the namespace and all records for tenant1:

from pinecone import Pinecone

pc = Pinecone(api_key="YOUR_API_KEY")
index = pc.Index("multitenant-app")

index.delete(delete_all=True, namespace='tenant1')

Alternatives

  • When tenant data isolation is not a requirement, or when you require the ability to query across tenants, you can store all records in a single index and use metadata fields to assign records to tenants/customers. At query time, you can them filter by metadata. For more guidance on this approach, see Multitenancy in Vector Databases.

  • The serverless architecture is ideal for multitenant use cases, but you can also implement multitenancy with pod-based indexes and namespaces. The process is the same. However, note the following disadvantages compared to serverless:

    • Noisy neighbors: In the pod-based architecture, all namespaces in an index share compute resources, so the behavior of one tenant/customer can affect other tenant/customers.

    • Higher cost: With pod-based indexes, billing is determined by the per-minute price per pod and the number of pods the index uses, regardless of index activity. With this multitenancy approach, costs are likely to be high, with a minimum of $70/month per tenant. Also, you pay for unused resources when index usage is lower than expected.

    • Maintenance effort: When you need additional storage or compute, you must manually scale the index.