nats-io / nats.rs

Rust client for NATS, the cloud native messaging system.
Apache License 2.0
980 stars 159 forks source link

Ordered consumers don't have all of the same settings as unordered ones #1252

Open paolobarbolini opened 2 months ago

paolobarbolini commented 2 months ago

Proposed change

Give pull::OrderedConfig most of the options as pull::Config, instead of exposing just a few of them and hard-coding the rest like it's currently being done: https://github.com/nats-io/nats.rs/blob/1303b8bad39c83d9e3f93ff4fb0348fcedc3c8f2/async-nats/src/jetstream/consumer/pull.rs#L695-L729

Use case

Ingest all messages with batching acknowledgement (AckPolicy::All) or guarantee ordered ingestion of messages while keeping acknowledgments.

Contribution

Yes

Jarema commented 2 months ago

Hey!

Ordered consumer is a client side construct, and most of the settings are required for proper functioning. The only reason why normal pull consumer can get messages out of order are redeliveries. Those would happen if you have max ack pending > 1.

So Ordered Consumer has ack: none and max deliver = 1, which ensure that redeliveries will never happen, while still having very high performance characteristics. This is achieved by having in memory, r1 consumer. The consumer ensures no messages lost without acks by recreating itself by the client whenever a missing sequence is detected.

Because of all above, those settings are locked how they are. Enabling ack would make this whole idea no longer work.

Did you consider having a normal pull consumer with max ack pending = 1? That way you would have both acks and ordered guarantees.

paolobarbolini commented 2 months ago

The part about being client side makes perfect sense, and that's what I was expecting too. Everything else though seems not to work the way I was expecting it to.

Looks like I'll have to write myself a wrapper that does At Least Once delivery (re-deliveries are fine) using batching, making sure to process messages in order by detecting dropped messages + re-deliveries already being handled on the application side.

Jarema commented 2 months ago

If you have max_ack_pending set to 1, you will get guarantee of order, though without being able to ack many messages at once.

Otherwise, you can leverage the message metadata to check deliver count for redeliveries/dropped messages with your own logic. However, if performance is not critical (as max_ack_pending = 1 will be a bit slower as it delivers next message after ack for previous one was received), that is much simpler solution without any custom code on your end.