Skip to content

afpd: a stock config gets the documented 64K dircache - #3264

Open
andylemin wants to merge 1 commit into
mainfrom
perf-dircache-size-default
Open

afpd: a stock config gets the documented 64K dircache#3264
andylemin wants to merge 1 commit into
mainfrom
perf-dircache-size-default

Conversation

@andylemin

@andylemin andylemin commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Root cause

Two constants both mean "the dircache size default", and they drifted apart:

  • DEFAULT_DIRCACHE_SIZE = 65536 in etc/afpd/dircache.h — what dircache_init() applies, and what the man page, the docker image, and the webmin module all document.
  • DEFAULT_MAX_DIRCACHE_SIZE = 8192 in include/atalk/globals.h — what an unset dircache size actually resolves to in afp_config_parse().

Because 8192 is above the 1024 minimum, dircache_init() accepted it verbatim — so every stock configuration has been running an 8× smaller directory cache than documented. Explicit dircache size settings were never affected.

How the drift got here

when commit what happened
2012-02-17 6655a7d volume loading moved to libatalk, bringing the config-side default constant (8192) with it — the documented default at the time
later 9aa8080 the dircache work raised the documented default to 65536 (man page and dircache.h), but the config-side constant in globals.h was not part of that change

Two constants meaning "the default" diverged, and nothing tied them together.

Defect → fix

defect fix
stock config resolves 8192 while all documentation says 65536; nothing prevents the two constants drifting apart again collapsed into a single DEFAULT_DIRCACHE_SIZE = 65536 in globals.h, which dircache.h already includes — one definition, nothing to drift
size-bounds behaviour was untestable (dircache_maxsize is a static set inside dircache_init(), which also builds live cache state) resolution extracted into a pure dircache_resolve_size() helper; dircache_init() calls it
two stale comments describe the resolution as max(DEFAULT_DIRCACHE_SIZE, min(size, MAX_DIRCACHE_SIZE)) — never the actual algorithm — and reference a nonexistent DIRCACHE_FREE comments now describe the real resolution (unset/below-min → default, in-range → round up to power of two, oversize → clamp); DIRCACHE_FREE_QUANTUM
webmin labels the option "(bytes)" though the unit is entries, and its form cap (2097152) exceeds MAX_DIRCACHE_SIZE (1048576) label and cap corrected
the shutdown statistics lines print the configured cache maximum unit-less (config_max:), inviting the same bytes-vs-entries misreading renamed to config_max_entries: in both the ARC and LRU lines; lantest only matches the line prefix, so nothing parses the old label; doc/developer/dircache.md field list and examples updated
doc/developer/dircache.md still described the dircache as "by default just 8192 entries" — the very stale default this PR fixes reworded to the corrected 65536 default

Behaviour changes

  • Stock install: 8K → 64K-entry dircache (roughly ~1.5 MB → ~12 MB under LRU at full occupancy).
  • Explicit dircache size values: unchanged. Bounds unchanged: minimum 1024 (deliberately kept — the spectest CI legs run dircache size = 1024 to exercise cache exhaustion and eviction), maximum 1048576, in-range values still round up to the next power of two.
  • The man page bounds line was already correct and is untouched.

Size resolution

requested dircache size dircache_resolve_size() result
unset (≤ 0) 65536
1 – 1023 65536, with a warning
1024 – 1048576 rounded up to the next power of two
> 1048576 clamped to 1048576, with a warning

Testing

  • New unit test utest_conf_dircache_resolve_size in test/afpd/subtests_conf.c: bounds matrix over the pure helper — -1 → 65536, 512 → 65536 (warn), 1024 → 1024, 100000 → 131072, 2000000 → 1048576 (clamp warn)
  • meson test (Alpine container, CI build flags): 3/3 suites, 88 afpd subtests passed
  • AFP spectest, ea=sys and ea=ad legs (single container, CI env including dircache size = 1024): both exit 0, zero failures

@andylemin

Copy link
Copy Markdown
Contributor Author

augment review

@augmentcode

augmentcode Bot commented Aug 24, 2026

Copy link
Copy Markdown

Looks like you're out of credits for this review. Head to your account settings to top up and keep reviews running. https://app.augmentcode.com/account/subscription

An unset 'dircache size' resolved to DEFAULT_MAX_DIRCACHE_SIZE = 8192
(globals.h), a netatalk-2.x-era constant that predates the dircache
rewrite, while dircache_init()'s own DEFAULT_DIRCACHE_SIZE, the man
page, the docker image, and the webmin module all say 65536 -- two
constants meaning "the default" drifted, and every stock install has
run an 8x smaller cache than documented.

