Loading…
Loading…
Configure the runtime service's database connection pool, including IAM and password authentication, multi-tenancy, and row-level security (RLS).
This document describes the configuration of the primary database DataSource for the runtime service. This component is responsible for creating and managing a pool of database connections used by the application for all its database operations.
The configuration establishes a HikariDataSource connection pool, tuned for performance and resilience. It supports two primary authentication methods:
Beyond connection management, this configuration integrates critical features for multi-tenancy and security. It wraps the data source to bind a tenant context to each connection, a prerequisite for row-level security (RLS). It also enforces RLS by switching the database role for each connection and includes a health check to verify that RLS is active.
The system initializes the database DataSource bean through a sequence of configuration and wrapping steps. This process ensures that connections are resilient, secure, and correctly configured for a multi-tenant environment.
flowchart TD
subgraph "1. Load Configuration"
A[Environment Variables & Application Properties]
end
subgraph "2. Initialize Connection Pool"
B(Create HikariDataSource)
C{IAM Auth<br>Enabled?}
D[Configure for IAM Auth<br>(Cloud SQL Connector)]
E[Configure for Password Auth<br>(JDBC URL)]
F[Set Common Pool Properties<br>(size, lifetime)]
G[Set Resilience Timeouts<br>(socket, leak detection)]
H[Set RLS Role-Switching SQL<br>(connectionInitSql)]
end
subgraph "3. Apply Multi-Tenancy Wrappers"
I(Wrap with TenantBindingDataSource)
J(Install SaaS Unbound Connection Gate)
end
subgraph "4. Verify at Startup"
K(ApplicationReadyEvent)
L[Probe RLS Status]
M[Update 'rlsRole' Health Check]
end
A --> B
B --> F
F --> C
C -- Yes --> D
C -- No --> E
D --> G
E --> G
G --> H
H --> I
I --> J
J --> Z[DataSource Bean Ready]
K --> L
L --> MS2R_DB_IAM_AUTH_ENABLED environment variable and the authMode property. If IAM is enabled and the mode is iam, it proceeds with IAM configuration. Otherwise, it falls back to password-based authentication.HikariDataSource instance is created. It is configured with common pool settings (e.g., maximumPoolSize, maxLifetime) and crucial resilience settings. A socketTimeout is applied to prevent borrowed connections from getting stuck on dead sockets during network partitions, and a leakDetectionThreshold is set to detect anomalously long-running operations.com.google.cloud.sql.postgres.SocketFactory and sets properties like cloudSqlInstance. It does not provide a password, as the connector manages token-based authentication.connectionInitSql property is set on the data source. This SQL statement runs every time a new connection is created, switching the session's role to s2r_runtime_rls. This ensures that row-level security policies are applied to all queries from the runtime service.HikariDataSource is wrapped in a TenantBindingDataSource. This wrapper intercepts connection borrowing to associate the current tenant's ID with the database session via the app.tenant_id setting.DataSource. In a SaaS deployment, this gate acts as a fail-closed mechanism, preventing any database operation from proceeding on a connection that does not have a tenant ID bound.rlsRole health contributor, which is exposed via the actuator health endpoint. In a SaaS environment, if RLS is not active, the service will report as DOWN.The database connection is configured using a set of properties, typically provided as environment variables.
IMPORTANT
At a minimum, S2R_DB_USER and S2R_DB_NAME must always be configured. Other required properties depend on the chosen authentication method.
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
S2R_DB_IAM_AUTH_ENABLED | Boolean | true | No | If false, forces password authentication. Otherwise, the authMode property is used. |
authMode | String | iam | No | Determines the authentication strategy. Set to a value other than iam (case-insensitive) to use password authentication. |
S2R_DB_USER | String | — | Yes | The database user for the connection pool. |
S2R_DB_NAME | String | — | Yes | The name of the database to connect to. |
These properties are used when IAM authentication is active.
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
S2R_DB_INSTANCE_CONNECTION_NAME | String | — | Yes | The Cloud SQL instance connection name (e.g., project:region:instance). |
ipType | String | PRIVATE | No | The preferred IP type for the connection (e.g., PRIVATE, PUBLIC). |
These properties are used when IAM authentication is disabled.
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
S2R_DB_HOST | String | — | Yes | The hostname or IP address of the database server. |
S2R_DB_PORT | Integer | — | Yes | The port number of the database server. |
S2R_DB_PASSWORD | String | — | Yes | The password for the database user. |
sslMode | String | require | No | The SSL mode for the JDBC connection (e.g., require, verify-full). |
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
socketTimeoutSeconds | Integer | — | No | The timeout in seconds for socket read operations. Helps the pool recover from network partitions by abandoning reads on dead sockets. If set, this also enables the leakDetectionThreshold. |
connectTimeoutSeconds | Integer | — | No | The timeout in seconds for establishing a new database connection. |
The HikariCP connection pool is configured with the following static values for performance and stability.
| Property | Value | Description |
|---|---|---|
poolName | s2r-runtime-hikari | The name of the connection pool, visible in logs and metrics. |
maximumPoolSize | 20 | The maximum number of connections the pool can maintain. |
minimumIdle | 2 | The minimum number of idle connections the pool will attempt to maintain. |
connectionTimeout | 3000 ms | The maximum time to wait for a connection from the pool. |
validationTimeout | 2000 ms | The maximum time to wait for a connection to be validated. |
initializationFailTimeout | -1 | If -1, the service will fail to start if the pool cannot be initialized. |
maxLifetime | 30 minutes | The maximum lifetime of a connection in the pool. After this time, it will be retired upon being returned to the pool. |
keepaliveTime | 10 minutes | The frequency at which keepalive probes are sent to idle connections to prevent them from being terminated by the network. |
leakDetectionThreshold | 2 * socketTimeoutSeconds | If socketTimeoutSeconds is set, this is the time a connection can be borrowed before a leak is suspected and logged. |
To ensure multi-tenant data isolation, the runtime service relies on the database's row-level security capabilities. The DataSource configuration is instrumental in enforcing RLS.
Every connection borrowed from the pool executes an initial SQL statement to switch its role to s2r_runtime_rls. The application's login user may own database tables, which would cause it to bypass RLS policies. By switching to a dedicated, non-login role that does not own tables but has the necessary privileges, RLS policies are guaranteed to apply to all queries.
The SQL statement is designed to be safe and resilient:
s2r_runtime_rls role exists.SET ROLE.SET ROLE command is refused by the database, it raises a WARNING but does not throw an error, preventing the connection pool from failing.DO $$ BEGIN
IF EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 's2r_runtime_rls') THEN
IF pg_catalog.pg_has_role(session_user, 's2r_runtime_rls', 'MEMBER') THEN
BEGIN EXECUTE 'SET ROLE s2r_runtime_rls';
EXCEPTION WHEN insufficient_privilege THEN
RAISE WARNING 'S2R-RLS-ROLE SET ROLE s2r_runtime_rls refused: %', SQLERRM;
END;
END IF;
END IF;
END $$
The service includes a health contributor named rlsRole that is exposed via the actuator health endpoint (e.g., /actuator/health). At application startup, this component probes the database to confirm that RLS is active on the table public.s2r_service for the connections in the pool.
rlsRole contributor will report DOWN. This causes the application's overall readiness status to become DOWN, preventing it from receiving traffic.WARN message, but the service health is not affected.| Condition | Trigger | Meaning and Resolution |
|---|---|---|
IllegalStateException: S2R_DB_USER must be configured | The S2R_DB_USER property is not set. | The application cannot start without a database username. Set the S2R_DB_USER environment variable. |
IllegalStateException: S2R_DB_INSTANCE_CONNECTION_NAME required for IAM auth | IAM authentication is active, but S2R_DB_INSTANCE_CONNECTION_NAME is missing. | Set the S2R_DB_INSTANCE_CONNECTION_NAME environment variable to the correct Cloud SQL instance string. |
IllegalStateException: S2R_DB_HOST must be configured for password auth | Password authentication is active, but S2R_DB_HOST is missing. | Set the S2R_DB_HOST environment variable to the database server's hostname. |
Log: S2R-RLS-ROLE ... rlsActive=false in SaaS mode: readiness reports DOWN | The RLS health probe found that RLS policies are not being applied to the runtime's connections in a SaaS environment. | This is a critical misconfiguration. The service will not become ready. The log provides a fix: run scripts/db-bootstrap/runtime-app-rls-role.sql as a privileged role ... then restart the runtime. |
Log: S2R-RLS-ROLE ... rlsActive=false: row-level security does not constrain the runtime | Same as above, but in a non-SaaS environment. | This is a warning that data isolation is not enforced at the database level. Follow the fix suggested in the log message to enable RLS. |
Log: S2R-RLS-ROLE probe failed in SaaS mode - readiness stays DOWN | The health probe's query to the database failed. | In a SaaS environment, this is a critical failure. Check database connectivity and permissions. The service will not become ready until a probe succeeds. |
Log: S2R-RLS-ROLE SET ROLE ... refused | The connectionInitSql failed to switch the database role. | This is a warning. It means the login user (session_user) has not been granted membership in the s2r_runtime_rls role. RLS will not be active for this connection. |
Log: HikariPool-x - Connection is not available, request timed out after 3000ms. | The connection pool is exhausted, and a request for a new connection timed out. | This may indicate high load or, more critically, stuck connections. Check for leakDetectionThreshold warnings to identify long-running queries that may be starving the pool. |
rlsRole contributor at the actuator health endpoint.TenantBindingDataSource and SettingBackedSaasCapability components are central to the platform's multi-tenancy implementation.