Skip to content

Fix bulk attendance download stalling over WAN / high-latency links#10

Open
hmshb wants to merge 1 commit into
coding-libs:masterfrom
hmshb:fix/wan-large-download-stall
Open

Fix bulk attendance download stalling over WAN / high-latency links#10
hmshb wants to merge 1 commit into
coding-libs:masterfrom
hmshb:fix/wan-large-download-stall

Conversation

@hmshb

@hmshb hmshb commented Jun 9, 2026

Copy link
Copy Markdown

Problem

getAttendances() works perfectly on a LAN but truncates the bulk download to roughly the first ~2 chunks when the device is reached across the internet or any slow / high-latency link. It typically surfaces as Fetched 0 logs or only old records, with the recent punches (at the tail of the history) never arriving. This is a long-standing pain point — see node-zklib#31.

I traced it on a live ZKTeco K50 behind PPPoE, reached from a cloud server. The connection, voice test and record-count all worked; only the bulk download stalled — consistently at the same byte boundary regardless of timeout.

Root causes (in readWithBuffer)

  1. All chunk requests were fired up front (for (i=0..numberChunks) sendChunkRequest(...)). On a LAN the device keeps up. Over a high-latency link the device's send buffer fills faster than the link drains, the later chunks never arrive, and the stream wedges — truncating the download.

  2. The inter-packet timeout was hardcoded to 10000 ms and ignored the constructor timeout, so slow links couldn't be given more breathing room.

Changes (all backward compatible)

  • Request chunks sequentially — send one chunk request, wait for it to fully arrive, then request the next. Only one chunk is ever in flight, so the slow link keeps up and the download runs to completion. This is the same approach mature implementations (e.g. Python pyzk) use.
  • Respect the configured timeout for the inter-packet wait instead of the hardcoded 10s (falls back to 10s when unset).
  • New optional maxChunk constructor argument (default unchanged at 65472). Lowering it (e.g. 8184) makes each chunk individually more robust on poor links.
// unchanged default behaviour:
const device = new ZKLib(ip, 4370, 10000, 5200)
// smaller chunks for a slow/WAN link:
const device = new ZKLib(ip, 4370, 60000, 5200, 8184)

Result

On the real K50 over the WAN link this was the difference between a download that stalled at ~3,200 records (months-old data) and one that pulled the full ~9,900 records including same-day punches in ~18s.

No API breakage: omit maxChunk and behaviour is identical to before. Verified against a live device; node -c clean.

…ency links

getAttendances() reliably returns the full history on a LAN but truncates
to the first ~2 chunks (often surfacing as "0 logs" or only old records)
when the device is reached across the internet / a slow link. This is a
long-standing pain point (see node-zklib#31).

Two root causes in readWithBuffer, plus one knob:

1. Chunk requests were fired all at once (`for i in numberChunks:
   sendChunkRequest`). On a LAN the device keeps up; over a high-latency
   link its send buffer fills faster than the link drains and the later
   chunks never arrive, truncating the download. Now requests one chunk at
   a time, waiting for each to fully arrive before requesting the next.

2. The inter-packet timeout was hardcoded to 10000ms and ignored the
   constructor `timeout`. Now respects `this.timeout` (falls back to 10s),
   so slow links can be given more time.

3. New optional `maxChunk` constructor arg (default unchanged at 65472).
   Lowering it (e.g. 8184) makes each chunk individually more robust; on a
   real ZKTeco K50 behind PPPoE this was the difference between a download
   that stalled at ~3,200 records and one that pulled the full ~9,900 in
   ~18s. Fully backward compatible — omit it and behaviour is unchanged.

Verified against a live K50 over a WAN link.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant