The @concourse/rsdb contract, connection verifier, and test layers
@concourse/rsdb is a private, server-only workspace package. It owns database selection,
generated schema loading, Drizzle configuration, TLS rules, and MySQL pool lifecycle. It also owns
the reusable MySQL discovery and generation logic behind a server-only ./probe entry for the
local probe and Step 1 tracer.
The package exports these generated values and types:
RackspaceDatabaseName, which is the union of registered database names.databaseNames, which is the stable, ordered list of registered database names.Before the first merged snapshot, the registry is empty. An empty registry makes
RackspaceDatabaseName the never type. The package must compile and pass its checks with an
empty registry. The validator must reject every name, and the factory must fail before any
connection attempt.
The package also exports a configured factory. The caller supplies the host, port, runtime credentials, TLS settings, and pool settings. The package does not read environment variables.
The factory has an asynchronous database-selection method. This method accepts only a registered database name. It returns a discriminated handle with these values:
A literal database name retains its exact schema type. A runtime-selected name returns the related discriminated union. The caller narrows that union by its database name before a schema-specific query.
The first version does not accept arbitrary database strings. It does not provide public subpath exports for individual databases. An unknown name fails before the package opens a MySQL connection.
The factory creates each MySQL pool only when a caller selects its database. It caches one pool and one Drizzle handle for each selected database. A repeated selection reuses that handle. The factory provides one method to close a selected database pool and another method to close all pools that the factory owns.
The runtime account permits application data reads and writes through Drizzle. The package does not run Drizzle Kit, apply migrations, or change schema objects. MySQL grants enforce these limits.
The implementation uses one server-only connection verifier in @concourse/rsdb. Local tests, the
refresh workflow, and the administrator page call this verifier. Each caller adds its own setup
and result handling.
The verifier accepts one of two object-list sources:
Candidate verification runs plain zero-row SELECT statements from that metadata. It never loads
or executes candidate TypeScript. Compile and load checks for generated TypeScript happen in
pull-request CI and in the deployed verification of merged schemas.
The verifier performs these checks in order:
SELECT DATABASE() and compare the result with the selected database name.SELECT with the listed columns and LIMIT 0 against each object.The object query must select the generated columns. MySQL must resolve each generated table, view,
and column identifier. LIMIT 0 prevents the query from returning application rows.
For a merged database, the verifier loads the generated relations when it creates the Drizzle client. This check detects relation-module load and Drizzle configuration failures. The generic verifier does not run business queries because the Rackspace databases can have unrelated schemas.
The verifier returns a structured result with these fields:
The verifier uses these stable error codes:
| Code | Meaning |
|---|---|
invalid_name | The database is absent from the applicable registry |
connection_failed | The MySQL connection did not open |
tls_failed | Certificate validation failed or the session has no cipher |
database_mismatch | SELECT DATABASE() returned a different database |
schema_load_failed | The generated schema or relations did not load |
object_query_failed | A generated table, view, or column did not resolve |
verification_timeout | A query or complete database verification exceeded its limit |
An empty registered database can pass with zero queryable objects. Its result records zero tables and zero views.
A transient connection error uses the refresh retry policy. A TLS error, database mismatch, module load error, or object-query error is permanent for that run. Every attempt has query and database timeouts. The implementation must select the timeout values during representative load tests.
The verifier never writes production data. Data-write verification uses only local fixtures or a dedicated non-production validation database. A write test must use a transaction and roll it back.
The package integration suite runs in CI with a local MySQL test service. The repository does not have MySQL infrastructure yet. The implementation must add a MySQL container for local development and CI, and MySQL lifecycle helpers for tests. The suite does not use Rackspace credentials, Vercel Static IPs, or external network access.
The suite creates two database fixtures with different schemas. The fixtures include tables, views, foreign keys, and relations. They also include different table and column names so that an incorrect database-to-schema mapping fails.
The test setup creates two MySQL accounts:
The suite uses checked-in generated fixture modules for compile-time type assertions. It also runs the registry generator against those fixtures and writes its output to a temporary directory.
A documented script regenerates the checked-in fixture modules from the fixture databases. A drift test compares the checked-in modules with fresh Drizzle Kit output and fails when they differ. This test keeps the fixtures honest when the pinned Drizzle Kit version changes.
The CI suite must verify these behaviors:
The test suite uses the repository's Node test runner and the new MySQL lifecycle helpers. It does not add a second test framework. The MySQL test service exists only for this package and does not change the application database.
Each database step runs a live verification after Drizzle Kit and exact DDL discovery finish. The step must complete verification before it adds the candidate to the review artifact.
The new schema is not in the merged registry yet. The step therefore reads the table, view, and
column list from the generated snapshot metadata in its unique /tmp directory.
The database step supplies that candidate object list to the shared verifier. The step never loads, bundles, or executes candidate TypeScript. The verifier uses the runtime account for the live query checks. The snapshot account remains limited to inventory and definition discovery.
The candidate verification checks the selected database only. It never queries an unselected database. Multiple selected databases continue to run in sequence.
A candidate gets a changed or unchanged outcome only after every verification passes. A
failure gets the failed outcome. A prior snapshot gives that result stale freshness.
The pull-request report includes the successful verification summary for each published database. It also includes the failed stage, failed object, and sanitized error for each rejected candidate.
The pull-request CI run performs the full package type check after publication. The deployed administrator verification checks runtime loading after merge. A generated schema that compiles but fails to load surfaces in that deployed verification, not before merge. This gap is accepted because the alternative executes unreviewed generated code inside the refresh workflow.
The schema snapshot administrator page provides a separate connection-verification action. Only a
fresh systemAdmin session can start it. An impersonated session cannot use the action.
The page uses databaseNames from the merged package registry. It does not use the live inventory
because an inventory database can lack a generated schema.
The page provides search, one-database selection, multiple selection, select-all, and clear controls. An empty selection is invalid. The administrator can rerun one failed database without selecting unrelated databases.
The action starts a durable verification workflow in the deployed Vercel application. The workflow
uses the current @concourse/rsdb package, production runtime account, Static IPs, and TLS
settings.
Each selected database runs in its own sequential step. The step loads the merged registry entry and calls the shared verifier. The workflow continues after one database fails.
The page shows these values for each database:
passed or failed status.The application database stores the workflow result. The result is immutable and remains available through its operation ID after Workflow run retention ends. The action creates no Git object or schema file.
Only one administrator verification workflow can run at a time. A new administrator verification does not start while a refresh workflow is active. The application-database lease enforces these rules. This limit protects Rackspace load and connection pools.
The deployed test is read-only even though the runtime account permits application data writes. A separate non-production rollout check verifies data-write privileges with a rolled-back transaction.
The @concourse/rsdb contract, connection verifier, and test layers
@concourse/rsdb is a private, server-only workspace package. It owns database selection,
generated schema loading, Drizzle configuration, TLS rules, and MySQL pool lifecycle. It also owns
the reusable MySQL discovery and generation logic behind a server-only ./probe entry for the
local probe and Step 1 tracer.
The package exports these generated values and types:
RackspaceDatabaseName, which is the union of registered database names.databaseNames, which is the stable, ordered list of registered database names.Before the first merged snapshot, the registry is empty. An empty registry makes
RackspaceDatabaseName the never type. The package must compile and pass its checks with an
empty registry. The validator must reject every name, and the factory must fail before any
connection attempt.
The package also exports a configured factory. The caller supplies the host, port, runtime credentials, TLS settings, and pool settings. The package does not read environment variables.
The factory has an asynchronous database-selection method. This method accepts only a registered database name. It returns a discriminated handle with these values:
A literal database name retains its exact schema type. A runtime-selected name returns the related discriminated union. The caller narrows that union by its database name before a schema-specific query.
The first version does not accept arbitrary database strings. It does not provide public subpath exports for individual databases. An unknown name fails before the package opens a MySQL connection.
The factory creates each MySQL pool only when a caller selects its database. It caches one pool and one Drizzle handle for each selected database. A repeated selection reuses that handle. The factory provides one method to close a selected database pool and another method to close all pools that the factory owns.
The runtime account permits application data reads and writes through Drizzle. The package does not run Drizzle Kit, apply migrations, or change schema objects. MySQL grants enforce these limits.
The implementation uses one server-only connection verifier in @concourse/rsdb. Local tests, the
refresh workflow, and the administrator page call this verifier. Each caller adds its own setup
and result handling.
The verifier accepts one of two object-list sources:
Candidate verification runs plain zero-row SELECT statements from that metadata. It never loads
or executes candidate TypeScript. Compile and load checks for generated TypeScript happen in
pull-request CI and in the deployed verification of merged schemas.
The verifier performs these checks in order:
SELECT DATABASE() and compare the result with the selected database name.SELECT with the listed columns and LIMIT 0 against each object.The object query must select the generated columns. MySQL must resolve each generated table, view,
and column identifier. LIMIT 0 prevents the query from returning application rows.
For a merged database, the verifier loads the generated relations when it creates the Drizzle client. This check detects relation-module load and Drizzle configuration failures. The generic verifier does not run business queries because the Rackspace databases can have unrelated schemas.
The verifier returns a structured result with these fields:
The verifier uses these stable error codes:
| Code | Meaning |
|---|---|
invalid_name | The database is absent from the applicable registry |
connection_failed | The MySQL connection did not open |
tls_failed | Certificate validation failed or the session has no cipher |
database_mismatch | SELECT DATABASE() returned a different database |
schema_load_failed | The generated schema or relations did not load |
object_query_failed | A generated table, view, or column did not resolve |
verification_timeout | A query or complete database verification exceeded its limit |
An empty registered database can pass with zero queryable objects. Its result records zero tables and zero views.
A transient connection error uses the refresh retry policy. A TLS error, database mismatch, module load error, or object-query error is permanent for that run. Every attempt has query and database timeouts. The implementation must select the timeout values during representative load tests.
The verifier never writes production data. Data-write verification uses only local fixtures or a dedicated non-production validation database. A write test must use a transaction and roll it back.
The package integration suite runs in CI with a local MySQL test service. The repository does not have MySQL infrastructure yet. The implementation must add a MySQL container for local development and CI, and MySQL lifecycle helpers for tests. The suite does not use Rackspace credentials, Vercel Static IPs, or external network access.
The suite creates two database fixtures with different schemas. The fixtures include tables, views, foreign keys, and relations. They also include different table and column names so that an incorrect database-to-schema mapping fails.
The test setup creates two MySQL accounts:
The suite uses checked-in generated fixture modules for compile-time type assertions. It also runs the registry generator against those fixtures and writes its output to a temporary directory.
A documented script regenerates the checked-in fixture modules from the fixture databases. A drift test compares the checked-in modules with fresh Drizzle Kit output and fails when they differ. This test keeps the fixtures honest when the pinned Drizzle Kit version changes.
The CI suite must verify these behaviors:
The test suite uses the repository's Node test runner and the new MySQL lifecycle helpers. It does not add a second test framework. The MySQL test service exists only for this package and does not change the application database.
Each database step runs a live verification after Drizzle Kit and exact DDL discovery finish. The step must complete verification before it adds the candidate to the review artifact.
The new schema is not in the merged registry yet. The step therefore reads the table, view, and
column list from the generated snapshot metadata in its unique /tmp directory.
The database step supplies that candidate object list to the shared verifier. The step never loads, bundles, or executes candidate TypeScript. The verifier uses the runtime account for the live query checks. The snapshot account remains limited to inventory and definition discovery.
The candidate verification checks the selected database only. It never queries an unselected database. Multiple selected databases continue to run in sequence.
A candidate gets a changed or unchanged outcome only after every verification passes. A
failure gets the failed outcome. A prior snapshot gives that result stale freshness.
The pull-request report includes the successful verification summary for each published database. It also includes the failed stage, failed object, and sanitized error for each rejected candidate.
The pull-request CI run performs the full package type check after publication. The deployed administrator verification checks runtime loading after merge. A generated schema that compiles but fails to load surfaces in that deployed verification, not before merge. This gap is accepted because the alternative executes unreviewed generated code inside the refresh workflow.
The schema snapshot administrator page provides a separate connection-verification action. Only a
fresh systemAdmin session can start it. An impersonated session cannot use the action.
The page uses databaseNames from the merged package registry. It does not use the live inventory
because an inventory database can lack a generated schema.
The page provides search, one-database selection, multiple selection, select-all, and clear controls. An empty selection is invalid. The administrator can rerun one failed database without selecting unrelated databases.
The action starts a durable verification workflow in the deployed Vercel application. The workflow
uses the current @concourse/rsdb package, production runtime account, Static IPs, and TLS
settings.
Each selected database runs in its own sequential step. The step loads the merged registry entry and calls the shared verifier. The workflow continues after one database fails.
The page shows these values for each database:
passed or failed status.The application database stores the workflow result. The result is immutable and remains available through its operation ID after Workflow run retention ends. The action creates no Git object or schema file.
Only one administrator verification workflow can run at a time. A new administrator verification does not start while a refresh workflow is active. The application-database lease enforces these rules. This limit protects Rackspace load and connection pools.
The deployed test is read-only even though the runtime account permits application data writes. A separate non-production rollout check verifies data-write privileges with a rolled-back transaction.