Issue draft — OpenADS v1.6.0 performance regression on local INDEX ON
Title: Local (rddads) INDEX ON is ~11× slower than SAP ACE on real-world DBFCDX
Symptom
Rebuilding the CDX bag of a single production table takes:
| Engine |
Time |
| SAP ACE 11.10 (ace64.dll) |
0:28 |
| OpenADS 1.6.0 (this build) |
5:16 |
Same DBF, same expressions, same host, single-threaded local access, no server involved. That is roughly an 11× slowdown.
Environment
- OpenADS: v1.6.0 prebuilt Windows x64 release + a locally rebuilt DLL using
openads_ace_mapped.def (SAP ordinal remap per docs/en/ordinal-compat.md) — both showed the same regression.
- Host: Windows 11 Pro x64, MSVC 2026 (v18) toolchain.
- Harbour: 3.2dev,
contrib/rddads (name-linked build for the isolated repro; production consumer is ordinal-linked).
- Mode:
AdsSetServerType(ADS_LOCAL_SERVER), AdsSetFileType(2) (DBFCDX), AdsSetCharType(ADS_OEM, .T.), AdsCacheOpenTables(1000), AdsCacheOpenCursors(500), AdsLocking(.T.), AdsRightsCheck(.F.).
- CDX collation:
NTXPL852 (from adslocal.cfg).
Workload scale (real production dataset)
A single client's database directory:
- 609 DBF files totalling ~5.6 GB (top individual files:
WOP_ARC.DBF 318 MB, ZAP.DBF (monthly, several copies) 200-300 MB each, NOTY_R.DBF 270 MB).
- 374 CDX files, each typically holding 10-16 tags (production example
ASORTYM.CDX: 16 tags, ~1.9 MB).
- Root-level global dictionaries (~65 DBF) plus per-magazine per-month subdirs (
2026/01/…2026/07/…) each containing full document/asortyment/transaction tables.
A full reindex is a routine maintenance action (e.g., after a crash-recovery or during migration). On SAP ACE it completes in minutes; scaled by the 11× factor observed on a single table, on OpenADS 1.6.0 the same job would take hours, which makes it operationally unusable.
Isolated repro (single table)
Table: kopia ASORTYM.DBF (14 118 records, 125 columns, ~21 MB) + 16-tag ASORTYM.CDX.
Repro script (Harbour, contrib/rddads, ace64.dll side-by-side):
REQUEST ADSCDX
PROCEDURE Main()
LOCAL nStart
rddSetDefault("ADSCDX")
AdsSetServerType(1); AdsSetCharType(2, .T.); AdsSetFileType(2)
AdsCacheOpenTables(1000); AdsCacheOpenCursors(500)
AdsLocking(.T.); AdsRightsCheck(.F.)
USE ASORTYM VIA "ADSCDX" NEW EXCLUSIVE
FErase("ASORTYM.cdx") // drop existing CDX to force full rebuild
nStart := Seconds()
INDEX ON Upper(A_NAZWA) TAG A_NAZWA
INDEX ON Val(A_OZNACZE) TAG A_OZNACZ
INDEX ON A_KOD1 TAG A_KOD1
INDEX ON A_KOD2 TAG A_KOD2
// ... 12 more tags analogous to production CDX
? "Elapsed:", Seconds() - nStart
USE
RETURN
What we already ruled out
- Not I/O: source DBF is on the same local NVMe for both engines, warm cache; SAP finishes in 28 s reading the same bytes.
- Not
AdsRightsCheck / locking: identical flags used.
- Not ordinal drift: we rebuilt OpenADS with SAP-ordinal
.def and the perf number is unchanged from the stock release DLL.
- Not tag count: even single-tag
INDEX ON Val(A_OZNACZE) on the same table is visibly slower than the full 16-tag build on SAP.
Ask
- Is there a known perf bottleneck in the CDX write path (leaf allocation, key-encoding, or collation lookup for
NTXPL852) that could account for an ~11× cost?
- Are there compile-time or runtime options we should try (e.g., a bulk-load mode) before this is treated as a bug?
- Would you like a
dumpbin-style profile or a self-contained repro archive (anonymised DBF + the .prg above)? Happy to attach if it helps.
Related earlier issue: #127 (numeric dbSeek + NTXPL852 collation) — that was fixed in v1.6.0 based on our earlier isolated tests, so we're re-evaluating adoption; this is the last remaining blocker.
Thanks for the fast pace of releases — the 1.5.x → 1.6.0 progress on correctness has been remarkable, hoping the write path can follow.
Issue draft — OpenADS v1.6.0 performance regression on local INDEX ON
Title:
Local (rddads) INDEX ON is ~11× slower than SAP ACE on real-world DBFCDXSymptom
Rebuilding the CDX bag of a single production table takes:
Same DBF, same expressions, same host, single-threaded local access, no server involved. That is roughly an 11× slowdown.
Environment
openads_ace_mapped.def(SAP ordinal remap perdocs/en/ordinal-compat.md) — both showed the same regression.contrib/rddads(name-linked build for the isolated repro; production consumer is ordinal-linked).AdsSetServerType(ADS_LOCAL_SERVER),AdsSetFileType(2)(DBFCDX),AdsSetCharType(ADS_OEM, .T.),AdsCacheOpenTables(1000),AdsCacheOpenCursors(500),AdsLocking(.T.),AdsRightsCheck(.F.).NTXPL852(fromadslocal.cfg).Workload scale (real production dataset)
A single client's database directory:
WOP_ARC.DBF318 MB,ZAP.DBF(monthly, several copies) 200-300 MB each,NOTY_R.DBF270 MB).ASORTYM.CDX: 16 tags, ~1.9 MB).2026/01/…2026/07/…) each containing full document/asortyment/transaction tables.A full reindex is a routine maintenance action (e.g., after a crash-recovery or during migration). On SAP ACE it completes in minutes; scaled by the 11× factor observed on a single table, on OpenADS 1.6.0 the same job would take hours, which makes it operationally unusable.
Isolated repro (single table)
Table: kopia
ASORTYM.DBF(14 118 records, 125 columns, ~21 MB) + 16-tagASORTYM.CDX.Repro script (Harbour,
contrib/rddads,ace64.dllside-by-side):What we already ruled out
AdsRightsCheck/ locking: identical flags used..defand the perf number is unchanged from the stock release DLL.INDEX ON Val(A_OZNACZE)on the same table is visibly slower than the full 16-tag build on SAP.Ask
NTXPL852) that could account for an ~11× cost?dumpbin-style profile or a self-contained repro archive (anonymised DBF + the .prg above)? Happy to attach if it helps.Related earlier issue: #127 (numeric dbSeek + NTXPL852 collation) — that was fixed in v1.6.0 based on our earlier isolated tests, so we're re-evaluating adoption; this is the last remaining blocker.
Thanks for the fast pace of releases — the 1.5.x → 1.6.0 progress on correctness has been remarkable, hoping the write path can follow.