tencent cloud

TencentDB for PostgreSQL

Multi-Tenancy Capabilities

Download
Focus Mode
Font Size
Last updated: 2026-11-09 11:18:18
AI-Translated
A multi-tenant instance consolidates multiple tenants into a single PostgreSQL instance, where each tenant uses an independent database and account while sharing the instance's CPU, memory, and other resources. To prevent information leakage and resource contention between tenants, TencentDB for PostgreSQL provides two types of multi-tenant isolation capabilities, both of which are implemented by the self-developed extension tencentdb_serverless:
View isolation (information isolation): Enable row-level security (RLS) policies on shared system views so that each tenant can only see information related to their own database, preventing information leakage between tenants.
Resource isolation (database-level CPU limit): Set a CPU usage cap for each database. When the cap is exceeded, statements are automatically slowed down by waiting, preventing a single tenant's load from consuming the CPU resources of the entire instance.

Supported Versions

Capability
PostgreSQL Version
Supported Version
Custom multi-tenancy enablement capability
PostgreSQL 17
v17.11_r1.26 or later
Note:
You can view the kernel version of the current instance on the instance details page in the console, or by running SHOW tencentdb_version;.

Enabling Multi-Tenancy Capabilities

1. In the shared_preload_libraries parameter under console parameter settings, select tencentdb_serverless, save the changes, and restart the instance.
2. In the console parameter settings, set the tencentdb_multi_tenant_mode parameter to on.
3. View isolation: See the usage tutorial in View Isolation and enable it in the tenant database.
4. Tenant database CPU limit: Create the tencentdb_serverless extension in the postgres database and call the quota limit function to configure it. For details, see Resource Isolation.

Parameter Description

Parameter
Level
Default Value
Description
tencentdb_multi_tenant_mode
postmaster
off
Master switch for multi-tenancy mode. Modify it in the console parameter settings, and restart the instance after modification.
shared_preload_libraries
postmaster
-
Select tencentdb_serverless, modify it in the console parameter settings, and restart the instance after modification.
tencentdb_multi_tenant_role_prefix
sighup
tencentdb_
Multi-tenancy mode: name prefix for management roles. Only roles whose names start with this prefix are treated as management identities (can log in remotely, can hold the REPLICATION privilege, and can exercise tencentdb_superuser privileges), and are not affected by tenant database view isolation restrictions.
tencentdb_serverless.throttle_replay_database
postmaster
postgres
Resource isolation: limits the quota replay worker processes to read only the databases specified in the restriction configuration, with postgres as the default database.
Note:
Parameters marked as "maintained by the platform" are not visible in the console and are intended for platform-side configuration only, so you do not need to pay attention to them.
The CPU limit for tenant databases (Chapter 2) does not depend on the min_cpu_cores/max_cpu_cores parameters. It is configured entirely on a per-database basis through the SQL interface of tencentdb_serverless.

Usage

The following example assumes that the multi-tenant instance already has the administrator root (a member of tencentdb_superuser), the tenant role tenant_a and its database tenant_a_db, and the public read-only role proxy_ro.

View Isolation

In a multi-tenant instance, tenants can see database names, role names, and other information of the entire instance by default through system views such as pg_database and pg_roles. The view isolation feature enables row-level security (RLS) policies on shared system tables so that each tenant can only see information related to their own database.
After the feature is enabled, the following 9 shared system tables (and their related system views) are isolated. The visibility scope for tenants is as follows:
Isolated System Table
Tenant-Visible Scope
pg_database (pg_database view)
Current database only
pg_authid (views such as pg_roles and pg_user)
Roles of the database owner and additional roles from parameter configuration
pg_auth_members (pg_auth_members view)
Membership relationships between visible roles
pg_db_role_setting (pg_db_role_setting view)
Parameter settings for the current database only
pg_shdepend (pg_shdepend view)
Dependencies for the current database only
pg_shdescription (pg_shdescription view)
Comments for the current database and visible roles only
pg_shseclabel (security label view)
Security labels for the current database and visible roles only
pg_subscription (pg_subscription view)
Subscriptions for the current database only
pg_tablespace (pg_tablespace view)
Tablespaces used by the current database only

1. Enabling Isolation

