Multi-Tenancy in Geode

Multi-tenancy enables multiple customers (tenants) to share a single Geode deployment while maintaining strict data isolation. This architecture is fundamental for SaaS applications, reducing operational complexity and infrastructure costs.

Multi-Tenancy Strategies

Row-Level Security (RLS)

The preferred approach uses RLS policies to automatically filter data by tenant:

-- Add tenant_id to all tenant-scoped labels
CREATE CONSTRAINT tenant_required ON (n:Customer) ASSERT EXISTS(n.tenant_id);

-- Create RLS policy for tenant isolation
CREATE POLICY tenant_isolation ON Customer
FOR ALL
USING (tenant_id = current_setting('app.tenant_id'));

-- Enable RLS
ALTER LABEL Customer ENABLE ROW LEVEL SECURITY;

Setting tenant context:

from geode_client import Client

async def query_as_tenant(tenant_id: str):
    client = Client(host="localhost", port=3141)
    async with client.connection() as conn:
        # Set tenant context for this session
        await conn.execute(
            "SET app.tenant_id = $tenant_id",
            {'tenant_id': tenant_id}
        )

        # All queries automatically filtered to this tenant
        result, _ = await conn.query("""
            MATCH (c:Customer)
            RETURN c.name, c.email
        """)
        # Only returns customers belonging to tenant_id
        return result

Schema-Based Isolation

For stronger isolation, use separate schemas per tenant:

-- Create tenant-specific schema
CREATE SCHEMA tenant_acme;
CREATE SCHEMA tenant_globex;

-- Queries target specific schema
USE tenant_acme;
MATCH (c:Customer) RETURN c;

Database-Level Isolation

Maximum isolation with separate databases:

# Create per-tenant databases
geode admin create-database --name tenant_acme
geode admin create-database --name tenant_globex

Choosing an Approach

ApproachIsolationEfficiencyComplexity
RLSLogicalHighLow
SchemaModerateMediumMedium
DatabasePhysicalLowerHigher

RLS is recommended for most SaaS applications, offering the best balance of isolation, efficiency, and operational simplicity.

Implementation Patterns

Tenant-aware connection middleware:

class TenantMiddleware:
    def __init__(self, client):
        self.client = client

    async def get_connection(self, tenant_id: str):
        conn = await self.client.connect()
        await conn.execute(
            "SET app.tenant_id = $tenant_id",
            {'tenant_id': tenant_id}
        )
        return conn

Cross-tenant queries (admin only):

-- Bypass RLS for administrative queries
SET ROLE admin_bypass_rls;
MATCH (c:Customer)
RETURN c.tenant_id, count(*) AS customer_count
GROUP BY c.tenant_id;

Best Practices

Always include tenant_id: Use constraints to ensure all tenant-scoped data includes the tenant identifier.

Audit cross-tenant access: Log any queries that bypass tenant isolation.

Test isolation thoroughly: Verify RLS policies prevent cross-tenant data access.

Consider tenant-specific indexes: For large tenants, partition indexes may improve performance.


Related Articles

No articles found with this tag yet.

Back to Home