Configure Visibility Timeout
Control how long messages are hidden during processing before redelivery.
How Visibility Timeout Works
When a consumer receives a message, it becomes hidden from other consumers for a configurable duration. If the consumer does not acknowledge within the timeout, the message becomes available again for redelivery.
A message stays hidden from other consumers until the owner acks or the visibility timeout expires.
A single message moves through three states while it is being processed. It is Hidden from other consumers from the moment it is delivered. An ack settles it as Acked (removed from the queue). If the timeout expires first, it returns to Visible and the next poll redelivers it.
Message lifecycle under a visibility timeout: a poll hides the message, an ack settles it, and an expiry returns it for redelivery.
Set Visibility Timeout Per Request
A per-request visibility override is exposed by the Java SDK via QueuesPollRequest.visibilitySeconds. It overrides the server default (DefaultVisibilitySeconds, 60 seconds) for that poll. The other SDKs do not expose a per-request override; messages stay hidden for the server-side default until you ack, so acknowledge before that window elapses (or use a language that surfaces the field).
// A per-request visibility override is not exposed in the Go SDK — the server
// default (DefaultVisibilitySeconds) applies. Ack before it expires.
resp, err := client.PollQueue(ctx, &kubemq.PollRequest{
Channel: "orders",
MaxItems: 1,
WaitTimeoutSeconds: 5,
})
if err != nil {
log.Fatal(err)
}
for _, dsMsg := range resp.Messages {
fmt.Printf("Processing: %s\n", string(dsMsg.Message.Body))
}
resp.AckAll()# A per-request visibility override is not exposed in the Python SDK — the server
# default (DefaultVisibilitySeconds) applies. Ack before it expires.
response = client.receive_queue_messages(
channel="orders",
max_messages=1,
wait_timeout_in_seconds=5,
)
for msg in response.messages:
print(f"Processing: {msg.body.decode('utf-8')}")
msg.ack()// A per-request visibility override is not exposed in the Node.js SDK — the server
// default (DefaultVisibilitySeconds) applies. Ack before it expires.
const messages = await client.receiveQueueMessages({
channel: 'orders',
maxMessages: 1,
waitTimeoutSeconds: 5,
});
for (const msg of messages) {
console.log('Processing:', new TextDecoder().decode(msg.body));
await msg.ack();
}// visibilitySeconds is a client-side timer: if the message is not acked or
// rejected within 120s, the SDK auto-rejects it and the server redelivers.
QueuesPollRequest pollRequest = QueuesPollRequest.builder()
.channel("orders")
.pollMaxMessages(1)
.pollWaitTimeoutInSeconds(5)
.autoAckMessages(false)
.visibilitySeconds(120)
.build();
QueuesPollResponse response = client.receiveQueueMessages(pollRequest);
for (QueueMessageReceived msg : response.getMessages()) {
System.out.println("Processing (120s visibility): " + new String(msg.getBody()));
msg.ack();
}// A per-request visibility override is not exposed in the C# SDK — the server
// default (DefaultVisibilitySeconds) applies. Ack before it expires.
var response = await client.ReceiveQueueMessagesAsync(new QueuePollRequest
{
Channel = "orders",
MaxMessages = 1,
WaitTimeoutSeconds = 5,
AutoAck = false,
});
foreach (var msg in response.Messages)
{
Console.WriteLine($"Processing: {Encoding.UTF8.GetString(msg.Body.Span)}");
await msg.AckAsync();
}// A per-request visibility override is not exposed in the Kotlin SDK — the server
// default (DefaultVisibilitySeconds) applies. Ack before it expires.
val response = client.receiveQueuesMessages {
channel = "orders"
maxItems = 1
waitTimeoutMs = 5000
autoAck = false
}
for (msg in response.messages) {
println("Processing: ${String(msg.body)}")
msg.ack()
}// A per-request visibility override is not exposed in the C++ SDK — the server
// default (DefaultVisibilitySeconds) applies. Ack before it expires.
kubemq::PollRequest poll_req;
poll_req.channel = "orders";
poll_req.max_items = 1;
poll_req.wait_timeout_seconds = 5;
auto poll_result = client->PollQueue(poll_req);
for (const auto& dm : poll_result->messages()) {
std::cout << "Processing: " << dm.message().body() << std::endl;
dm.Ack();
}// A per-request visibility override is not exposed in the Rust SDK — the server
// default (DefaultVisibilitySeconds) applies. Ack before it expires.
let mut receiver = client.new_queue_downstream_receiver().await?;
let poll = PollRequest {
channel: "orders".to_string(),
max_items: 1,
wait_timeout_seconds: 5,
auto_ack: false,
};
let response = receiver.poll(poll).await?;
for msg in &response.messages {
println!("Processing: {}", String::from_utf8_lossy(&msg.message.body));
}
response.ack_all().await?;# A per-request visibility override is not exposed in the Ruby SDK — the server
# default (DefaultVisibilitySeconds) applies. Ack before it expires.
# Per-message ack requires the streaming receiver, not the unary receive call.
receiver = client.create_downstream_receiver
request = KubeMQ::Queues::QueuePollRequest.new(
channel: 'orders',
max_items: 1,
wait_timeout: 5
)
response = receiver.poll(request)
response.messages.each do |m|
puts "Processing: #{m.body}"
m.ack
end
receiver.close# A per-request visibility override is not exposed in the Elixir SDK — the server
# default (DefaultVisibilitySeconds) applies. Ack before it expires.
{:ok, poll} =
KubeMQ.Client.poll_queue(client,
channel: "orders",
max_items: 1,
wait_timeout: 5_000
)
Enum.each(poll.messages, fn msg ->
IO.puts("Processing: #{msg.body}")
end)
{:ok, _} = KubeMQ.PollResponse.ack_all(poll)Best Practices
| Guideline | Recommendation |
|---|---|
| Set timeout to 2-3x expected processing time | Prevents premature redelivery |
| Use shorter timeouts for fast operations | Reduces delay when consumers crash |
| Use longer timeouts for heavy processing | Prevents duplicate work |
| Consider extending visibility | For variable-duration tasks |
The maximum visibility timeout is controlled by the server setting MaxVisibilitySeconds (default: 43,200 seconds / 12 hours). The default when not specified is DefaultVisibilitySeconds (60 seconds).
Next Steps
Was this page helpful?