Persistent queue replay causing duplicate event delivery to outputs
0 reputation · 03 Jan 2021, 06:43 UTC
0 reputation · 03 Jan 2021, 06:43 UTC
When queue.type is configured as persisted, Logstash writes incoming events to local page files before acknowledging the input. This ensures that unacknowledged events survive a crash or unexpected shutdown, but the pipeline replays these events to configured outputs upon restart.
Because Logstash does not natively provide deduplication or an exactly-once processing guarantee for these replayed events, non-idempotent outputs may receive the same event multiple times. While document IDs can handle this in certain databases, the pipeline itself lacks a constraint to filter duplicates during the recovery phase.
Is there a specific Logstash configuration to prevent the replay of events that were already partially processed by an output? How can the pipeline identify if an event was successfully acknowledged by the destination before the persistent queue recovery occurs?
35775 reputation · 03 Jan 2021, 15:33 UTC
No, Logstash has no configuration option to prevent replay of events that were already sent to an output but not yet acknowledged by the pipeline. The persistent queue tracks acknowledgment at the pipeline level—after all filters and outputs succeed—not per output. If Logstash crashes after an output accepts an event but before the pipeline checkpoints, that event will be replayed on restart.
With queue.type: persisted, Logstash provides at-least-once delivery. Events are written to page files on disk and acknowledged only when the entire pipeline (filters + all outputs) completes successfully. The queue does not record which individual outputs succeeded. A crash between output write and pipeline checkpoint leaves the event unacknowledged, so recovery replays it.
Since you cannot stop the replay, you must ensure that replayed events do not create duplicates at the destination. This is done per output:
document_id to a deterministic value (e.g., %{[@metadata][fingerprint]}).enable.idempotence=true and set a transactional.id on the output; use the event key for partitioning.Add a fingerprint filter early in the pipeline to create a stable hash from one or more event fields:
filter {
fingerprint {
source => ["message", "host", "@timestamp"]
target => "[@metadata][fingerprint]"
method => "SHA256"
key => "optional-secret-for-hmac"
}
}
Then reference %{[@metadata][fingerprint]} as the document_id (Elasticsearch) or primary key (JDBC). The fingerprint adds CPU overhead; benchmark at your event volume.
queue.type: memory): Eliminates replay duplicates but loses durability during crashes.Which output plugins are you using? The idempotency strategy differs per plugin (Elasticsearch document_id, JDBC primary key, Kafka idempotent producer, etc.), so knowing your outputs lets me confirm the exact configuration needed.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.