Skip to content

Commit

Permalink
Bugfix: DLX doesn't update timestamp on republish (#583)
Browse files Browse the repository at this point in the history
When a message was dead letter the timestamp wasn't updated. This caused TTL to misbehave if a message was dead lettered to another queue with TTL:

    Declare queue q1 with x-message-ttl = 100 and x-dead-letter-routing-key = q2
    Declare queue q2 with x-message-ttl = 100 and x-dead-letter-routing-key = q3
    Publish message to q1.

After 100ms the message is dead lettered to q2 (via default exchange). Because the message timestamp isn't updated the message will expire immediately in q2 and dead lettered to q3.

This will fix this by setting a new timestamp on the message when it's published by the dead lettering.
  • Loading branch information
spuun authored Oct 27, 2023
1 parent 54a47f7 commit 7768ea5
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- A bug in delay exchanges caused messages to be routed to x-dead-letter-exchange instead of bound queues. It also ruined any dead lettering headers.
- A bug that prevented headers exchange to match on table in message headers.
- Purging a queue with a lot of messages blocked LavinMQ from other operations.
- Message timestamps not being updated when dead lettered breaking TTL.

## [1.2.4] - 2023-09-26

Expand Down
29 changes: 29 additions & 0 deletions spec/dlx_spec.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "./spec_helper"
require "./../src/lavinmq/queue"
require "./../src/lavinmq/rough_time"

describe "Dead lettering" do
q_name = "ttl"
Expand Down Expand Up @@ -29,4 +30,32 @@ describe "Dead lettering" do
x_death[1].as(AMQ::Protocol::Table)["queue"].should eq q_delayed_2.name
end
end

it "should update message timestamp on publish" do
v = Server.vhosts.create("test")
q_args = AMQ::Protocol::Table.new({
"x-message-ttl" => 200,
"x-dead-letter-exchange" => "",
"x-dead-letter-routing-key" => "q2",
})
v.declare_queue("q", true, false, q_args)
v.declare_queue("q2", true, false, AMQ::Protocol::Table.new)

ts = RoughTime.unix_ms
msg = LavinMQ::Message.new(ts, "", "q", AMQ::Protocol::Properties.new, 0, IO::Memory.new)

v.publish msg

select
when v.queues["q2"].empty_change.receive
when timeout(1.second)
fail "timeout: message not dead lettered?"
end

v.queues["q2"].basic_get(no_ack: true) do |env|
msg = env.message
end

msg.timestamp.should be > ts
end
end
2 changes: 1 addition & 1 deletion src/lavinmq/queue/queue.cr
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ module LavinMQ

private def dead_letter_msg(msg : BytesMessage, props, dlx, dlrk)
@log.debug { "Dead lettering ex=#{dlx} rk=#{dlrk} body_size=#{msg.bodysize} props=#{props}" }
@vhost.publish Message.new(msg.timestamp, dlx.to_s, dlrk.to_s,
@vhost.publish Message.new(RoughTime.unix_ms, dlx.to_s, dlrk.to_s,
props, msg.bodysize, IO::Memory.new(msg.body))
end

Expand Down

0 comments on commit 7768ea5

Please sign in to comment.