The administrator connects to the tenant database, creates the extension, and calls the enable function:
postgres=# \\c tenant_a_db root
You are now connected to database "tenant_a_db" as user "root".
root=> CREATE EXTENSION tencentdb_serverless;
CREATE EXTENSION
root=> SELECT tencentdb_serverless.enable_system_view_isolation();
NOTICE: policy "p_pg_database" for relation "pg_catalog.pg_database" does not exist, skipping
NOTICE: policy "p_pg_authid" for relation "pg_catalog.pg_authid" does not exist, skipping
NOTICE: policy "p_pg_auth_members" for relation "pg_catalog.pg_auth_members" does not exist, skipping
NOTICE: policy "p_pg_db_role_setting" for relation "pg_catalog.pg_db_role_setting" does not exist, skipping
NOTICE: policy "p_pg_shdepend" for relation "pg_catalog.pg_shdepend" does not exist, skipping
NOTICE: policy "p_pg_shdescription" for relation "pg_catalog.pg_shdescription" does not exist, skipping
NOTICE: policy "p_pg_seclabel" for relation "pg_catalog.pg_shseclabel" does not exist, skipping
NOTICE: policy "p_pg_subscription" for relation "pg_catalog.pg_subscription" does not exist, skipping
NOTICE: policy "p_pg_tablespace" for relation "pg_catalog.pg_tablespace" does not exist, skipping
enable_system_view_isolation
------------------------------

(1 row)
Note:
The NOTICE comes from the DROP POLICY IF EXISTS statement inside the enable script and is a normal message.

2. Configuring Visible Roles (Optional)

By default, tenants can only see their own roles. To allow tenants to see additional public roles (such as proxy_ro), a superuser or administrator must configure a database-level parameter, with multiple roles separated by commas:
postgres=# ALTER DATABASE tenant_a_db SET tencentdb_multi_tenant_visible_roles = 'tenant_a,proxy_ro';
ALTER DATABASE
Note:
It takes effect only for sessions created after the configuration.

3. Verifying the Results

After a tenant logs in and queries system views, they can only see information related to their own database:
tenant_a=> SELECT datname FROM pg_database ORDER BY 1;
datname
-------------
tenant_a_db
(1 row)

tenant_a=> SELECT rolname FROM pg_roles WHERE rolname NOT LIKE 'pg\\_%' ORDER BY 1;
rolname
----------
proxy_ro
tenant_a
(2 rows)

tenant_a=> SELECT spcname FROM pg_tablespace ORDER BY 1;
spcname
------------
pg_default
(1 row)

4. Disabling Isolation

The administrator executes the disable function in the target database, and the tenant is immediately restored to global visibility:
root=> SELECT tencentdb_serverless.disable_system_view_isolation();
disable_system_view_isolation
-------------------------------

(1 row)

tenant_a=> SELECT datname FROM pg_database ORDER BY 1;
datname
-------------
postgres
template0
template1
tenant_a_db
tenant_b_db
tenant_c_db
(6 rows)

Resource Isolation: Database-Level CPU Limits

Set a CPU usage cap for each database (in cores). After the cap is set, the CPU consumption of all sessions in the database (including parallel workers) is accounted for collectively. Any consumption exceeding the cap is converted into waiting, which automatically slows down statements and prevents a single tenant from exhausting the CPU resources of the instance.
The following example is executed in the management database (where the tencentdb_serverless extension is installed), with the limit target being the tenant database tenant_a_db. The sample table big_table (8 million rows) is a business table in the tenant database and is created in the tenant database:
postgres=# \\c tenant_a_db root
You are now connected to database "tenant_a_db" as user "root".
root=> CREATE TABLE big_table AS SELECT g FROM generate_series(1, 8000000) g;
SELECT 8000000
root=> ANALYZE big_table;
ANALYZE

1. Setting CPU Limits

The parameters of set_database_cpu_limit are as follows: database name, CPU lower limit (reserved, not enabled in the current version), and CPU upper limit. The following limits the tenant database to a maximum of 0.5 cores:
root=> SELECT tencentdb_serverless.set_database_cpu_limit('tenant_a_db', 0.1, 0.5);
set_database_cpu_limit
------------------------

(1 row)
The limit takes effect immediately after it is set, and no instance restart is required.

2. Viewing Limit Configurations

root=> SELECT * FROM tencentdb_serverless.resource_limit_view;
database_name | min_cpu_cores | max_cpu_cores | min_mem_kilobytes | max_mem_kilobytes
---------------+---------------+---------------+-------------------+-------------------
tenant_a_db | 0.1 | 0.5 | |
(1 row)
min_mem_kilobytes and max_mem_kilobytes are reserved columns and are not used in the current version.

3. Viewing CPU Usage and Throttling

root=> SELECT * FROM tencentdb_serverless.get_cpu_throttle_usage('tenant_a_db');
cpu_limit | cpu_usage_rate | throttled_count | throttled_time_ms
-----------+----------------+-----------------+-------------------
0.5 | 0 | 0 | 0
(1 row)
cpu_limit: The currently effective CPU upper limit (in cores).
cpu_usage_rate: The average number of CPU cores used by the database within the last 5-second observation window.
throttled_count: The total number of times throttling has been triggered for the database since the limit was first set.
throttled_time_ms: The total number of milliseconds the database has spent waiting due to throttling since the limit was first set.

4. Verifying Throttling Effects