Collapse the two into a single DEFAULT_DIRCACHE_SIZE in globals.h
(which dircache.h already includes), so the default is defined once
and cannot drift again. Extract dircache_init()'s size resolution
into a pure helper so the bounds behaviour is unit-testable. Fix the
stale size-formula comments and the webmin label/cap while touching
the files.
@andylemin
andylemin force-pushed the perf-dircache-size-default branch from 1878605 to bbe6a6a Compare August 24, 2026 11:04
@andylemin

Copy link
Copy Markdown
Contributor Author

@rdmark ready for review

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

📊 Performance Dashboard

Commit: bbe6a6a7f8a3a468db2df05506166c3730c41e62

🔥 Spectest (AFP 3.4) - FlameGraph

Netatalk Code-time: 3.2% · Runtime: 59s · Stacks: 727

🔥 Click the preview to open the interactive flamegraph (zoom + search).

Flamegraph preview

🔝 Top 10 leaf functions
Function Samples
_raw_spin_unlock_irqrestore 147739690
do_syscall_64 72767310
[libsqlite3.so.3.53.4] 71664775
afpd 49614075
finish_task_switch.isra.0 46306470
__cp_end 42998865
_raw_spin_lock 18743095
perf_syscall_enter 16538025
__syscall_cp_c 15435490
__raw_callee_save___pv_queued_spin_unlock 14332955

📈 Speedtest (AFP 3.4) - PerfGraph

Speedtest throughput

Peak Read: 6387 MB/s (-11.7% vs hist avg 7233.9 MB/s; min 5963 / max 9526 over 26 PRs)
Peak Write: 1547 MB/s (+28.9% vs hist avg 1200.0 MB/s; min 225 / max 1897 over 26 PRs)

🔝 Throughputs per operation (vs. historical average)
Metric Current (MB/s) Cur Avg Δ% Hist avg Hist min Hist max
Read peak mean 6387 -11.7% 7233.9 5963 9526
Read avg mean 3548 -15.6% 4204.6 3524 5393
Read avg max 4197 -9.5% 4636.9 3812 5932
Write peak mean 1547 +28.9% 1200.0 225 1897
Write avg mean 463 +14.6% 404.0 91 567
Write avg max 848 +17.3% 723.2 224 965
Copy peak mean 2547 -5.3% 2688.7 2294 3489
Copy avg mean 1460 -8.4% 1593.7 1360 2251
Copy avg max 1573 -9.8% 1743.5 1515 2448
ServerCopy peak mean 3449 -7.8% 3741.0 3055 5090
ServerCopy avg mean 1825 -6.7% 1956.8 1604 2700
ServerCopy avg max 1873 -7.9% 2033.2 1675 2773

⏱️ Lantest (AFP 3.4) - LatencyGraph

Lantest latency

Avg total runtime: 4432 ms (+3.5% vs hist avg 4282.9 ms; min 2412 / max 5208 over 26 PRs)
Avg time per AFP op: 89 µs (+3.6% vs hist avg 85.9 µs; min 48 / max 105 over 26 PRs)

🐢 All operations (avg runtime, in test order, vs. historical average)
Metric Current (ms) Cur Avg Δ% Adj Δ% Hist avg Hist min Hist max
Writing one large file 39 +7.4% 36.3 26 41
Reading one large file 15 -1.8% 15.3 12 18
Creating 2000 files 507 -1.9% -13.5% 516.7 244 831
Create 2000 dirs tree (20×9×10) 496 -4.0% -15.5% 516.4 284 796
Open, write 1024 bytes, close 2000 files 416 +12.3% +0.7% 370.5 233 459
Open, read 1024 bytes, close 2000 files 362 +11.6% +0.0% 324.4 209 408
Copying 1000 files client-side (R+W) 586 +4.9% -6.7% 558.6 312 677
Copying 2000 files server-side 498 -5.5% -17.1% 527.2 216 727
Stat (lookup+getparams) 2000 files 246 +11.8% +0.2% 220.0 145 282
Enumerate dir with 2000 files 11 +29.4% +17.8% 8.50 3 14
Lock then unlock 2000 open forks 181 +12.2% +0.6% 161.3 118 199
Deleting 2000 files 337 -2.8% -14.4% 346.7 149 447
Byte-range lock/unlock 2000 ranges in one fork 183 +10.8% -0.8% 165.2 124 196
Directory cache hits (20 dirs x 100 files) 111 +13.4% +1.8% 97.9 67 122
Mixed cache operations (create/stat/enum/delete) on 500 files 217 -0.9% -12.5% 219.1 111 265
Deep path traversal (20 levels x 100 walks) 121 +16.0% +4.5% 104.3 69 131
Cache validation (500 files x 4 lookups) 106 +12.3% +0.7% 94.4 65 116

Run baseline: median op-test delta +11.6%, MAD 4.5%. Adj Δ% shifts each delta by the median; standouts ≥5% in bold. A large MAD means the run did not move uniformly — read the adjusted column with caution.

Performance trend

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