Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mqtt-streaming: Prefer wrapping instead of reissuing packet ids #1489

Merged
merged 1 commit into from
Feb 14, 2019

Conversation

longshorej
Copy link
Contributor

@longshorej longshorej commented Feb 12, 2019

Packet ids now monotonically increase until they reach the maximum value, upon which they wrap around to the minimum. Care is taken to ensure that packet ids that are in use are not issued, as it is
technically possible, albeit rare.

This fixes a number of potential race conditions. For example, consider a PUBLISH -> PUBACK exchange with QoS1 with the old behavior:

  1. Server retransmits an unacknowledged publish
  2. Before client receives retransmission, it finally ACKs original publish.
  3. Server receives ACK, and performs new publish, reusing the last id.
  4. Client receives retransmission from # 1, and ACKs it.
  5. Server receives ACK, and misinterprets it as an ack from # 3
  6. Oh no!

By wrapping around, we've effectively eliminated the changes of this happening. This is similar to how Linux issues PIDs for processes to avoid a similar sort of problem.

/cc @huntc

private val MinPacketId = PacketId(1)
private val MaxPacketId = PacketId(0xffff)
val MinPacketId = PacketId(1)
val MaxPacketId = PacketId(0xffff)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are enclosed in a private[streaming] already. I am using them in the tests, hence removing the private here.

@huntc
Copy link
Contributor

huntc commented Feb 13, 2019

I’m wondering if we can simplify this even further. The idea is centered on maintaining a set where we track the allocated packet ids.

  1. Fail if allocation set is full.

  2. Allocate by adding to the last number allocated.

  3. If exceeds max then set to min.

  4. Test if allocated is already in the allocated packet ids set. If so, add 1 and go to 2.

  5. Add packet id to allocated packet ids set.

When deallocating, simply remove from the set.

Copy link
Contributor

@huntc huntc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we can simplify this further. Will chat with Jason.

Copy link
Contributor

@huntc huntc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. One comment re the need for retaining pending registrations, but nothing show-stopping. Thanks for having tracked down this gnarly situation!

// all packet ids are taken, so we'll wait until one is unregistered
// to continue

main(registrantsByPacketId, nextPacketId, pendingRegistrations :+ register)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still unsure as to the benefit of having a pendingRegistrations structure. It should be a rare condition and adds complexity.

@huntc
Copy link
Contributor

huntc commented Feb 13, 2019

@ennru, @longshorej and I discussed further - this PR is now reviewed from our perspective and ready to go.

Copy link
Member

@ennru ennru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Needs a rebase.

Packet ids now monotonically increase until they reach the maximum
value, upon which they wrap around to the minimum. Care is taken to
ensure that packet ids that are in use are not issued, as it is
technically possible, albeit rare.

This fixes a number of potential race conditions. For example, consider
a PUBLISH -> PUBACK exchange with QoS1 with the old behavior:

1) Server retransmits an unacknowleged publish
2) Before client receives retransmission, it finally ACKs original
publish.
3) Server receives ACK, and performs new publish, reusing the last id.
4) Client receives retransmission from #1, and ACKs it.
5) Server receives ACK, and misinterprets it as an ack from #3
6) Oh no!

By wrapping around, we've effectively eliminated the changes of this
happening. This is similar to how Linux issues PIDs for processes to
avoid a similar sort of problem.
@longshorej
Copy link
Contributor Author

Rebased!

@ennru ennru merged commit ccc3d3e into akka:master Feb 14, 2019
@ennru ennru added this to the 1.0-M3 milestone Feb 14, 2019
@longshorej longshorej deleted the mqtt-packet-ids branch February 14, 2019 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants