Skip to main content

Client

Struct Client 

Source
pub struct Client { /* private fields */ }
Expand description

An async Pylon client — a connection pool plus a compiled schema, shared cheaply across every Client::with_globals/Client::with_config view of it (mirrors pylon/client.py’s own shared-pool-ref pattern).

Connecting is lazy: Builder::build reaches nothing over the network, and the first query (or an explicit Client::ensure_connected) opens the pool and fetches the schema. Clones — including every with_globals/with_config view — share one connection, so connecting through any of them connects all of them, exactly as pylon/client.py’s _PoolRef is shared across its own views.

Implementations§

Source§

impl Client

Source

pub fn builder(dsn: impl Into<String>) -> Builder

Source

pub async fn ensure_connected(&self) -> Result<()>

Opens the connection pool and fetches the schema snapshot if that hasn’t happened yet. Safe to call repeatedly; after the first success it costs one atomic load.

Queries connect on their own, so this is never required — it exists for a process that would rather learn about an unreachable database, bad credentials or a database with no schema snapshot (Error::NoSchemaSnapshot) at startup than on whichever request arrives first. Mirrors pylon/client.py’s Client.ensure_connected.

Source

pub async fn reload_schema(&self) -> Result<()>

Re-fetches the schema snapshot from _pylon."Schema". Visible to every clone sharing this client’s pool (with_globals/with_config views included) — there’s only one schema slot per underlying connection pool, matching pylon/client.py’s single process-level singleton.

Source

pub fn with_globals( &self, globals: impl IntoIterator<Item = (String, DecodedValue)>, ) -> Client

Returns a client view that injects globals into every query, keyed by qualified name ("module::name") — sharing the same connection pool. Mirrors pylon/client.py:287-304.

Source

pub fn with_config(&self, config: SessionConfig) -> Client

Returns a client view that applies config to every query — sharing the same connection pool. Mirrors pylon/client.py:306-327.

Source

pub async fn raw_connection(&self) -> Result<&PgPool>

Escape hatch for hand-written SQL outside PyQL — mirrors pylon/client.py:567-579. Connects if this client hasn’t yet.

Source

pub fn pool_if_connected(&self) -> Option<&PgPool>

The pool, but only if this client is already connected — None rather than connecting. For an observer that wants to report on whatever connections a process happens to be holding (pool-status metrics, say) without a metrics scrape being the thing that opens them.

Source

pub async fn schema(&self) -> Result<SchemaDescriptor>

A clone of the currently-loaded schema — for callers that need to introspect it directly (e.g. a schema-browser endpoint), not just compile queries against it. Connects (and so fetches the snapshot) if this client hasn’t yet. Clones out from behind the lock rather than returning a guard, same reasoning as every query method here.

Source

pub fn cache_handle(&self) -> Option<Arc<Cache>>

The Arc<pylon_cache::Cache> this client reads/writes through, if Builder::cache was configured — for a caller (pylon-server’s worker-wiring startup) that needs to attach a CacheInvalidationWorker to the exact same LMDB handle this client’s own read-through caching uses, rather than opening a second one (LMDB refuses a second Env::open on the same path within one process).

Source

pub async fn query<R: Queryable, A: QueryArgs + ?Sized>( &self, pyql: &str, args: &A, ) -> Result<Vec<R>>

Source

pub async fn query_single<R: Queryable, A: QueryArgs + ?Sized>( &self, pyql: &str, args: &A, ) -> Result<Option<R>>

Source

pub async fn query_required_single<R: Queryable, A: QueryArgs + ?Sized>( &self, pyql: &str, args: &A, ) -> Result<R>

Source

pub async fn execute<A: QueryArgs + ?Sized>( &self, pyql: &str, args: &A, ) -> Result<()>

Source

pub async fn listen(&self, channel: &str) -> Result<ChannelListener>

Subscribes to a schema-declared Channel (bare or module::name reference — the same string a schema author already writes inside a PyQL notify(...) call) and returns a [ChannelListener] whose recv() yields decoded payloads matching that Channel’s own declared shape: Value::Uuid for a Type-shaped channel (the changed row’s id, not a fetched object — see docs/schema/channels.md), the matching Value variant for a Scalar-shaped channel, or Value::Object for an Object-shaped channel. A payload that doesn’t match the declared shape comes back as Err(Error::MalformedPayload(_)) from that recv() call rather than being silently dropped.

Opens its own dedicated (non-pooled) connection, held for the returned ChannelListener’s lifetime — LISTEN is per-session, so running it on a pooled connection would leak the subscription onto whatever unrelated query later borrows that connection back out of the pool. The connection (and the server-side subscription with it) closes once the ChannelListener is dropped.

Mirrors pylon/client.py’s own Client.listen() — there, a typed async generator; here, a recv()-based handle instead, since this crate has no Stream/async-generator precedent to build on.

Source

pub async fn query_json<A: QueryArgs + ?Sized>( &self, pyql: &str, args: &A, ) -> Result<String>

Source

pub async fn query_single_json<A: QueryArgs + ?Sized>( &self, pyql: &str, args: &A, ) -> Result<Option<String>>

Source

pub async fn query_required_single_json<A: QueryArgs + ?Sized>( &self, pyql: &str, args: &A, ) -> Result<String>

Source

pub fn cache_stat(&self) -> Result<Option<CacheStats>>

Current cache size, or None if Builder::cache wasn’t configured — mirrors pylon.cache.stat().

Source

pub fn cache_clear(&self) -> Result<()>

Evicts every cache entry — a no-op if Builder::cache wasn’t configured. Mirrors pylon.cache.clear().

Source

pub async fn analyze<A: QueryArgs + ?Sized>( &self, pyql: &str, args: &A, ) -> Result<String>

Runs pyql through Postgres’s EXPLAIN (ANALYZE, FORMAT JSON) and returns a query plan grouped by the query’s own shape instead of raw SQL relation names. pyql doesn’t need the leading analyze keyword already written. Mirrors pylon/client.py:461-480.

Source

pub async fn transaction<T, F>( &self, isolation: Isolation, body: F, ) -> Result<T>
where F: for<'a> FnMut(&'a Transaction) -> TxFuture<'a, T>,

Runs a retrying transaction with the default isolation level (Serializable) and attempt budget (3) — see Client::transaction_with_attempts for full control.

body is re-run once per attempt against a fresh Transaction; it commits automatically when body returns Ok, and rolls back and retries (with a 0ms, 100ms, 200ms, … back-off) when body returns a serialization-failure/deadlock error, up to the attempt budget. Any other error rolls back and propagates immediately.

A body that returns Error::Rollback rolls back and is never retried — a decision, not a failure. It still propagates here (there is no T to return); Client::transaction_opt is the same call with that sentinel folded into Ok(None).

client.transaction(pylon_client::Isolation::Serializable, |tx| Box::pin(async move {
    tx.execute("insert Person { name := <str>$name }", &[("name", DecodedValue::Str("Bob".into()))]).await
})).await?;
Source

pub async fn transaction_opt<T, F>( &self, isolation: Isolation, body: F, ) -> Result<Option<T>>
where F: for<'a> FnMut(&'a Transaction) -> TxFuture<'a, T>,

Client::transaction, but a body that deliberately rolls back is an outcome rather than an error: Ok(Some(value)) when it committed, Ok(None) when it returned Error::Rollback. Real failures still propagate as Err.

This is the closest Rust gets to pylon.Rollback in the Python client, where the exception is simply swallowed and the loop ends. Everything the body wrote is visible to the body’s own queries and to nothing else — the point being a test or dry run that needs real writes without leaving rows behind.

let committed: Option<()> = client
    .transaction_opt(pylon_client::Isolation::Serializable, |tx| Box::pin(async move {
        tx.execute("insert Person { name := <str>$name }", &[("name", DecodedValue::Str("Bob".into()))]).await?;
        // ... assert on what the transaction can see, then discard it.
        Err(Error::Rollback)
    }))
    .await?;
assert!(committed.is_none());
Source

pub async fn transaction_opt_with_attempts<T, F>( &self, isolation: Isolation, max_attempts: u32, body: F, ) -> Result<Option<T>>
where F: for<'a> FnMut(&'a Transaction) -> TxFuture<'a, T>,

Client::transaction_opt with an explicit attempt budget — the Ok(None)-on-rollback counterpart to Client::transaction_with_attempts.

Source

pub async fn transaction_with_attempts<T, F>( &self, isolation: Isolation, max_attempts: u32, body: F, ) -> Result<T>
where F: for<'a> FnMut(&'a Transaction) -> TxFuture<'a, T>,

Trait Implementations§

Source§

impl Clone for Client

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> ArchivePointee for T

Source§

type ArchivedMetadata = ()

The archived version of the pointer metadata for this type.
Source§

fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata

Converts some archived metadata to the pointer metadata for itself.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> LayoutRaw for T

Source§

fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>

Returns the layout of the type.
Source§

impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
where T: SharedNiching<N1, N2>, N1: Niching<T>, N2: Niching<T>,

Source§

unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool

Returns whether the given value has been niched. Read more
Source§

fn resolve_niched(out: Place<NichedOption<T, N1>>)

Writes data to out indicating that a T is niched.
Source§

impl<T> Pointee for T

Source§

type Metadata = ()

The metadata type for pointers and references to this type.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more