Understanding the Seam Architecture in pgrust: Breaking Dependency Cycles with Compile-Time Indirection
The seam architecture in pgrust uses compile-time indirection through the seam! macro to replace direct function calls with runtime-filled slots, breaking circular dependencies by separating interface declarations from implementations.
The pgrust project (malisper/pgrust) reimplements PostgreSQL in Rust while maintaining a strictly acyclic crate dependency graph. To solve the tight coupling inherent in database systems—where modules often need to call each other—the codebase employs a novel seam architecture that shifts linkage from compile-time to runtime without sacrificing type safety or performance.
What Is the Seam Architecture in pgrust?
The Core Concept: Compile-Time Indirection
A seam is a compile-time indirection point that replaces a direct function call with a slot that can be filled at runtime. In pgrust, the seam! macro—defined in [crates/_support/seam/seam_core/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/_support/seam/seam_core/src/lib.rs)—expands to a module containing three public items:
set(f)– Installs the concrete implementation. Can only be called once.call(..)– Invokes the installed implementation; panics if the seam is not yet initialized.is_installed()– Returns whether a concrete implementation has been registered.
This mechanism allows crates to agree on function signatures at compile time while deferring the actual dependency linking until program startup.
How the Seam Architecture Breaks Dependency Cycles
The architecture eliminates circular dependencies through a four-step ownership pattern that separates contracts from implementations.
Step 1: Separate Seams Crates
For any crate X that needs to expose functions externally, a companion crate X-seams is created. This crate contains only seam! declarations and never depends on the concrete implementation of X. For example, port_pqsignal_seams declares signal-handling slots without importing the actual signal implementation.
Step 2: Implementation Crates Depend on Their Own Seams
The original crate X adds a dependency on X-seams and installs its implementation inside an init_seams() function. In [crates/port/port_pqsignal/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/port/port_pqsignal/src/lib.rs), the initialization looks like this:
pub fn init_seams() {
port_pqsignal_seams::pqsignal::set(pqsignal_be);
}
This allows the implementation to reside in the original crate while remaining invisible to consumers at compile time.
Step 3: Consumers Call Through the Seam
Consumer crates depend only on the lightweight X-seams crate, not on X itself. They invoke functionality through the seam slot:
use port_pqsignal_seams::pqsignal;
fn handle_signal() {
// Calls the implementation installed by port_pqsignal
pqsignal::call(signal_number);
}
Because the consumer only sees the seam declaration in [port_pqsignal_seams/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/port/port_pqsignal_seams/src/lib.rs), the build graph remains acyclic.
Step 4: Runtime Initialization with seams-init
The special seams-init crate orchestrates startup. It iterates over all crates in the workspace and invokes each init_seams() exactly once before any consumer code runs. The init runtime is defined in [crates/_support/seam/init/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/_support/seam/init/src/lib.rs).
This produces a unidirectional dependency graph:
consumer ─► X-seams ─► (runtime) X (installs)
↘︎ ↗︎
──────► seams-init (calls X::init_seams())
Practical Example: Implementing a Seam in pgrust
Declaring the Seam
First, create a -seams crate that declares the interface:
// crates/myfeature_seams/src/lib.rs
seam_core::seam!(
/// Executes the core logic of MyFeature.
pub fn do_work(arg: i32) -> i32
);
Installing the Implementation
The owning crate implements the logic and registers it during initialization:
// crates/myfeature/src/lib.rs
pub fn init_seams() {
myfeature_seams::do_work::set(|arg| arg * 2); // concrete implementation
}
Calling from Consumer Code
Downstream crates depend only on myfeature_seams, not myfeature:
// crates/consumer/src/lib.rs
use myfeature_seams::do_work;
pub fn run() -> i32 {
// Panics if myfeature::init_seams() has not been called yet.
do_work::call(21)
}
Orchestrating Startup
Finally, ensure all seams are installed before use:
// crates/seams_init/src/lib.rs
pub fn init_all() {
myfeature::init_seams(); // repeat for each crate that owns seams
// … other init_seams() calls …
}
Running seams_init::init_all() at program entry guarantees that do_work::call executes safely.
Key Source Files in the pgrust Seam Architecture
- [
crates/_support/seam/seam_core/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/_support/seam/seam_core/src/lib.rs) – Defines theseam!macro and the slot mechanics (set,call,is_installed). - [
crates/_support/seam/init/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/_support/seam/init/src/lib.rs) – Runtime entry point that invokes every crate'sinit_seams(). - [
crates/port/port_pqsignal_seams/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/port/port_pqsignal_seams/src/lib.rs) – Real-world example of a seams crate for signal handling. - [
crates/port/port_pqsignal/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/port/port_pqsignal/src/lib.rs) – Implementation crate that installs signal handlers into the seam. - [
crates/backend/utils/time/snapmgr_seams/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/time/snapmgr_seams/src/lib.rs) – Time-snapshot manager interface declarations. - [
crates/backend/utils/time/snapmgr/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/time/snapmgr/src/lib.rs) – Concrete implementation wiring into snapmgr seams.
Summary
- The seam architecture in pgrust replaces direct function calls with runtime-filled slots declared via the
seam!macro. - Dependency cycles break by separating interface declarations (
X-seamscrate) from implementations (Xcrate). - Consumers depend only on lightweight seams crates, never on the concrete implementations.
- Initialization happens once at startup via the
seams-initcrate, which calls each module'sinit_seams()function before any consumer code executes. - The pattern keeps the Rust build graph strictly acyclic while preserving zero-cost runtime performance.
Frequently Asked Questions
What happens if a seam is called before initialization?
The call() function will panic at runtime if invoked before the corresponding set() has been called. This is by design—the seams-init crate guarantees that all init_seams() functions run before any consumer code, making this panic a safety net for initialization ordering bugs rather than an expected runtime path.
Why not use trait objects or dependency injection instead of seams?
Trait objects require dyn dispatch and boxing, which introduces vtable overhead and allocation complexity. The seam architecture uses static dispatch through function pointers filled at startup, maintaining zero-cost abstraction characteristics while still breaking compile-time cycles. It also integrates cleanly with pgrust's existing C-to-Rust FFI boundaries where trait objects would be cumbersome.
How does the seam! macro work technically?
The seam! macro, defined in [seam_core/src/lib.rs](https://github.com/malisper/pgrust/blob/main/crates/_support/seam/seam_core/src/lib.rs), expands to a module containing a thread-safe static OnceCell or equivalent storage. The set method writes a function pointer into this storage exactly once, while call reads and invokes that pointer. This generates minimal overhead—just a pointer indirection compared to a direct function call.
Can seams be used for async functions?
While the analysis focuses on synchronous signatures, the architecture supports any function type that can be stored as a function pointer or closure. Async seams would require boxing the future or using async fn pointers, which the underlying storage in seam_core accommodates provided the function signature matches the declaration in the -seams crate.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →