# Execution plan: template database support for fast test isolation
This execution plan implements ADR 002, enabling PostgreSQL template databases
for fast test isolation. The feature reduces per-test overhead from seconds to
milliseconds by cloning pre-migrated template databases instead of
bootstrapping fresh clusters for each test.
**Related documents:**
- ADR: `docs/adr-002-template-database-support.md`
- Design: `docs/zero-config-raii-postgres-test-fixture-design.md`
## 1. Core database lifecycle API
Add database creation and deletion methods to enable programmatic database
management on a running cluster.
### 1.1. Database Data Definition Language (DDL) methods on TestClusterConnection
- [ ] **1.1.1.** Implement `create_database(name: &str) -> BootstrapResult<()>`
- [ ] Add `postgres` crate dependency (`postgres = "0.19"`) to `Cargo.toml`.
- [ ] Connect to `postgres` database using `admin_url()`.
- [ ] Execute `CREATE DATABASE "{name}"` via `batch_execute`.
- [ ] Emit tracing span for observability.
- [ ] Handle error for duplicate database name.
- [ ] **1.1.2.** Implement `drop_database(name: &str) -> BootstrapResult<()>`
- [ ] Execute `DROP DATABASE "{name}"` via `batch_execute`.
- [ ] Handle error for non-existent database.
- [ ] Handle error for database with active connections.
- [ ] **1.1.3.** Implement
`database_exists(name: &str) -> BootstrapResult<bool>`
- [ ] Query `pg_database` catalogue for database name.
- [ ] Return boolean result.
- [ ] **1.1.4.** Add unit tests for DDL methods
- [ ] Test `create_database` creates accessible database.
- [ ] Test `drop_database` removes database.
- [ ] Test `database_exists` returns correct boolean.
- [ ] Test error handling for invalid names.
- [ ] Test error handling for existing databases.
- [ ] **1.1.5.** Add behavioural tests (rstest-bdd)
- [ ] Test database creation and query workflow.
- [ ] Test error scenarios (duplicate names, non-existent drops).
**Files:**
- `Cargo.toml` — add `postgres` dependency
- `src/cluster/connection.rs` — add DDL methods
- `tests/database_lifecycle.rs` — unit tests
- `tests/database_lifecycle_bdd.rs` — behavioural tests
### 1.2. Delegation methods on TestCluster
- [ ] **1.2.1.** Add convenience wrappers on `TestCluster`
- [ ] `create_database(name)` delegates to
`self.connection().create_database(name)`.
- [ ] `drop_database(name)` delegates to
`self.connection().drop_database(name)`.
- [ ] `database_exists(name)` delegates to
`self.connection().database_exists(name)`.
- [ ] **1.2.2.** Add unit tests for delegation methods
- [ ] Verify delegation produces same results as direct connection calls.
**Files:**
- `src/cluster/mod.rs` — add delegation methods
### 1.3. Documentation updates
- [ ] **1.3.1.** Update ADR 002 with `postgres` crate choice rationale
- [ ] **1.3.2.** Document APIs in `docs/users-guide.md`
- [ ] Document `create_database`, `drop_database`, `database_exists`.
- [ ] Provide usage example.
## 2. Shared cluster fixture
Add a process-global shared cluster fixture to eliminate per-test cluster
bootstrap overhead.
### 2.1. Shared cluster function
- [ ] **2.1.1.** Implement
`shared_cluster() -> BootstrapResult<&'static TestCluster>`
- [ ] Use `OnceLock<TestCluster>` for lazy initialization.
- [ ] Use `get_or_try_init` for fallible initialization.
- [ ] Handle worker environment setup via existing `ensure_worker_env()`.
- [ ] **2.1.2.** Implement rstest fixture wrapper
- [ ] Add `#[fixture] shared_test_cluster() -> &'static TestCluster`.
- [ ] Panic with `SKIP-TEST-CLUSTER:` prefix on bootstrap failure.
- [ ] **2.1.3.** Add unit tests
- [ ] Test multiple calls return same instance (pointer equality).
- [ ] Test thread-safe concurrent access.
- [ ] Test bootstrap error handling.
- [ ] **2.1.4.** Add behavioural tests
- [ ] Test fixture reuse across multiple tests in same binary.
- [ ] Test environment variable inheritance.
**Files:**
- `src/test_support/fixtures.rs` — add `shared_cluster()` and fixture
- `src/test_support/mod.rs` — export new fixture
- `tests/shared_cluster.rs` — unit tests
- `tests/shared_cluster_bdd.rs` — behavioural tests
### 2.2. Documentation updates
- [ ] **2.2.1.** Document shared fixture pattern in `docs/users-guide.md`
- [ ] Explain when to use shared vs per-test cluster.
- [ ] Provide complete example with template pattern.
## 3. Template support with concurrency safety
Add template database cloning and concurrency-safe template creation.
### 3.1. Template cloning
- [ ] **3.1.1.** Implement
`create_database_from_template(name: &str, template: &str) -> BootstrapResult<()>`
- [ ] Execute `CREATE DATABASE "{name}" TEMPLATE "{template}"`.
- [ ] Handle error for non-existent template.
- [ ] Handle error for template with active connections.
- [ ] **3.1.2.** Add unit tests
- [ ] Test template cloning creates independent database.
- [ ] Test cloned database has same schema as template.
- [ ] Test error handling for non-existent template.
**Files:**
- `src/cluster/connection.rs` — add `create_database_from_template`
- `tests/template_database.rs` — unit tests
### 3.2. Concurrent-safe template creation
- [x] **3.2.1.** Add `dashmap` dependency (`dashmap = "6"`) to `Cargo.toml`
- [x] **3.2.2.** Implement the template creation seam used by
`TestClusterConnection::ensure_template_exists`
- [x] Use `StdTemplateLocks` and `TemplateLockOps` with
`DashMap<String, Arc<Mutex<()>>>` for per-template locking.
- [x] Expose `STD_TEMPLATE_LOCKS`, `TemplateCreationOps`, and
`ensure_template_exists_with_lock` internally so production code and
Loom tests share the same coordination path.
- [x] Acquire the per-template lock before checking existence.
- [x] Call `TemplateCreationOps::setup_fn` only if the template does not
exist and creation succeeds.
- [x] Release the lock after setup or rollback completes.
- [x] **3.2.3.** Add unit tests
- [x] Test rollback after setup failure.
- [x] Test rollback failure preserves setup-error context.
- [x] Test rollback after setup panic, including successful panic resume and
rollback-failure conversion to `BootstrapError`.
- [x] Test creation panic does not roll back a template that this invocation
did not create.
Concurrency safety is additionally verified by Loom model checks in
`src/cluster/lifecycle_loom_tests.rs`:
`same_template_setup_runs_once_under_race` and
`different_template_setups_do_not_deadlock`. Run these under the `loom-tests`
feature as documented in `docs/developers-guide.md`.
- [ ] **3.2.4.** Add behavioural tests
- [ ] Test full template workflow (create, migrate, clone).
- [ ] Test concurrent test template creation.
**Files:**
- `Cargo.toml` — add `dashmap` dependency
- `src/cluster/lifecycle_template/mod.rs` — per-template lock seam,
`TemplateCreationOps`, `ensure_template_exists_with_lock`, and rollback
helpers
- `src/cluster/lifecycle_template/tests.rs` — rollback unit tests
- `src/cluster/lifecycle.rs` — production
`TestClusterConnection::ensure_template_exists` integration
- `src/cluster/lifecycle_loom_tests.rs` — Loom concurrency tests
### 3.3. Migration directory hashing
- [ ] **3.3.1.** Add `sha2` dependency (`sha2 = "0.10"`) to `Cargo.toml`
- [ ] **3.3.2.** Implement
`hash_directory(path: &Path) -> BootstrapResult<String>`
- [ ] Collect all files in directory recursively, sorted by path.
- [ ] Compute SHA-256 hash of concatenated file contents.
- [ ] Return first 12 hexadecimal characters.
- [ ] **3.3.3.** Add unit tests
- [ ] Test `hash_directory` produces consistent hashes.
- [ ] Test hash changes when file contents change.
- [ ] Test hash changes when files are added or removed.
- [ ] Test empty directory produces valid hash.
- [ ] Test non-existent directory returns error.
**Files:**
- `Cargo.toml` — add `sha2` dependency
- `src/cluster/hash.rs` — new module for hashing utilities
- `src/cluster/mod.rs` — declare `hash` module
- `tests/hash_directory.rs` — unit tests
### 3.4. Documentation updates
- [ ] **3.4.1.** Update ADR 002 with migration hashing decision
- [ ] Record directory hash approach.
- [ ] Record 12 hex character truncation.
- [ ] **3.4.2.** Document template pattern in `docs/users-guide.md`
- [ ] Explain template workflow with migration hashing.
- [ ] Provide complete code example.
## 4. Documentation
Comprehensive documentation of the template database pattern.
### 4.1. User guide updates
- [ ] **4.1.1.** Add "Template databases for fast test isolation" section
- [ ] Explain the problem (per-test bootstrap overhead).
- [ ] Explain the solution (shared cluster + template cloning).
- [ ] Provide complete example with all APIs.
- [ ] **4.1.2.** Add migration hashing example
- [ ] Show `hash_directory` usage for template naming.
- [ ] Explain when templates are invalidated.
- [ ] **4.1.3.** Add performance comparison
- [ ] Compare per-test cluster vs shared cluster with templates.
- [ ] Provide guidance on when to use each approach.
- [ ] **4.1.4.** Add cleanup strategy guidance
- [ ] Explain explicit `drop_database` vs cluster teardown.
- [ ] Explain `TemporaryDatabase` guard (Phase 5).
**Files:**
- `docs/users-guide.md` — add template database section
### 4.2. README updates
- [ ] **4.2.1.** Add template support to features section
- [ ] Brief mention of `shared_cluster()` fixture.
- [ ] Brief mention of template cloning.
- [ ] Link to user guide for details.
**Files:**
- `README.md` — update features section
## 5. Cleanup automation
Add Resource Acquisition Is Initialization (RAII) guard for automatic database
cleanup.
### 5.1. TemporaryDatabase struct
- [ ] **5.1.1.** Implement `TemporaryDatabase` struct
- [ ] Store database name and admin URL (not borrowed connection).
- [ ] Implement `name(&self) -> &str` accessor.
- [ ] Implement `url(&self) -> String` for database connection URL.
- [ ] **5.1.2.** Implement `drop(self) -> BootstrapResult<()>`
- [ ] Drop database, failing if connections exist.
- [ ] Mirror PostgreSQL native behaviour.
- [ ] **5.1.3.** Implement `force_drop(self) -> BootstrapResult<()>`
- [ ] Terminate active connections via `pg_terminate_backend`.
- [ ] Drop database after connections terminated.
- [ ] **5.1.4.** Implement `Drop` trait
- [ ] Best-effort drop with `try_drop()`.
- [ ] Log warning on failure via `tracing::warn!`.
- [ ] **5.1.5.** Add unit tests
- [ ] Test `TemporaryDatabase` drops database on scope exit.
- [ ] Test `drop()` fails with active connections.
- [ ] Test `force_drop()` succeeds with active connections.
- [ ] Test warning logged on `Drop` failure.
**Files:**
- `src/cluster/temporary_database.rs` — new module
- `src/cluster/mod.rs` — declare and export module
- `tests/temporary_database.rs` — unit tests
### 5.2. Factory methods
- [ ] **5.2.1.** Add factory methods to `TestClusterConnection`
- [ ] `temporary_database(name) -> BootstrapResult<TemporaryDatabase>`.
- [ ] `temporary_database_from_template(name, template) -> BootstrapResult<TemporaryDatabase>`.
- [ ] **5.2.2.** Add delegation methods to `TestCluster`
- [ ] Delegate to connection methods.
- [ ] **5.2.3.** Add behavioural tests
- [ ] Test RAII cleanup in test context.
- [ ] Test `force_drop` terminates connections.
**Files:**
- `src/cluster/connection.rs` — add factory methods
- `src/cluster/mod.rs` — add delegation methods
- `tests/temporary_database_bdd.rs` — behavioural tests
### 5.3. Documentation updates
- [ ] **5.3.1.** Document `TemporaryDatabase` in `docs/users-guide.md`
- [ ] Explain RAII cleanup semantics.
- [ ] Document `drop()` vs `force_drop()`.
- [ ] Provide usage example.
## Success criteria
- [ ] All unit tests pass (`make test`).
- [ ] All behavioural tests pass.
- [ ] Clippy passes with no warnings (`make lint`).
- [ ] Formatting validated (`make check-fmt`).
- [ ] Markdown validated (`make markdownlint`).
- [ ] ADR 002 updated with implementation decisions.
- [ ] User guide documents all new APIs.
- [ ] Consumer codebases (`../wildside/backend`, `../mxd`) can adopt template
pattern with minimal changes.