Without a limit set, a full table scan on the sample table takes approximately 0.4 seconds:
root=> EXPLAIN (ANALYZE, COSTS OFF) SELECT count(*) FROM big_table;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Finalize Aggregate (actual time=409.405..410.836 rows=1 loops=1)
-> Gather (actual time=409.301..410.824 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (actual time=407.081..407.083 rows=1 loops=3)
-> Parallel Seq Scan on big_table (actual time=0.033..247.186 rows=2666667 loops=3)
Planning Time: 0.212 ms
Execution Time: 410.958 ms
(8 rows)
After the database cap is tightened to 0.1 cores, the CPU consumption of the same statement is compressed to near the cap, and the execution time becomes approximately 25 times longer. The statement still completes normally without errors:
root=> SELECT tencentdb_serverless.set_database_cpu_limit('tenant_a_db', 0.1, 0.1);
set_database_cpu_limit
------------------------

(1 row)

root=> EXPLAIN (ANALYZE, COSTS OFF) SELECT count(*) FROM big_table;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Finalize Aggregate (actual time=10422.597..10424.092 rows=1 loops=1)
-> Gather (actual time=10422.498..10424.083 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (actual time=10420.262..10420.264 rows=1 loops=3)
-> Parallel Seq Scan on big_table (actual time=0.030..10276.123 rows=2666667 loops=3)
Planning Time: 0.215 ms
Execution Time: 10424.213 ms
(8 rows)
During throttling, you can query the usage view to see that the CPU usage of the database is precisely kept near 0.1 cores, and throttling records are generated:
root=> SELECT * FROM tencentdb_serverless.get_cpu_throttle_usage('tenant_a_db');
cpu_limit | cpu_usage_rate | throttled_count | throttled_time_ms
-----------+---------------------+-----------------+-------------------
0.1 | 0.09571135357027115 | 328 | 46545.3
(1 row)
Note:
The sample statement is executed in parallel (2 workers). cpu_usage_rate is accounted for collectively across all sessions in the database, so the overall usage is kept near the cap.

5. Removing Limits

root=> SELECT tencentdb_serverless.reset_database_limit('tenant_a_db');
reset_database_limit
----------------------

(1 row)

root=> SELECT * FROM tencentdb_serverless.resource_limit_view;
database_name | min_cpu_cores | max_cpu_cores | min_mem_kilobytes | max_mem_kilobytes
---------------+---------------+---------------+-------------------+-------------------
(0 rows)
After deletion, the database immediately returns to the unrestricted state. You can also call tencentdb_serverless.reset_all_database_limit() to delete all limits in the instance at once.

6. Common Errors

An error is reported when the target database does not exist:
root=> SELECT tencentdb_serverless.set_database_cpu_limit('db_not_exists', 0.1, 0.5);
ERROR: Database db_not_exists does not exist.
CONTEXT: PL/pgSQL function tencentdb_serverless.set_database_cpu_limit(text,numeric,numeric) line 9 at RAISE
An error is reported when the CPU upper limit is less than the lower limit:
root=> SELECT tencentdb_serverless.set_database_cpu_limit('tenant_a_db', 0.5, 0.1);
ERROR: max_cpu_cores should larger than min_cpu_cores.
CONTEXT: PL/pgSQL function tencentdb_serverless.set_database_cpu_limit(text,numeric,numeric) line 28 at RAISE

FAQs

Q1: Is view isolation automatically enabled for a newly created tenant database?

A: No. Isolation takes effect on a per-database basis. You need to recreate the extension in the new database and execute the enable function.

Q2: How can tenants view additional public roles?

A: Execute ALTER DATABASE ... SET tencentdb_multi_tenant_visible_roles to add the role to the list. The change takes effect after the tenant reconnects.

Q3: Can roles with the BYPASSRLS attribute bypass view isolation?

A: No. Only superusers and members of the tencentdb_superuser role are exempt.

Q4: Why can't tenants see template0 and template1?

A: The isolation policy only allows access to the current database, so the template database is invisible. This is expected behavior.

Q5: Why do queries become slower without errors after CPU limits are set?

A: This is expected behavior. Throttling slows down statement execution by waiting, but statements still complete normally. You can query get_cpu_throttle_usage to check whether throttling has occurred.

Q6: Do CPU limits affect other tenant databases or the admin database?

A: No. Limits take effect on a per-database basis and only constrain the databases for which they are configured. The default postgres administrative database is not subject to throttling constraints.

Q7: Why is the rate limiting count not reset to zero after limits are removed?

A: throttled_count and throttled_time_ms are cumulative values for the database since the limit was first set. Deleting the limit does not reset them to zero.

Q8: Why does executing set_database_cpu_limit report the error "relation ... does not exist"?

A: Make sure that you run the command in a database where the tencentdb_serverless extension (v1.2 or later) is installed, and that the extension version supports the CPU limit capability. For supported versions, see the relevant documentation.

Related References

Help and Support

Was this page helpful?

Help us improve! Rate your documentation experience in 5 mins.

Feedback