Component(s)
receiver/sqlquery
Describe the issue you're reporting
Description
In the sqlqueryreceiver's logs component, the tracking value (retrieved from the specified tracking_column) is persistently written to the configured storageClient on every single row processed within the collection loop.
This behavior:
- Causes significant performance degradation, high CPU overhead, and high disk/network I/O due to redundant serial writes to the storage backend, especially when querying large result sets (e.g., thousands of log records per collection cycle).
- Introduces a data loss risk under crash conditions. If the collector restarts or crashes after the storage client has been updated but before
nextConsumer.ConsumeLogs successfully processes/exports the collected logs, the tracking value in persistent storage will have already been advanced. Upon restart, the receiver will query starting from the advanced tracking value, causing the failed/unexported logs to be permanently lost.
Steps to Reproduce
- Configure
sqlqueryreceiver with a tracking_column and a persistent storage extension (e.g., filestorage or dbstorage).
- Run a query that returns a large number of rows (e.g., 10,000 logs).
- The receiver iterates through all 10,000 rows. For each row, it executes
storageClient.Set to write the new tracking value to storage.
- Observe the performance degradation, high disk write throughput, or CPU wait times.
- Alternatively, simulate a failure in
ConsumeLogs or crash the collector mid-collection; on restart, notice that the tracking value has advanced in the storage and the failed logs are skipped.
Expected Result
The receiver should only write the tracking value to persistent storage once per query collection cycle (e.g., at the end of the collection loop, or ideally, after the batch has been successfully consumed/exported by the pipeline). At minimum, writing to storage should happen once at the end of the collection loop rather than per row.
Actual Result
storageClient.Set is invoked synchronously on every single row inside the rows iteration, leading to thousands of redundant writes.
Version
v0.104.0 / main branch (commit 928d968ca2 or latest)
Environment Information
- OS: All (Windows, Linux, macOS)
- Compiler (if applicable): Go 1.22+
Repo Configuration
extensions:
file_storage:
directory: /tmp/otelcol/storage
receivers:
sqlquery:
driver: postgres
datasource: "host=localhost user=postgres password=postgres dbname=postgres sslmode=disable"
storage: file_storage
collection_interval: 10s
queries:
- sql: "SELECT id, body, timestamp FROM logs_table WHERE id > $$1"
tracking_column: id
tracking_start_value: "0"
logs:
- body_column: body
attribute_columns: [timestamp]
exporters:
nop:
service:
extensions: [file_storage]
pipelines:
logs:
receivers: [sqlquery]
processors: []
exporters: [nop]
Log Output
Under normal operation, there is no explicit error, but significant latency and wait times are visible, potentially resulting in timeouts:
2026-07-13T17:25:13.000Z warn sqlqueryreceiver problems encountered getting log rows {"error": "context deadline exceeded"}
Additional Context
- Root cause analysis:
In logs_receiver.go, the loop that converts database rows to OTel logs calls queryReceiver.storeTrackingValue(ctx, row) for every row in rows when processing the first logs config (logsConfigIndex == 0).
This method synchronously invokes the storage client's Set method (lines 359-371).
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.
Component(s)
receiver/sqlquery
Describe the issue you're reporting
Description
In the
sqlqueryreceiver's logs component, the tracking value (retrieved from the specifiedtracking_column) is persistently written to the configuredstorageClienton every single row processed within the collection loop.This behavior:
nextConsumer.ConsumeLogssuccessfully processes/exports the collected logs, the tracking value in persistent storage will have already been advanced. Upon restart, the receiver will query starting from the advanced tracking value, causing the failed/unexported logs to be permanently lost.Steps to Reproduce
sqlqueryreceiverwith atracking_columnand a persistent storage extension (e.g.,filestorageordbstorage).storageClient.Setto write the new tracking value to storage.ConsumeLogsor crash the collector mid-collection; on restart, notice that the tracking value has advanced in the storage and the failed logs are skipped.Expected Result
The receiver should only write the tracking value to persistent storage once per query collection cycle (e.g., at the end of the collection loop, or ideally, after the batch has been successfully consumed/exported by the pipeline). At minimum, writing to storage should happen once at the end of the collection loop rather than per row.
Actual Result
storageClient.Setis invoked synchronously on every single row inside therowsiteration, leading to thousands of redundant writes.Version
v0.104.0/ main branch (commit928d968ca2or latest)Environment Information
Repo Configuration
Log Output
Under normal operation, there is no explicit error, but significant latency and wait times are visible, potentially resulting in timeouts:
Additional Context
In logs_receiver.go, the loop that converts database rows to OTel logs calls
queryReceiver.storeTrackingValue(ctx, row)for every row inrowswhen processing the first logs config (logsConfigIndex == 0).This method synchronously invokes the storage client's
Setmethod (lines 359-371).Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding
+1orme too, to help us triage it. Learn more here.