4 ms·
[Edited] Two Generals does not apply since it deals with the problem of guaranteeing that two parties agree on the exact same consistent state after a finite am
by firebacon 7y ago
[Edited] Two Generals does not apply since it deals with the problem of guaranteeing that two parties agree on the exact same consistent state after a finite amount of time/messages. MQTT's "exactly once" delivery is basically an eventually consistent mechanism -- it just guarantees that the message is eventually delivered to the broker which then does some simple deduplication. Eventually may be infinite. So, no violation of the space time continuum here...
In other words, MQTT's exactly once delivery conceptually works as if the server would store a big hashmap of all message IDs it has seen once and the rejecting any duplicates using that hashmap. The client the simply keeps retrying with the same message ID forever. This guarantees that the message is received by the server "exactly once". The actual protocol is more involved, but the details are basically just an optimization to prevent the server from having to keep around the "received messages" hashmap forever.
The problem that you're thinking of that MQTT of course doesn't solve is that it is impossible to perform any externally visible action (such as moving an actuator, storing a record into another database system or detonating a bomb) "exactly once" in the general case when you take into account the possibility of failures in between the two steps of performing the action and storing the information that you have performed it. You can either first commit and then perform the action, which means you can not safely retry after having failed between the commit and performing the action, because you might have already performed it ("at most once"). Or you can first perform the action and then commit, potentially redoing the action after a failed commit ("at least once"). There is, conceptually, no way around this unless we get help from the environment, such as an atomic perform+commit primitive. To see how this relates to two generals, think about the exchange of the "performed" bit between the external system and the storage as an exchange of messages that can be lossy. We can not come up with any algorithm that guarantees that the external system and the storage always agree on the state of the performed bit after any finite number of steps.