# Installation Guide ## Prerequisites | Requirement | Version | |---|---| | PostgreSQL | 18.x | > **Building from source** additionally requires Rust 1.82+ and pgrx 0.17.x. > Pre-built release artifacts only need a running PostgreSQL 18.x instance. --- ## Installing from a Pre-built Release ### 1. Download the release archive Download the archive for your platform from the [GitHub Releases](../../releases) page: | Platform | Archive | |---|---| | Linux x86_64 | `pg_trickle--pg18-linux-amd64.tar.gz` | | macOS Apple Silicon | `pg_trickle--pg18-macos-arm64.tar.gz` | | Windows x64 | `pg_trickle--pg18-windows-amd64.zip` | Optionally verify the checksum against `SHA256SUMS.txt` from the same release: ```bash sha256sum -c SHA256SUMS.txt ``` ### 2. Extract and install **Linux / macOS:** ```bash tar xzf pg_trickle--pg18-linux-amd64.tar.gz cd pg_trickle--pg18-linux-amd64 sudo cp lib/*.so "$(pg_config --pkglibdir)/" sudo cp extension/*.control extension/*.sql "$(pg_config --sharedir)/extension/" ``` **Windows (PowerShell):** ```powershell Expand-Archive pg_trickle--pg18-windows-amd64.zip -DestinationPath . cd pg_trickle--pg18-windows-amd64 Copy-Item lib\*.dll "$(pg_config --pkglibdir)\" Copy-Item extension\* "$(pg_config --sharedir)\extension\" ``` ### 3. Using with CloudNativePG (Kubernetes) pg_trickle is distributed as an OCI extension image for use with [CloudNativePG Image Volume Extensions](https://cloudnative-pg.io/docs/1.28/imagevolume_extensions/). **Requirements:** Kubernetes 1.33+, CNPG 1.28+, PostgreSQL 18. ```bash # Pull the extension image docker pull ghcr.io/grove/pg_trickle-ext: ``` See [cnpg/cluster-example.yaml](cnpg/cluster-example.yaml) and [cnpg/database-example.yaml](cnpg/database-example.yaml) for complete Cluster and Database deployment examples. ### 4. Local Docker development (without Kubernetes) For local development without Kubernetes, install the extension files manually into a standard PostgreSQL container from a release archive: ```bash # Extract extension files from the release archive tar xzf pg_trickle--pg18-linux-amd64.tar.gz cd pg_trickle--pg18-linux-amd64 # Run PostgreSQL with the extension mounted docker run --rm \ -v $PWD/lib/pg_trickle.so:/usr/lib/postgresql/18/lib/pg_trickle.so:ro \ -v $PWD/extension/:/tmp/ext/:ro \ -e POSTGRES_PASSWORD=postgres \ postgres:18.3 \ sh -c 'cp /tmp/ext/* /usr/share/postgresql/18/extension/ && \ exec postgres -c shared_preload_libraries=pg_trickle' ``` --- ## Building from Source ### 1. Install Rust ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` ### 2. Install pgrx ```bash cargo install --locked cargo-pgrx --version 0.17.0 cargo pgrx init --pg18 $(pg_config --bindir)/pg_config ``` ### 3. Build the Extension ```bash # Development build (faster compilation) cargo pgrx install --pg-config $(pg_config --bindir)/pg_config # Release build (optimized, for production) cargo pgrx install --release --pg-config $(pg_config --bindir)/pg_config # Package for deployment (creates installable artifacts) cargo pgrx package --pg-config $(pg_config --bindir)/pg_config ``` ## PostgreSQL Configuration Add the following to `postgresql.conf` **before starting PostgreSQL**: ```ini # Required — loads the extension shared library at server start shared_preload_libraries = 'pg_trickle' # Must accommodate scheduler + optional parallel refresh workers. # Default (8) is sufficient for sequential mode (1 slot per database). # For parallel refresh (pg_trickle.parallel_refresh_mode = 'on'), budget: # 1 (launcher) + N (scheduler per DB) + max_dynamic_refresh_workers # + autovacuum workers + parallel query workers + other extensions # A 2-database deployment with 4 parallel workers typically needs ≥16. max_worker_processes = 16 ``` > **Note:** `wal_level = logical` and `max_replication_slots` are **not** required. The extension uses lightweight row-level triggers for CDC, not logical replication. Restart PostgreSQL after modifying these settings: ```bash pg_ctl restart -D /path/to/data # or systemctl restart postgresql ``` ## Extension Installation Connect to the target database and run: ```sql CREATE EXTENSION pg_trickle; ``` This creates: - The `pgtrickle` schema with catalog tables and SQL functions - The `pgtrickle_changes` schema for change buffer tables - Event triggers for DDL tracking - The `pgtrickle.pg_stat_stream_tables` monitoring view ## Verification After installation, verify everything is working: ```sql -- Check the extension version SELECT extname, extversion FROM pg_extension WHERE extname = 'pg_trickle'; -- Or get a full status overview (includes version, scheduler state, stream table count) SELECT * FROM pgtrickle.pgt_status(); ``` ### Inspecting the installation ```sql -- Check the installed version SELECT extversion FROM pg_extension WHERE extname = 'pg_trickle'; -- Check which schemas were created SELECT schema_name FROM information_schema.schemata WHERE schema_name IN ('pgtrickle', 'pgtrickle_changes'); -- Check all registered GUC variables SHOW pg_trickle.enabled; SHOW pg_trickle.scheduler_interval_ms; SHOW pg_trickle.max_concurrent_refreshes; -- Check the scheduler background worker is running SELECT * FROM pgtrickle.pgt_status(); -- List all stream tables SELECT pgt_schema, pgt_name, status, refresh_mode, is_populated FROM pgtrickle.pgt_stream_tables; -- Check that the shared library loaded correctly SELECT * FROM pg_extension WHERE extname = 'pg_trickle'; -- Verify the catalog tables exist SELECT tablename FROM pg_tables WHERE schemaname = 'pgtrickle' ORDER BY tablename; ``` ### Quick functional test ```sql CREATE TABLE test_source (id INT PRIMARY KEY, val TEXT); INSERT INTO test_source VALUES (1, 'hello'); SELECT pgtrickle.create_stream_table( 'test_st', 'SELECT id, val FROM test_source', '1m', 'FULL' ); SELECT * FROM test_st; -- Should return: 1 | hello -- Clean up SELECT pgtrickle.drop_stream_table('test_st'); DROP TABLE test_source; ``` ## Upgrading To upgrade pg_trickle to a newer version without losing data: > For comprehensive upgrade instructions, version-specific notes, > troubleshooting, and rollback procedures, see [docs/UPGRADING.md](docs/UPGRADING.md). ### 1. Install the new extension files Follow the same steps as [Installing from a Pre-built Release](#installing-from-a-pre-built-release) to overwrite the shared library and SQL files with the new version. You do **not** need to drop the extension from your databases first. **Linux / macOS:** ```bash tar xzf pg_trickle--pg18-linux-amd64.tar.gz cd pg_trickle--pg18-linux-amd64 sudo cp lib/*.so "$(pg_config --pkglibdir)/" sudo cp extension/*.control extension/*.sql "$(pg_config --sharedir)/extension/" ``` ### 2. Restart PostgreSQL (when required) If the shared library ABI has changed, restart PostgreSQL before proceeding so the new `.so`/`.dll` is loaded. The release notes for each version will call this out explicitly when a restart is required. ```bash pg_ctl restart -D /path/to/data # or systemctl restart postgresql ``` ### 3. Apply the schema migration in each database Connect to every database where pg_trickle is installed and run: ```sql -- Upgrade to the latest bundled version ALTER EXTENSION pg_trickle UPDATE; -- Or upgrade to a specific version ALTER EXTENSION pg_trickle UPDATE TO ''; ``` PostgreSQL uses the versioned SQL migration scripts bundled with the release (e.g. `pg_trickle--0.2.3--0.3.0.sql`, `pg_trickle--0.3.0--0.4.0.sql`) to apply catalog and SQL-surface changes. PostgreSQL automatically chains these scripts when you run `ALTER EXTENSION pg_trickle UPDATE`. The command is a no-op when no migration script is needed for a given release. You can confirm the active version afterwards: ```sql SELECT extversion FROM pg_extension WHERE extname = 'pg_trickle'; ``` > **Coming soon:** A future release will include a helper function > (`pgtrickle.upgrade()`) that automates steps 2–3 across all databases in the > cluster and validates catalog integrity after the migration. Until then, the > manual steps above are the supported upgrade path. --- ## Uninstallation ```sql -- Drop all stream tables first SELECT pgtrickle.drop_stream_table(pgt_schema || '.' || pgt_name) FROM pgtrickle.pgt_stream_tables; -- Drop the extension DROP EXTENSION pg_trickle CASCADE; ``` Remove `pg_trickle` from `shared_preload_libraries` in `postgresql.conf` and restart PostgreSQL. ## Troubleshooting ### Unit tests crash on macOS 26+ (`symbol not found in flat namespace`) macOS 26 (Tahoe) changed `dyld` to eagerly resolve all flat-namespace symbols at binary load time. pgrx extensions reference PostgreSQL server-internal symbols (e.g. `CacheMemoryContext`, `SPI_connect`) via the `-Wl,-undefined,dynamic_lookup` linker flag. These symbols are normally provided by the `postgres` executable when the extension is loaded as a shared library — but for `cargo test --lib` there is no postgres process, so the test binary aborts immediately: ``` dyld[66617]: symbol not found in flat namespace '_CacheMemoryContext' ``` **This affects local development only** — integration tests, E2E tests, and the extension itself running inside PostgreSQL are unaffected. The fix is built into the `just test-unit` recipe. It automatically: 1. Compiles a tiny C stub library (`scripts/pg_stub.c` → `target/libpg_stub.dylib`) that provides NULL/no-op definitions for the ~28 PostgreSQL symbols. 2. Compiles the test binary with `--no-run`. 3. Runs the binary with `DYLD_INSERT_LIBRARIES` pointing to the stub. The stub is only built on macOS 26+. On Linux or older macOS, `just test-unit` runs `cargo test --lib` directly with no changes. > **Note:** The stub symbols are never called — unit tests exercise pure Rust > logic only. If a test accidentally calls a PostgreSQL function it will crash > with a NULL dereference (the desired fail-fast behavior). If you run unit tests without `just` (e.g. directly via `cargo test --lib`), you can use the wrapper script instead: ```bash ./scripts/run_unit_tests.sh pg18 # With test name filter: ./scripts/run_unit_tests.sh pg18 -- test_parse_basic ``` ### Extension fails to load Ensure `shared_preload_libraries = 'pg_trickle'` is set and PostgreSQL has been **restarted** (not just reloaded). The extension requires shared memory initialization at startup. ### Background worker not starting Check that `max_worker_processes` is high enough. In sequential mode (default) pg_trickle needs one slot per database with stream tables. With parallel refresh enabled (`pg_trickle.parallel_refresh_mode = 'on'`) it additionally needs `max_dynamic_refresh_workers` slots (default 4) shared across all databases. See the [worker-budget formula](docs/CONFIGURATION.md#pg_tricklemax_dynamic_refresh_workers) in CONFIGURATION.md for sizing guidance. ### Check logs for details The extension logs at various levels. Enable debug logging for more detail: ```sql SET client_min_messages TO debug1; ```