-
Type:
Incident report
-
Resolution: Duplicate
-
Priority:
Trivial
-
None
-
Affects Version/s: 8.0.0
-
Component/s: Proxy (P)
-
None
-
- Summary
After a network outage between an active proxy and the server that is longer than the time needed to fill ProxyMemoryBufferSize, the data that overflowed into `proxy_history` and the data kept in memory arrive on the server completely. However, history collected by the proxy during the following ~150 seconds, starting at the moment the connection comes back, never reaches the server. No error or warning is logged on the proxy or on the server (default log level; no debug log was captured, see "Not investigated"). With `ProxyBufferMode=disk` and everything else unchanged there is no gap.
A second run with `DebugLevel=4` (2026-11-10, see "Root cause evidence") shows the mechanism: after the database to memory transition the proxy assigns ids to new values that are lower than the ids already sent from the database, and the server drops them with "received value identifier N from proxy 1 is lower than the last processed value identifier M".
-
- Environment
- Zabbix server 8.0.0 and Zabbix proxy 8.0.0 (revision c81e276e505), packages from repo.zabbix.com (RHEL 10 compatible build), Oracle Linux 10 on both.
- Proxy: active mode, MySQL 9.7.0 (`AllowUnsupportedDBVersions=1` is set because 9.7 is not in the supported list for 8.0.0), `ProxyBufferMode=hybrid`, `ProxyMemoryBufferSize=16M`, `ProxyMemoryBufferAge` and `ProxyOfflineBuffer` left at defaults.
- Monitored host with 500 items on the proxy: `net.tcp.service[tcp,127.0.0.1,<port>]`, type simple check, unsigned integer, update interval 1s, history 1h, no trends. About 500 values/s. A second host monitored by the server itself is used as a reference (no gaps).
- Server and proxy are VMs on the same network. The outage is simulated on the proxy only (`ip route add unreachable <server>/32`), the proxy process keeps running and collecting.
-
- Steps to reproduce
1. Proxy 8.0.0 with `ProxyBufferMode=hybrid`, `ProxyMemoryBufferSize=16M`, 500 items at 1s as above, running normally.
2. Record `select count
, min(id), max(id) from proxy_history` and `select nextid from ids where table_name='proxy_history' and field_name='history_lastid'` on the proxy.
3. Cut the proxy-to-server connection for 5 minutes (`ip route add unreachable <server>/32`, then `ip route del ...`).
4. Wait about 6 more minutes, then query the server with `history.get` for a few of the items for the whole period and look for gaps in `clock` (more than 3 s).
-
- Actual result
Run on 2026-11-09 (route cut 15:51:41 to 15:56:42, local time):
- The memory buffer holds about 80,000 values (about 160 s at 500 values/s). Overflow started about 150 s after the cut: `proxy_history` went from 1 row to 72,949 rows, ids 4,275,689 to 4,348,637, clock 15:54:17 to 15:56:45, contiguous ids (memory rows before that used ids 4,111,618 to 4,275,688, one monotonic counter).
- Right after the connection is restored `history_lastid` jumped within 15 s to 4,348,637 (= `max(id)`) and the number of rows in `proxy_history` stayed at 72,950 until the next housekeeper run.
- No new rows were written to `proxy_history` after clock 15:56:45 (`max(clock)` constant for 6 minutes), so the rows collected after reconnect went through memory.
- On the server, `history.get` for three of the items shows data for 15:51:41 to 15:56:45 (both the part from memory and the part from the database buffer arrived) and then *no values from 15:56:45 to 15:59:15 (150 s, about 75,000 values for 500 items)*, then normal data again. The reference host has no gap.
- Server and proxy logs (default log level) contain no error. The server only logs the proxy state change (online to offline to online).
-
- Expected result
All values collected by the proxy during and after the outage are delivered, as documented for the proxy buffer.
-
- Comparison
| Run | Mode | Outage | Result |
| — | — | — | — |
| A | hybrid 16M | 120 s (shorter than the buffer) | no loss, nothing written to the database |
| B | hybrid 16M | 5 min (run on 2026-11-09, described above) | 150 s gap after reconnect |
| C | disk (ProxyMemoryBufferSize removed, because 8.0.0 refuses to start with it) | 5 min | no gap; `proxy_history` grows continuously during the outage (about 148,000 rows), `history_lastid` catches up within about 90 s after reconnect |
Earlier runs on 2026-11-08 with the same configuration (5 min three times, 10 min 42 s once) also showed missing data on the server in every run longer than the buffer. In those runs the position of the gap relative to the reconnect was not recorded precisely, so only run B above is reported as evidence for "after reconnect". About 1 % of values are missing in every run (also in run C) because of normal poller jitter on 1 s checks; this is not the issue reported here.
-
- Not investigated / limitations
- The 2026-11-09 run had no debug log; the 2026-11-10 run (run D below) has one, and the server log of the same run.
- Only one test setup, MySQL 9.7.0 (unsupported for 8.0.0 officially), synthetic data, a single run per mode on 2026-11-09.
- I have not tested 7.4 or PostgreSQL. A production proxy runs 7.4.9 with `ProxyBufferMode=hybrid`; the 7.4 and 8.0 `pb_history.c` have the same size, but I did not diff them and I do not know if 7.4.9 is affected.
- Code reading of `release/8.0` (`pb_history.c`, `proxybuffer.c`, `datasender.c`, `dbmisc.c`) did not give an explanation; I did not locate the body of `zbx_dc_get_nextid`. Hypotheses I tested and rejected: two different id counters for memory and database rows (ids of database rows were higher than the ones from memory), reset of the id cache (no such function in 8.0).
- Possibly related but a different symptom:
ZBX-26560(7.2.5, hybrid, outage of hours; closed "Commercial support required"; the server logs "received id ... is less than last id", the reporter gets no new data at all). In my runs the proxy kept sending, only the 150 s are missing, and I did not check the server log for that message. ZBX-27656 (affects 7.4.7, fixed in 7.4.17rc1 / 8.0.1rc1; proxy group, data of the outage window missing after the server was stopped) has a similar outcome but a different trigger; I have not tested 8.0.1rc1.
-
- Root cause evidence (run D, 2026-11-10, hybrid 16M, proxy `DebugLevel=4`, route cut 11:15:18 to 11:20:19)
Proxy log (state transitions):
```
11:18:06.772 pb_set_state() memory => memory->database "initiated proxy buffer transition to database state: not enough space"
11:20:21.420 pb_set_lastid() [proxy_history.history_lastid:8160706]
11:20:21.442 pb_set_state() memory->database => database "switched proxy buffer to database state: memory records have been uploaded"
11:20:23.088 pb_set_state() database => database->memory "initiated proxy buffer transition to memory state: no more database records to upload"
11:20:24.103 pb_set_state() database->memory => memory "switched proxy buffer to memory state: database records have been uploaded"
```
- Rows written to the database during the overflow have contiguous ids 8160707 to 8222995 (62,289 rows); the last id used by memory rows before that is 8160706. They are delivered completely (11:20:21 to 11:20:23).
- After the switch back to memory the data sender sets `history_lastid` to *8161828* (11:20:25) and then advances it by 500 per second (500 values/s), i.e. new values get ids that continue from the last memory id, not from 8222995:
```
11:20:26 rows:500 lastid:8162328 11:20:27 rows:500 lastid:8162828 ... 11:22:36 rows:500 lastid:8223377
```
- Server log of the same run (server 8.0.0, one line per second from 11:20:25):
```
received value identifier 8161828 from proxy 1 is lower than the last processed value identifier 8222995
received value identifier 8162328 from proxy 1 is lower than the last processed value identifier 8222995
...
```
- The proxy id passes 8222995 at 11:22:36 and that is exactly when values appear again on the server. Missing interval on the server: 11:20:21 to 11:22:36 (135 s, about 67,500 values for 500 items); duration is about (number of overflow rows) / (values per second): run B 72,949 rows gave 150 s, run D 62,289 rows gave 135 s.
- Conclusion from the logs: when leaving the `database` state, the id counter used for memory rows is not moved past the ids that were given to rows written to the database (a single counter that is not advanced by the database writes). The server then rejects every value whose id is not above the last processed id. Not looked up in the code: where the counter is assigned.
- Not tested: 7.4 (the same id logic may apply), PostgreSQL.
-
- Workaround found
`ProxyBufferMode=disk` (remove `ProxyMemoryBufferSize`).
- duplicates
-
ZBX-27656 The proxy group stopped data collection after the server stopped.
-
- Ready for QA
-