Skip to content

Add sync benchmarks#2276

Open
myieye wants to merge 14 commits into
developfrom
sync-perf-benchmarks
Open

Add sync benchmarks#2276
myieye wants to merge 14 commits into
developfrom
sync-perf-benchmarks

Conversation

@myieye

@myieye myieye commented May 19, 2026

Copy link
Copy Markdown
Collaborator

Adds BenchmarkDotNet-based sync benchmarks for FwLiteProjectSync so we can actually measure sync-performance work. This started as exploration of perf optimizations and gives us a baseline to measure future speedups against.

  • SyncBenchmark — first sync of the full Sena-3 project from an empty CRDT snapshot.
  • SyncMutationBenchmark — post-mutation syncs across several profiles (patch-heavy, reorder, complex-form components, mixed) to stress different change paths.
  • New Release-only CI job runs the Category=Benchmark tests and uploads BenchmarkDotNet artifacts; the main test job excludes them. Benchmark comments record the approximate speedup from the commits-order index.

No schema/migration changes. An earlier revision of this branch added a DB migration for the commits-order index; that was dropped during rebase because the index now ships from the Harmony submodule already on develop (CommitEntityConfig via EFCore.ComplexIndexes). dotnet ef migrations has-pending-model-changes is clean.

CI trigger: the benchmark job runs post-merge on develop only — not on PRs (too slow / too variance-prone on shared runners to gate every PR) and not on main (so it can't delay releases). We can move it onto the PR critical path once it's fast enough.

Also note: Sena3SyncTests moved from IClassFixture to a shared [Collection] so it serializes with the benchmark classes against the shared on-disk Sena-3 fixture.

(Reopening of #2254, which got accidentally merged into develop via the VS Code "Sync" button.)

@github-actions github-actions Bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label May 19, 2026
@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8bec4400-21fc-4f33-a135-f25ad4b64d7b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces comprehensive BenchmarkDotNet infrastructure to measure FwLite sync performance, adds two benchmark test suites (first-sync and post-mutation scenarios), integrates benchmarks into CI, and optimizes the database with a composite index on the Commits table. The harmony submodule is also updated.

Changes

Sync Performance Measurement and Optimization

Layer / File(s) Summary
BenchmarkDotNet dependencies and test fixture structure
backend/Directory.Packages.props, backend/FwLite/FwLiteProjectSync.Tests/FwLiteProjectSync.Tests.csproj, backend/FwLite/FwLiteProjectSync.Tests/Fixtures/Sena3Collection.cs, backend/FwLite/FwLiteProjectSync.Tests/Sena3SyncTests.cs
BenchmarkDotNet v0.15.8 is pinned centrally and added to the test project; Sena3Collection defines an xUnit collection fixture for serial test execution; Sena3SyncTests is updated to use the collection instead of class-level fixture.
Benchmark infrastructure and validation
backend/FwLite/FwLiteProjectSync.Tests/BenchmarkSupport.cs, backend/FwLite/FwLiteProjectSync.Tests/XUnitBenchmarkLogger.cs
BenchmarkSupport provides a factory for standardized BenchmarkDotNet configurations (in-process, monitoring strategy, 4 iterations, JSON export) and validation via AssertRunWasSuccessful; XUnitBenchmarkLogger bridges BenchmarkDotNet output to xUnit test output.
First sync benchmark measurement
backend/FwLite/FwLiteProjectSync.Tests/SyncBenchmark.cs
SyncBenchmark.First_Sync_Sena3 measures and asserts first-sync performance from empty CRDT state, enforcing a 51-second regression threshold in Release builds; skips BenchmarkDotNet in DEBUG builds.
Post-mutation sync benchmark measurement
backend/FwLite/FwLiteProjectSync.Tests/SyncMutationBenchmark.cs
SyncMutationBenchmark measures sync performance after applying profile-specific mutations (MixedRealistic, DeleteEntries, PatchLexemes, ReorderSenses, AndCompleteFormComponents); per-profile thresholds enforce regressions via BenchmarkDotNet parametrization and snapshot-based reports.
Benchmark CI workflow integration
.github/workflows/fw-lite.yaml
build-and-test job now excludes benchmarks via --filter "Category!=Benchmark"; new benchmark job builds in Release, runs only Category=Benchmark tests, and uploads BenchmarkDotNet.Artifacts as a workflow artifact.
Commits table composite index migration
backend/FwLite/LcmCrdt/Migrations/20260528091629_AddCommitsOrderIndex.cs, backend/FwLite/LcmCrdt/Migrations/20260528091629_AddCommitsOrderIndex.Designer.cs, backend/FwLite/LcmCrdt/Migrations/LcmCrdtDbContextModelSnapshot.cs
EF Core migration creates a composite index IX_Commits_DateTime_Counter_Id on the Commits table; designer and snapshot files are auto-generated and reflect EF Core ProductVersion update to 10.0.8 and complex property call signature change.
Harmony submodule update
backend/harmony
Submodule pointer updated to a newer commit.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • rmunn
  • hahn-kev
  • josephmyers

Poem

🐰 Benchmarks hop and bounds now measure sync,
First snapshots, mutations—not a blink,
Commits indexed by time and counter's link,
Performance tested before the sync shrink,
CI jobs split, artifacts in the stink!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add sync benchmarks' is clear and directly reflects the main change—adding new benchmark tests for measuring sync performance.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description clearly relates to the changeset, explaining the addition of BenchmarkDotNet-based sync benchmarks, CI configuration changes, database migrations, and test fixture adjustments.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync-perf-benchmarks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@argos-ci

argos-ci Bot commented May 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Jun 17, 2026, 4:39 PM

@github-actions

github-actions Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

UI unit Tests

  1 files   62 suites   33s ⏱️
186 tests 186 ✅ 0 💤 0 ❌
258 runs  258 ✅ 0 💤 0 ❌

Results for commit cb89aee.

♻️ This comment has been updated with latest results.

@myieye myieye force-pushed the sync-perf-benchmarks branch from 61ea495 to e1a5eac Compare May 19, 2026 12:02
@github-actions

github-actions Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

C# Unit Tests

165 tests   165 ✅  22s ⏱️
 23 suites    0 💤
  1 files      0 ❌

Results for commit cb89aee.

♻️ This comment has been updated with latest results.

@myieye myieye force-pushed the sync-perf-benchmarks branch from e1a5eac to 6082b4a Compare May 28, 2026 09:55
@github-actions github-actions Bot added the 📦 Lexbox issues related to any server side code, fw-headless included label May 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/fw-lite.yaml (1)

70-97: ⚡ Quick win

Consider hardening the benchmark job's security posture.

The static analysis tool flagged several security concerns in the new benchmark job:

  1. Unpinned actions (lines 78, 81, 93): actions/checkout@v4, actions/setup-dotnet@v4, and actions/upload-artifact@v4 use mutable tags rather than commit SHAs.
  2. Missing persist-credentials: false (lines 78-80): Credentials may persist in the checked-out repository.
  3. Missing permissions block: The job inherits default broad permissions.

While these issues are consistent with the existing workflow style in this file (other jobs use the same pattern), consider addressing them as part of a broader workflow security hardening effort across the repository.

🔒 Example hardening for the benchmark job
 benchmark:
   name: Run FW Lite sync benchmarks
+  permissions:
+    contents: read
+    actions: read
   # BenchmarkDotNet timings are only meaningful in Release; this job runs the FwLiteProjectSync.Tests
   # project in Release with --filter "Category=Benchmark" so threshold assertions actually fire.
   timeout-minutes: 40
   runs-on: windows-latest
   steps:
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
       with:
         submodules: true
+        persist-credentials: false
-    - uses: actions/setup-dotnet@v4
+    - uses: actions/setup-dotnet@3e891f8b4d438e9a0a674af8e3360b3c38e14aa1  # v4.2.0
       with:
         dotnet-version: '10.x'

Note: If adopting SHA pinning, apply it consistently across all workflow files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/fw-lite.yaml around lines 70 - 97, Update the benchmark
job to harden security: replace mutable action tags (actions/checkout@v4,
actions/setup-dotnet@v4, actions/upload-artifact@v4) with pinned commit SHAs or
fully-qualified refs, add persist-credentials: false to the actions/checkout
step to avoid leaking runner credentials, and add an explicit permissions block
scoped to the least privileges the job needs (e.g., minimal read permissions or
none for contents, and only write/artifact permissions if upload-artifact
requires it); apply these changes in the benchmark job steps that reference the
Checkout, setup-dotnet, and Upload benchmark results steps so the CI uses pinned
actions, does not persist credentials, and runs with minimal permissions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/FwLite/FwLiteProjectSync.Tests/SyncMutationBenchmark.cs`:
- Around line 96-97: The benchmark's mutation selection is nondeterministic
(using unseeded randomness via AutoFaker/unspecified RNG) causing flaky
thresholds; make mutation generation deterministic by creating a Random seeded
from the benchmark profile (e.g., profile index or name hash) and use it for
selection and shuffling instead of unseeded calls. Add a helper
Shuffle<T>(IReadOnlyList<T> source, Random rng) and use it wherever mutations
are chosen (the mutation setup code around AutoFaker and the blocks referenced
at the mutation generation sites) and, if AutoFaker is used to produce random
values, initialize or replace its RNG with the same seeded Random so the whole
mutation shape is reproducible per profile. Ensure the seed derivation is stable
and deterministic for each profile so CI results are stable.

---

Nitpick comments:
In @.github/workflows/fw-lite.yaml:
- Around line 70-97: Update the benchmark job to harden security: replace
mutable action tags (actions/checkout@v4, actions/setup-dotnet@v4,
actions/upload-artifact@v4) with pinned commit SHAs or fully-qualified refs, add
persist-credentials: false to the actions/checkout step to avoid leaking runner
credentials, and add an explicit permissions block scoped to the least
privileges the job needs (e.g., minimal read permissions or none for contents,
and only write/artifact permissions if upload-artifact requires it); apply these
changes in the benchmark job steps that reference the Checkout, setup-dotnet,
and Upload benchmark results steps so the CI uses pinned actions, does not
persist credentials, and runs with minimal permissions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a4a521b7-a9b5-420d-b662-ffb766c48d55

📥 Commits

Reviewing files that changed from the base of the PR and between c83114c and 6082b4a.

📒 Files selected for processing (13)
  • .github/workflows/fw-lite.yaml
  • backend/Directory.Packages.props
  • backend/FwLite/FwLiteProjectSync.Tests/BenchmarkSupport.cs
  • backend/FwLite/FwLiteProjectSync.Tests/Fixtures/Sena3Collection.cs
  • backend/FwLite/FwLiteProjectSync.Tests/FwLiteProjectSync.Tests.csproj
  • backend/FwLite/FwLiteProjectSync.Tests/Sena3SyncTests.cs
  • backend/FwLite/FwLiteProjectSync.Tests/SyncBenchmark.cs
  • backend/FwLite/FwLiteProjectSync.Tests/SyncMutationBenchmark.cs
  • backend/FwLite/FwLiteProjectSync.Tests/XUnitBenchmarkLogger.cs
  • backend/FwLite/LcmCrdt/Migrations/20260528091629_AddCommitsOrderIndex.Designer.cs
  • backend/FwLite/LcmCrdt/Migrations/20260528091629_AddCommitsOrderIndex.cs
  • backend/FwLite/LcmCrdt/Migrations/LcmCrdtDbContextModelSnapshot.cs
  • backend/harmony

Comment thread backend/FwLite/FwLiteProjectSync.Tests/SyncMutationBenchmark.cs
@myieye myieye force-pushed the sync-perf-benchmarks branch 6 times, most recently from 32f8d6e to a119903 Compare June 1, 2026 13:43
@myieye myieye marked this pull request as draft June 2, 2026 12:53
myieye added 11 commits June 17, 2026 15:13
Monitoring strategy with WarmupCount=1, IterationCount=4 — same total
work as before, but iteration 1's JIT/EF-model-build/cold-cache cost is
discarded instead of folded into the measurement, so StdDev tightens and
thresholds can come down to genuine 3σ above the with-index mean.

Both comment lines (baseline + with-index) are re-measured under this
config so the speedup comparison is apples-to-apples.
BenchmarkDotNet has supported async benchmark methods for a long time —
the old comment claiming otherwise was wrong, and the .Result blocking
call was unnecessary. IterationSetup stays sync (async support there
needs BDN 0.16+).
Hosted CI runners are noisy enough that the previous ~3σ thresholds flaked.
Bump bounds to catch large regressions only, and rewrite the threshold
comments to reflect what we actually believe holds up under run-to-run
variance (patch-heavy and first-sync show real speedups; the other
profiles are kept as regression guards, not perf claims).
@myieye myieye force-pushed the sync-perf-benchmarks branch from a119903 to f0278fd Compare June 17, 2026 13:50
- Pin benchmark job's actions to SHAs (match the rest of the workflow)
- Gate the benchmark job to push-on-develop (off the PR critical path)
- Surface the last error when complex-form link setup can't reach its target
- Fail artifact upload when no benchmark results are produced

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@myieye myieye force-pushed the sync-perf-benchmarks branch from f0278fd to f45447c Compare June 17, 2026 13:56
myieye and others added 2 commits June 17, 2026 16:40
- Bump the benchmark job timeout to 90 min: the suite re-imports Sena-3 per
  iteration across all profiles, so it runs well past the 40 min used elsewhere.
- Revert benchmark artifact upload to if-no-files-found: warn — with if: always()
  an error would add a misleading second failure on any build/crash that emits no
  artifacts, while a threshold-assertion failure still writes artifacts first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prior runs (before the job was disabled on feature branches) took ~30 min,
worst observed ~34. 60 min clears that plus hosted-runner variance without
the excessive headroom of the earlier value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@myieye myieye closed this Jun 17, 2026
@myieye myieye reopened this Jun 17, 2026
@myieye myieye marked this pull request as ready for review June 17, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related 📦 Lexbox issues related to any server side code, fw-headless included

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant