Ack Range
Acknowledge a range of KubeMQ queue messages by sequence using the stream downstream API in Rust.
Overview
A single poll response often bundles several messages into one batch, but "successfully processed" rarely applies to all of them uniformly — one handler might fail while its siblings succeed. Settling the whole batch together forces an all-or-nothing outcome: either you redeliver work you already finished, or you silently drop work you didn't. Sequence-based acknowledgment exists so each message's outcome can reflect what actually happened to it.
Messages returned by the downstream receiver's poll() carry broker-assigned sequence numbers (msg.sequence) that identify them within the batch. With auto-ack disabled, settling is explicit and per-message: only the sequences you acknowledge are removed from the queue, while the rest stay pending — still redeliverable — until they're settled or the visibility window expires.
Gotchas: messages you never touch aren't automatically fine — once the timeout elapses, anything left unsettled goes back to the queue for redelivery, so forgetting to settle a message isn't "done," it's "will retry." Selective, sequence-based settlement only applies when auto-ack is turned off; the simple auto-ack path settles everything the moment it's delivered, before your handler even runs. And there's no bulk "ack everything except these" shortcut — tracking which sequences you've already settled is on you.
Prerequisites
- KubeMQ server running on
localhost:50000 - Rust SDK installed (
cargo add kubemq)
Code
use kubemq::prelude::*;
use kubemq::{PollRequest, QueueMessageBuilder};
#[tokio::main]
async fn main() -> kubemq::Result<()> {
let client = KubemqClient::builder()
.host("localhost")
.port(50000)
.build()
.await?;
let channel = "rust-queues.ack-range";
for i in 0..5 {
let msg = QueueMessageBuilder::new()
.channel(channel)
.body(format!("range-msg-{}", i).into_bytes())
.build();
client.send_queue_message(msg).await?;
}
let mut receiver = client.new_queue_downstream_receiver().await?;
let response = receiver
.poll(PollRequest {
channel: channel.to_string(),
max_items: 5,
wait_timeout_seconds: 5,
auto_ack: false,
})
.await?;
println!("Polled {} messages", response.messages.len());
// Ack only the sequences you choose from the batch; the rest stay
// pending and are redelivered once the visibility timeout expires.
for (i, msg) in response.messages.iter().enumerate() {
if i < 3 {
msg.ack().await?;
println!("Acked sequence {}", msg.sequence);
} else {
println!("Leaving sequence {} unsettled", msg.sequence);
}
}
receiver.close().await?;
client.close().await?;
Ok(())
}How It Works
client.new_queue_downstream_receiver()andpoll(PollRequest{ auto_ack: false, .. })return a batch where nothing is settled yet.msg.ack()settles only that message's own sequence, so you choose which sequences to ack from the polled batch instead of settling it all at once.- Messages you don't call
ack()on stay pending and are redelivered once the visibility timeout expires; there's no bulk "ack everything except these" shortcut, so track which sequences you've settled. - The simple
receive_queue_messagesAPI auto-acknowledges everything the moment it's delivered; use the downstream receiver whenever the batch needs a per-message outcome. - Review timeouts, channel names, and client IDs before running against shared environments.
- Run the program while the server from the prerequisites is available.
Related
Was this page helpful?