Hello!
I think I may have hit a bug where Metricbeat SQL module's cursor state can be read from the wrong Elastic Agent component data path, causing cursor reset and duplicate ingestion.
This is on Elastic Agent / Metricbeat 9.4.2.
This was first observed through a custom integration package, but the package only renders a normal Elastic Agent sql/metrics input using the upstream Metricbeat SQL cursor config.
The input has a stable cursor.state_id, stable query text, and stable cursor column/direction.
What happens:
- SQL cursor runs normally and advances.
- I change an unrelated metric integration in the same Agent policy.
- After the policy reload, the SQL cursor sometimes starts from its default again and rereads rows.
In the logs I can see SQL cursor registries being created under different component paths. One registry was created under the expected sql/metrics-default path:
Jun 24, 2026 @ 18:39:08.034 component.id=sql/metrics-default
Created shared SQL cursor registry at /opt/Elastic/Agent/data/elastic-agent-9.4.2-dd9ee6/run/sql/metrics-default/sql-cursor (ptr=0x3348fa9e55c0)
Later, another SQL cursor registry was created under the system/metrics-default component path and then used by sql/metrics-default:
Jun 24, 2026 @ 18:40:04.553 component.id=system/metrics-default
Created shared SQL cursor registry at /opt/Elastic/Agent/data/elastic-agent-9.4.2-dd9ee6/run/system/metrics-default/sql-cursor (ptr=0x3348fad47e60)
Jun 24, 2026 @ 18:40:04.553 component.id=sql/metrics-default
Using shared SQL cursor registry at 0x3348fad47e60
For one cursor, the same state_key was already advanced immediately before this happened:
Jun 24, 2026 @ 18:39:10.912 component.id=sql/metrics-default
Cursor fetch completed: state_key=sql-cursor::fba2dd494f9d31a0 cursor_before=0 cursor_after=79 rows=79
After the system/metrics-default registry was used, the same state_key started from 0 again and reread the same rows:
Jun 24, 2026 @ 18:40:04.568 component.id=system/metrics-default
Finished loading transaction log file for '/opt/Elastic/Agent/data/elastic-agent-9.4.2-dd9ee6/run/system/metrics-default/sql-cursor/cursor-state'. Active transaction id=0
Jun 24, 2026 @ 18:40:06.152 component.id=sql/metrics-default
Cursor fetch start: driver=mysql timeout=1m0s state_key=sql-cursor::fba2dd494f9d31a0 cursor=0
Jun 24, 2026 @ 18:40:06.171 component.id=sql/metrics-default
Cursor fetch completed: state_key=sql-cursor::fba2dd494f9d31a0 cursor_before=0 cursor_after=79 rows=79
The interesting part is that this seems limited to the Agent OTel runtime. With this extra Agent config, I have not been able to reproduce the issue so far:
agent.internal:
runtime:
metricbeat:
sql/metrics: process
So my vague guess is that the SQL cursor registry may be using a shared/global path.data value that can point at another Metricbeat component under the OTel runtime.
Does this look like a bug in the Metricbeat SQL cursor implementation, or is there some expected Agent/OTel behavior I am missing?