benchmarks: durdir benchmark that evaluates the efficacy and performance of DurDir - #907
Conversation
Max Smythe (maxsmythe)
left a comment
There was a problem hiding this comment.
Thank you for this.
Left some comments.
Also, we should avoid sending 4k line PRs where possible -- they take multiple hours to review closely.
Can we avoid refactoring anything under internal/benchmarking/boomer for now? I don't think the abstractions are quite right and the refactoring is a large portion of the delta. We can implement ~3-4 tests and then figure out what the best refactor is.
| help="Size in bytes written to /var/lib/glutton/bench-data (default 8 MiB)", | ||
| include_in_web_ui=True, | ||
| ) | ||
| parser.add_argument( |
There was a problem hiding this comment.
If this applies to any workload, it should probably live in its own Python file
There was a problem hiding this comment.
Split out. --resume-mode now lives in common/resume_mode.py
| # if those disagree the stats rows describe traffic that was never sent. | ||
| # The value must name a stub in locust/tests/, a --workload case in | ||
| # cmd/benchmarking/boomer-glutton/main.go, and an entry in runner.py's | ||
| # BOOMER_WORKLOADS dict. |
There was a problem hiding this comment.
TODO: look to reduce # of sources of truth
There was a problem hiding this comment.
Addressed
| return args | ||
|
|
||
|
|
||
| def boomer_workload(test_file: str) -> str | None: |
There was a problem hiding this comment.
It would be better to test for Python workloads... I suspect we will no longer be adding those, so it will be a static list. A cleanup work item will be to remove those tests.
This will also remove one place workload lists need to be kept in alignment
There was a problem hiding this comment.
Went with a single static map from stub filename to user class:
BOOMER_USER_CLASSES = {"glutton.py": "GluttonUser", "durdir.py": "DurDirUser"}
needs_boomer is membership in it and the same entry supplies --user-class, so the filename and the class name stay in one place rather than two.
Kept the check on the boomer side rather than the Python side because runner.py needs the class name string either way
| python3 runner.py -f tests/durdir.py -t 1m -u 1 --name durdir_run --dest /tmp/bench \ | ||
| --durdir-template glutton-durdir-data --resume-mode explicit --durdir-file-size-bytes 8388608 | ||
| ``` | ||
| * **Interactive Web UI**: The stack runs one workload per deployment, chosen at deploy time: |
There was a problem hiding this comment.
I don't think "workload" is correct... that is the name given to the ActorTemplate and workers.
One "test type"? "Virtual user type"?
There was a problem hiding this comment.
UserClass?
There was a problem hiding this comment.
Fixed.
| * `glutton-durdir-data` (default): Attaches a durable data directory under `/var/lib/glutton` without memory snapshot restore. | ||
| * `glutton-durdir-full`: Attaches a durable data directory and performs a full memory snapshot restore. | ||
|
|
||
| #### File-Size Sweeps |
There was a problem hiding this comment.
I don't think we need to document that we can run multiple tests by adding multiple tests. Also, I think setting the read mode to digest would be orthogonal to "sweep".
I'd remove this section.
There was a problem hiding this comment.
Removed.
| cfg *Config | ||
| actorName string | ||
| hostHeader string | ||
| userClass string |
There was a problem hiding this comment.
I like this idea, though we will probably need some design/refactoring to make this truly generic across all users. We'll tackle that more on a later test.
|
|
||
| // resume brings the actor up before the next request. | ||
| // | ||
| // The first resume is always explicit, whatever the mode. It is the only |
There was a problem hiding this comment.
This is far too much context for a comment. Documents the code and likely to rot quickly.
There was a problem hiding this comment.
Addressed
| user *gluttonUser | ||
| expectedDigest string | ||
| fileSize int64 | ||
| readMode gluttonpb.ReadMode |
There was a problem hiding this comment.
Things that are stored as dynamic config should not be persisted to the class, they should be read from the dynamic config, so behavior can change dynamically (might need to have some read atomicity to avoid breaking behavior due to time aliasing)
There was a problem hiding this comment.
Fixed, including the aliasing half.
| slog.Info("configured durdir file size", slog.Int64("bytes", fileSize), slog.String("template", tmpl)) | ||
|
|
||
| if readMode == gluttonpb.ReadMode_READ_MODE_DATA && fileSize > 64*1024*1024 { | ||
| slog.Warn("durdir_file_size_bytes > 64MB with durdir_read_mode=data may hit router or Envoy timeouts; set durdir_read_mode=digest", |
There was a problem hiding this comment.
No need to warn about this... if we are hitting timeouts that would be the point of the benchmark, to find out.
There was a problem hiding this comment.
Addressed
| # its own per-worker diagnostics at /metrics on :8001 (aggregate stats | ||
| # flow through the master via boomer.RecordSuccess). | ||
| # | ||
| # ${BENCHMARK_WORKLOAD} is substituted by benchmarking/locust/deploy.sh |
There was a problem hiding this comment.
Comment describes too many internal details
There was a problem hiding this comment.
Addressed
|
let's squash this on merge (and more PRs, generally, unless there's really useful commit history) |
170d93b to
b7e91e5
Compare
b7e91e5 to
98916bc
Compare
Benchmark DurDir
Fixes #673
Adds a
DurDirUserload-generation workload that exercises the DurableDirsuspend/resume loop end to end, verifies every served byte against a SHA-256
digest, and emits separable latency percentiles for each step.
The loop
Under
onCommit: Datathe container cold-boots from the OCI image and processmemory is discarded, so a matching digest after resume can only have come from
the restored DurableDir. That is the durability assertion.
Results
All six scenarios ran on GKE/gvisor at 1 VU for 1m each:
DatavsFullsnapshot scope, explicit vs implicit resume, and a 5/10/64 MiB size sweep.
Zero failures across every run, so every served byte matched its digest in
all six.
Snapshot growth over repeated overwrites:
SuspendActorlatency stayed flatacross consecutive overwrite-and-suspend cycles on the same DurableDir volume.
The loop overwrites with
WRITE_MODE_TRUNCATE, so the file is exactly X bytesafter every write and the captured directory contents are the same size every
cycle. Nothing accumulates across suspends. That is the growth question the
issue asks about.
Latency percentiles per step are in the run artifacts.
Change surface
WriteDiskreturns size + sha256; newReadDiskwith aREAD_MODE_DIGEST_ONLYmode for measuring restore cost without paying wiretransfer; disk RPCs exposed over HTTP mode.
glutton-durdir-{data,full}, with adurableDirvolume andonPause: Full/onCommit: {Data,Full}.a
DurDirUsertask on top of it; a generalresume_modeknob.--workloadselects the task at deploy time;durdir.pystub,typed dynconfig flags, six nightly scenarios.
The two boomer commits above are incremental extractions made as the second
workload landed. The final package layout for
internal/benchmarking/boomerlands as a follow-up PR.
Testing
go build ./...,go test -race ./cmd/benchmarking/... ./internal/benchmarking/...clean, no race warnings.
hack/verify-all.sh: all nine checks pass. Python protos regenerated, treeclean.
Ready; golden snapshots confirmed in the bucket.re-read with matching sha256 across every read. Process memory discarded and
container cold-booted in between.
failures.
glutton_baseline_5_usersran 919 requests with 0failures and 7 ms ping latency post-rebase. No regression on the existing
benchmark.
Deliberately out of scope
ActorSnapshothas no size field, so there isnothing for a client to read. The issue permits deferring this.
SuspendActorlatency is the available proxy;
atelet.snapshot.sizeis the server-side one.reviewable in place.
WriteRAM; small follow-up.