Skip to content

perf(adt): sequential read-ahead cache + shared-lock header refresh - #163

Merged
FiveTechSoft merged 1 commit into
FiveTechSoft:mainfrom
russimicro:perf/adt-sequential-readahead
Aug 5, 2026
Merged

perf(adt): sequential read-ahead cache + shared-lock header refresh#163
FiveTechSoft merged 1 commit into
FiveTechSoft:mainfrom
russimicro:perf/adt-sequential-readahead

Conversation

@russimicro

Copy link
Copy Markdown
Collaborator

The ADT driver read one record per call even on a strictly sequential walk, and
refreshed the header under an exclusive lock. The CDX driver already had a
read-ahead block cache; this gives ADT the same treatment:

  • a 64 KB sequential read-ahead cache, invalidated on every write and on the
    absolute repositions that must see another station's work;
  • the header refresh takes a shared lock, so concurrent readers no longer
    serialise on it.

Measured on a Harbour/FiveWin ERP's 34,595-row article table: a full reindex
went from ~2 min to ~60 s. Nothing about visible semantics changes — only how
many reads it takes to get the same rows.

Tests

Full suite green on this branch (1290 cases). The behaviour is already pinned
by the existing ADT navigation, scope and multiuser tests; this PR adds no new
one because it adds no new behaviour.

Reapplies the Russoft ADT driver work on top of v1.8.31.

- 64 KB aligned read-ahead block cache in read_record_raw(). A reindex or
  PACK scan reads every record 1..N once per tag; each read used to be a
  positioned syscall plus a heap allocation, which made an ADT reindex of a
  large table (ESTAELEC ~441k rows) take minutes against ~seconds for
  DBF/CDX. Invalidated on any local write/append/zap/truncate.
- refresh_record_count_shared_(): take the 0..399 header region shared (with
  retry) before re-reading the count, so a reader waits out an appender
  instead of failing with ERROR_LOCK_VIOLATION -> 5000. read/write now
  refresh before reporting "record number out of range".
- open() takes the same shared header lock, and acquire_with_retry_ grew a
  LockKind parameter (defaults to Exclusive, so existing callers are
  unchanged).
- cap_record_count_from_size_(): clamp the header count to the records that
  physically fit, so a .DAT truncated by a crash or partial copy reads short
  instead of raising 5000 mid-scan.

Suite: 1177/1189, the same 12 SQL-parser (7200) failures — no regressions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit ae52590)
@FiveTechSoft
FiveTechSoft merged commit 1c24a4d into FiveTechSoft:main Aug 5, 2026
11 checks passed
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.

2 participants