Skip to main content

Sender

Struct Sender 

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

High-level sender for transmitting messages over the network.

The Sender provides a convenient interface for sending messages through Thubo’s multi-priority transmission pipeline. It handles message serialization, batching, fragmentation, and QoS enforcement automatically.

§Cloning

Sender is cheaply cloneable (uses Arc internally). Multiple clones can send messages concurrently from different tasks.

§Quality of Service

Each sender has configurable QoS settings that control:

  • Priority: Message importance (8 levels from Control to Background)
  • Congestion Control: Behavior when buffers are full (Block or Drop)
  • Express Mode: Whether to batch messages or send immediately

§Examples

// Configure sender for high-priority, real-time messages
sender.qos(
    QoS::default()
        .with_priority(Priority::High)
        .with_congestion_control(CongestionControl::Block)
        .with_express(true),
);
let msg = Bytes::from(vec![1, 2, 3, 4]);
sender.send(&msg).await?;

// Check if the sender is experiencing congestion
if sender.is_congested() {
    println!("Warning: sender is congested");
}

Implementations§

Source§

impl Sender

Source

pub fn qos(&mut self, qos: QoS) -> &mut Self

Configures the Quality of Service settings for this sender.

The QoS settings determine how messages are prioritized, batched, and handled when the pipeline is congested. This affects all subsequent send() calls.

Source

pub fn timeout(&mut self, timeout: Duration) -> &mut Self

Sets the overall deadline for the entire send operation.

This is the maximum total time that send() will attempt to serialize and queue a message. If the message cannot be successfully queued within this time, the operation fails with SendError::Timeout.

The timeout is checked periodically based on the timeout_progress interval. When no progress can be made (e.g., batch pool is empty), the operation waits up to the progress timeout before checking if the overall deadline has been exceeded.

Default: 10 seconds

§Examples
// Fail if message can't be sent within 5 seconds
sender.timeout(Duration::from_secs(5));

// Method chaining
let msg = Bytes::from(vec![42; 42]);
sender.timeout(Duration::from_secs(5)).send(&msg).await?;
Source

pub fn timeout_progress(&mut self, wait: Duration) -> &mut Self

Sets the progress check interval when the send operation stalls.

When the batch pool is temporarily empty (no batches available for serialization), send() will wait up to this duration for a batch to become available. After this timeout, it checks whether the overall timeout has been exceeded. If not, it waits again for up to this duration.

This creates a progress-checking loop:

  1. Try to acquire a batch
  2. If unavailable, wait up to timeout_progress duration
  3. Check if overall timeout exceeded → return error if yes
  4. Otherwise, repeat from step 1

Tuning guidance:

  • Too high (e.g., 5s): Send operations become unresponsive to the overall timeout
  • Too low (e.g., 1ms): Causes busy-waiting and unnecessary CPU usage
  • Recommended: 100-500ms for most applications

Default: 100ms

§Examples

// Check for progress every 100ms
sender.timeout_progress(Duration::from_millis(100));

// Typical configuration
let msg = Bytes::from(vec![42; 42]);
sender
    .timeout(Duration::from_secs(5)) // Give up after 5 seconds
    .timeout_progress(Duration::from_millis(100)) // Check progress every 100ms
    .send(&msg)
    .await?;
Source

pub fn is_congested(&self) -> bool

Checks if the sender’s priority queue is currently congested.

A queue is considered congested when it cannot keep up with incoming messages and has run out of available buffers. When congested:

§Examples
if sender.is_congested() {
    // Switch to a lower priority or handle congestion
    sender.qos(
        QoS::default()
            .with_priority(Priority::Background)
            .with_congestion_control(CongestionControl::Drop),
    );
}
Source

pub fn is_closed(&self) -> bool

Checks if the sender has been closed.

A sender is closed when stop() has been called or the associated SenderTask has terminated. Once closed, all send() calls will fail.

Source

pub async fn send<T>(&mut self, msg: T) -> Result<(), SendError>
where T: AsRef<Bytes>,

Asynchronously sends a message through the transmission pipeline.

The message is serialized, batched, and queued for transmission according to the configured QoS settings. Large messages are automatically fragmented across multiple batches if needed.

§Errors
§Examples
let msg = Bytes::from(vec![1, 2, 3, 4]);
match sender.send(&msg).await {
    Ok(()) => println!("Message sent successfully"),
    Err(SendError::Dropped) => println!("Message dropped (congestion)"),
    Err(SendError::Timeout) => println!("Timeout waiting for buffers"),
    Err(SendError::EncodingFailed) => println!("Serialization failed"),
    Err(SendError::Closed) => println!("Sender task is stopped"),
    Err(SendError::Internal) => println!("Internal error"),
}
§Performance note

Passing a reference (&Bytes) is significantly more efficient than passing by value and can impact performance by up to few million msg/s in case of very small messages (e.g. 8 bytes payload). References avoid cloning reference counters and allow the message to be reused immediately after the call returns.

However, passing ownership is required when using OnDrop or Boomerang wrappers for buffer lifecycle management (e.g., returning buffers to a pool or tracking when transmission completes).

Source

pub async fn stop(self)

Gracefully shuts down the sender and associated writer task.

This method:

  1. Disables the transmission pipeline (rejecting new messages)
  2. Waits for in-flight serialization to complete
  3. Signals the writer task to drain remaining batches and terminate

After calling stop(), the sender is consumed and cannot be used again. The writer task will finish transmitting any buffered data before exiting.

Trait Implementations§

Source§

impl Clone for Sender

Source§

fn clone(&self) -> Sender

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> 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, 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> 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 = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.