PostgreSQL superpowers
Publishing a post can also enqueue background work. With a PostgreSQL queue, the
post and its event can commit together. Start by requesting pgmq beside your
application models:
extension pgmq {
name = "pgmq"
}
extension vector {
name = "vector"
version = "0.8.1"
schema = "extensions"
}rent migrate diff includes the extension in your reviewed database changes.
Register its typed pack during application setup to get a handle for its functions,
types, operators, indexes, migrations, and higher-level client:
let mut extensions = ExtensionRegistry::default();
let pgmq = extensions.register_pack::<Pgmq>()?;
let events = pgmq.client(&pool).queue("events");
let id = events
.send(json!({"kind": "post.published"}))
.await?;Field-aware packs compose directly with generated field expressions and generated queries:
let nearest = vector.nearest_cosine(
post::embedding(),
&query_embedding,
);
let posts = client
.post()
.order_by_expression(nearest)
.limit(10)
.all()
.await?;Start with the durable queue application: it reuses the User/Post/Comment client, publishes a post and event atomically, and processes retries with an idempotent worker.
Complete example
Download this project, or copy the files below into an empty directory.
Replace RENT_REPOSITORY_URL in Cargo.toml with the URL supplied with your preview access. Use the matching Rent CLI release.
Use an empty PostgreSQL database with the extension binaries available on its server. Set DATABASE_URL
to that database and follow any server setup requirements described above.
rent extension check --provider self-hosted
rent generate
rent migrate dev --name initial
cargo runIf your host preinstalls other extensions, declare those in RSL as well. For a database with existing application tables, baseline it first.
Cargo.toml
[package]
name = "rent-extension-pgmq"
version = "0.1.0"
edition = "2024"
[workspace]
[[bin]]
name = "rent-extension-pgmq"
path = "main.rs"
[dependencies]
rent = { git = "RENT_REPOSITORY_URL", tag = "v0.8.6", default-features = false, features = ["postgres", "extensions", "migrate"] }
rent-ext-pgmq = { git = "RENT_REPOSITORY_URL", tag = "v0.8.6" }
anyhow = "1.0.104"
tokio = { version = "1.53.1", features = ["full"] }
serde_json = "1.0.151"rent.toml
schema = "schema.rsl"
generated = "generated"
migrations = "migrations"
[database]
dialect = "postgres"
backends = ["postgres"]schema.rsl
datasource db {
provider = "postgresql"
}
extension pgmq {
name = "pgmq"
}
model Post {
id BigInt @id @default(autoincrement())
title String
body String @default("")
}main.rs
use anyhow::Result;
use rent::{driver::DatabasePool, extension::ExtensionRegistry};
#[allow(dead_code, unused_imports)]
#[rustfmt::skip]
#[path = "generated/mod.rs"]
mod generated;
#[tokio::main]
async fn main() -> Result<()> {
let pool = DatabasePool::connect_url(&std::env::var("DATABASE_URL")?).await?;
let client = generated::client::Client::new(pool.clone());
let mut extensions = ExtensionRegistry::default();
use rent_ext_pgmq::{Pgmq, PgmqClientExt};
let pack = extensions.register_pack::<Pgmq>()?;
let queue_client = pack.client(&pool);
let queue = queue_client.queue("post_events");
queue.create().await?;
let message = client
.transaction_app(async |tx| -> anyhow::Result<i64> {
let post = tx.create_post().title("New post").save().await?;
let id = pack
.client(&tx.system())
.queue("post_events")
.send(serde_json::json!({"post_id": post.id}))
.await?;
Ok(i64::from(id))
})
.await?;
println!("Post and event {message} committed together.");
Ok(())
}