`ess_billing` billing stream generates duplicate records due to `total_ecu` in `_id` fingerprint
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- elasticsearch, yaml
- Domain
- data
Research direction
Open data_stream/billing/elasticsearch/ingest_pipeline/default.yml and inspect the fingerprint processor that builds _id. Remove ess.billing.total_ecu from its fields while retaining the deployment, billing-window, and SKU fields. Done means revised ECU values for the same billing line item produce the existing fingerprint instead of a new one; validate the pipeline configuration and relevant billing ingestion behavior.
Written by the indexing model from the issue text.
Description
Summary
Re-ingesting historical billing data (triggered by a cursor reset, a fresh
deploy, or a policy re-creation that changes the input id) produces duplicate
records in two patterns:
- Same deployment + same billing window → different
_id+ different ECU value
(upstream-revised data creates a new document instead of overwriting). - Same
_id→ appears in multiple backing indices simultaneously (Elasticsearch
cannot de-dup across data stream backing indices on re-ingest).
Root cause: total_ecu is a mutable field included in the _id fingerprint
The ingest pipeline uses a fingerprint processor to generate _id:
# data_stream/billing/elasticsearch/ingest_pipeline/default.yml
- fingerprint:
fields:
- ess.billing.deployment_id
- ess.billing.from
- ess.billing.to
- ess.billing.sku
- ess.billing.total_ecu # <-- mutable: revised by the API after initial publish
target_field: _id
The ESS billing API revises total_ecu after initial publish (finalization).
When historical data is re-fetched, even for the exact same
deployment_id + from + to + sku window, a revised ECU value produces a
different hash → a new document is written rather than overwriting the original.
Root cause: large lookbehind amplifies the problem on re-ingest
When the CEL cursor is absent (state reset, fresh deploy, or policy re-creation
with a new input id), lookbehind controls how far back data is fetched.
The default is 365 days; users can set it higher. This means a single
cursor-reset event re-ingests a large volume of history in one burst.
On re-ingest, documents already written live in older (possibly frozen/snapshot)
backing indices. New copies land in the current write index, so both are
queryable. Elasticsearch only de-dups by _id within a single index, not
across backing indices of a data stream.
Proposed fix
Remove total_ecu from the fingerprint. The natural identity of a billing
line item is deployment_id + from + to + sku — ECU is a measurement that
can legitimately change. With total_ecu excluded, a re-fetched record with
a revised ECU will produce the same _id and overwrite the stale document
rather than creating a duplicate.
- fingerprint:
fields:
- ess.billing.deployment_id
- ess.billing.from
- ess.billing.to
- ess.billing.sku
# total_ecu removed — mutable field, changes on API finalization
target_field: _id
Note: records already written with the old fingerprint will not be
retroactively de-duped. Those would require a one-time reindex or manual
cleanup. This fix prevents the problem from recurring going forward.
- Dominant language
- Handlebars
- Stars
- 334
- Forks
- 652
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 241
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from elastic/integrations
-
automation flaky-test Integration:kafka_input_otel Team:obs-ds-intake-services
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
elastic/integrations#21427 ·
-
automation flaky-test Integration:mysql_input_otel Team:Obs-InfraObs
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
elastic/integrations#21426 ·
-
automation flaky-test Integration:jaeger_input_otel Team:obs-ds-intake-services
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
elastic/integrations#21418 ·
-
automation flaky-test Integration:jaeger_input_otel Team:obs-ds-intake-services
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
elastic/integrations#21413 ·
-
automation flaky-test Integration:googlecloudmonitor_input_otel Team:Obs-InfraObs
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
elastic/integrations#21350 ·
All issues in elastic/integrations
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 1/5 Under an hour Newbie friendliness 90/100
open-compass/VLMEvalKit#1698 ·
-
triage:deciding
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
open-telemetry/otel-arrow#4123 · 1 reaction ·