What if the application doesn't restart before the queue decides the message was lost and resends?
One-or-more semantics + local deduplication gives one-and-only semantics.
In this case you're optimising local deduplication with strictly monotonic index.
One downside is that you leak internals of other system (partitions).
The other is that it implies serialised processing - you can't process anything in parallel as you have single index threshold that defines what has been and what has yet not been processed.
If one-and-only-one semantics are needed and processing should be parallel, other methods have to be used.
> One downside is that you leak internals of other system (partitions).
True, but we generalized the concept of partitions for other datasources, pretty convenient to use it for distributing indexing tasks.
Fortunately Kafka is partitioned. You cannot work in parallel along partitions.
Also, you can streamline your process. If you are running your data through operation (A, B, C). (C on batch N) can run at the same time as (B on batch N+1), and (A on batch N+2)
We do both at quickwit.
That's not really "exactly once". What happens when your system dies after it made sure the data is safely stored in the db and before ack-ing?