When multiple business units or customers share a single PolarDB for PostgreSQL cluster, an uncontrolled workload from one team can exhaust CPU or memory and degrade service for everyone else. Multi-tenancy solves this by dividing a cluster into isolated tenants, each with its own CPU and memory limits enforced at the process level—so a runaway query in one tenant cannot starve another.
How it works
Tenants and resource configurations
A tenant is the resource allocation unit inside a cluster. Multiple databases and users can belong to the same tenant, but each database or user can belong to only one tenant at a time.
There are two tenant types:
| Tenant type | Description |
|---|---|
| System tenant | A special administrative tenant—one per cluster. The system tenant can occupy the resources of all common tenants. Users in the system tenant can connect to any cluster if they have the required permissions. |
| Common tenant | A regular tenant with fully isolated resources. Common tenants must be created by the system tenant. |
A resource configuration is a named set of CPU and memory limits that you attach to a tenant.
Resource enforcement dimensions
Resources are tracked and enforced in three dimensions:
| Dimension | Scope |
|---|---|
| Process | A single connection (session process), including its parallel query child processes |
| User | All session processes started by one user |
| Database | All session processes connected to one database |
System auxiliary processes are exempt from tenant resource limits by default.
When a parallel query runs, its background child processes are assigned to the same tenant as the session that launched the query. When the query finishes, those child processes are removed from the tenant.
Prerequisites
Before you begin, ensure that you have:
A PolarDB for PostgreSQL cluster running PostgreSQL 14 (revision version 14.12.24.0 or later)
Run the following statement to check your cluster's minor version:
SELECT version();Parameters
The following parameters control multi-tenancy behavior. Parameters marked "requires restart" take effect only after the cluster restarts. The others take effect after you run SELECT pg_reload_conf();.
| Parameter | Description | Default | Effect time |
|---|---|---|---|
polar_max_tenants | Maximum number of tenants. Valid values: 0–65536. | 32 | Requires restart |
polar_resource_manager.enable_resource_manager | Enables the resource manager process for memory limits and out-of-memory (OOM) prevention. | on | Requires restart |
polar_resource_manager.database_name | Database that stores tenant metadata. | polardb_admin | Requires restart |
polar_resource_manager.stat_interval | Resource data collection interval, in milliseconds. Valid values: 10–10000. | 500 ms | After pg_reload_conf() |
polar_resource_manager.total_mem_request_rate | Active eviction threshold (percentage of total memory). Valid values: 50%–100%. | 80% | After pg_reload_conf() |
polar_resource_manager.total_mem_limit_rate | Forced eviction threshold (percentage of total memory). Valid values: 50%–100%. | 95% | After pg_reload_conf() |
polar_resource_manager.total_mem_limit_remain_size | Reserved memory size, in KB. Valid values: 131072–INT_MAX. | 256000 KB | After pg_reload_conf() |
polar_resource_manager.enable_log | Enables resource manager logging. | ON | After pg_reload_conf() |
Manage resource configurations
Create a resource configuration
SELECT polar_resource_manager.polar_create_resource_config('resource_config_name');resource_config_name follows the same length rules as database names: up to 64 characters. Longer names are automatically truncated.
Update a resource configuration
SELECT polar_resource_manager.polar_alter_resource_config('resource_config_name', 'config_name', value);Valid config_name values:
| Config name | Unit | Example |
|---|---|---|
cpu_rate_limit | CPU cores (float) | 2 = 2 full cores; 0.5 = half a core |
mem_limit | Bytes (float) | 4294967296 = 4 GB |
CPU limits are applied as a usage ceiling over time. The sum of all cpu_rate_limit values across tenants can exceed the total cluster CPU count—the system throttles any tenant that breaches its limit.Memory limits work the same way: the sum of all mem_limit values can exceed total cluster memory. See Memory resource management for what happens when the limit is reached.Delete a resource configuration
SELECT polar_resource_manager.polar_drop_resource_config('resource_config_name');Manage tenants
Create a tenant
SELECT polar_resource_manager.polar_create_tenant('tenant_name', 'resource_config_name');tenant_name: up to 64 characters; auto-truncated if longer.resource_config_name: must reference an existing resource configuration, otherwise the tenant creation fails.
Update a tenant
SELECT polar_resource_manager.polar_alter_tenant('tenant_name', 'config_name', 'value');Valid config_name values:
| Config name | Description |
|---|---|
name | Rename the tenant |
resource_config | Switch the tenant to a different resource configuration |
Delete a tenant
SELECT polar_resource_manager.polar_drop_tenant('tenant_name');Deleting a tenant does not delete its associated resource configuration.
Assign resources to a tenant
Assign a database
A database can belong to only one tenant.
SELECT polar_resource_manager.polar_tenant_add_database('tenant_name', 'database_name');To view all database-to-tenant assignments:
SELECT dbsname, tenantname FROM polar_resource_manager.polar_tenants_dbs;Assign a user
A user can belong to only one tenant.
SELECT polar_resource_manager.polar_tenant_add_user('tenant_name', 'user_name');To view all user-to-tenant assignments:
SELECT username, tenantname FROM polar_resource_manager.polar_tenants_users;Assign a process
SELECT polar_resource_manager.polar_tenant_add_process('tenant_name', pid);A process can belong to only one tenant. If you assign the same process to multiple tenants, only the last assignment takes effect.
Monitor tenant resource usage
The polar_all_resource_configs_detail view shows real-time resource usage for all tenants:
SELECT * FROM polar_resource_manager.polar_all_resource_configs_detail;| Column | Type | Description |
|---|---|---|
tenantname | NAME | Tenant name |
resource_config_name | NAME | Resource configuration name |
num_processes | INTEGER | Total number of processes |
num_idle_processes | INTEGER | Number of idle processes |
num_active_processes | INTEGER | Number of active processes |
cpu_rate_limit | DOUBLE PRESISION | Maximum CPU cores allocated to the tenant |
per_process_cpu_rate_limit | DOUBLE PRESISION | Maximum CPU cores per process |
mem_limit | DOUBLE PRESISION | Maximum memory allocated to the tenant |
mem_usage | DOUBLE PRESISION | Current memory usage |
idle_processes_mem_usage | DOUBLE PRESISION | Memory used by idle processes |
active_processes_mem_usage | DOUBLE PRESISION | Memory used by active processes |
cpu_usage_rate | DOUBLE PRESISION | CPU utilization (%) |
CPU resource management
Set cpu_rate_limit to cap how many CPU cores a tenant can use. The value is a float representing CPU cores:
| Value | Meaning |
|---|---|
0.3 | Up to 30% of one core |
0.5 | Up to 50% of one core |
2 | Up to 2 full cores |
When a tenant exceeds its limit, the cluster throttles its processes to bring utilization within the cap. The sum of cpu_rate_limit values across all tenants can exceed the total cluster CPU count.
Memory resource management
Memory is a hard resource. Unlike CPU, memory cannot be throttled dynamically—when a tenant exceeds its limit, the system terminates sessions to free memory. This is similar to how an operating system handles OOM conditions, but applied per tenant.
Set mem_limit to the maximum bytes a tenant can consume (for example, 4294967296 = 4 GB). The sum of mem_limit values across tenants can exceed total cluster memory.
Two eviction policies protect the cluster:
| Policy | Trigger | Behavior |
|---|---|---|
| Active eviction | Memory usage exceeds total_mem_request_rate (default: 80%) and the tenant's usage exceeds its mem_limit | Terminates the over-limit session. Returns a memory overrun error. Repeats until usage falls below the threshold. |
| Forced eviction | Memory usage exceeds total_mem_limit_rate (default: 95%) | Traverses all processes, terminates sessions that exceed their limit, and returns a memory overrun error. Prevents cluster-wide OOM. |
When a session is terminated, the system sends a SIGUSR2 signal to the session process and returns a memory overrun error. Only user processes can be terminated this way. System background processes belong to the system tenant and are exempt from forced termination.
If a parallel query's background process exceeds the memory limit, the corresponding user session is terminated rather than the background process itself.