Skip to content

ci: investigate recurring WHP mapped-memory-flush stalls #169

Description

Summary

Windows/WHP CI intermittently enters a slow snapshot-generation regime that is isolated to OpenVMM's capture.mapped_memory_flush phase. The condition can persist across both bounded lifecycle measurements and fail an otherwise unrelated pull request.

This recurred in run 35648438246, attempt 1, in the Windows/WHP platform job. An immediate workflow rerun on the same runner succeeded, but its first lifecycle measurement was unstable again before the one allowed remeasurement produced a stable series.

#119 tracks the telemetry and artifact improvements needed to diagnose Windows storage behavior. This issue tracks the recurring infrastructure condition itself, its runner correlation, and its eventual mitigation.

Current incident

  • Workflow: CI, run 35648438246, attempt 1
  • Event: pull_request for Enforce protected default branch updates #168; the pull request was Ready and remained on head 98960491ee5a4cc576370d895dfa27406c7ba4d7
  • Tested merge ref: 36e765615a5886df721eb39f0f8460e38c5400e5
  • OpenVMM source: 64054d7a7275c2c6d66c2bbb10f77ec777e11889
  • OpenVMM executable SHA-256: af7aded3301b4756ee11d91e062d60264e52ef038392dd04d5a44f9a9fc64853
  • Failed job/step: Platform / Windows / WHP / Virtual machine / Run benchmark
  • Runner: azure-windows-2, machine VMSSVAOAN000001
  • Labels: self-hosted, windows, whp, virtual-machine
  • Inputs: 1 vCPU guest, 128 MiB, host CPUs 0-7, host reserve 0, 1 warmup, 10 samples, 40-second marker timeout, guest-exit
  • Job interval: 2026-09-21 20:02:54Z - 20:05:25Z
  • Classification: current-head failure, not cancellation or pull-request supersession

The guard behaved as designed:

Workflow attempt Lifecycle measurement Snapshot p25 Snapshot p50 Result
1 1 778.553 ms 1145.855 ms Rejected: p50 47.2% above p25
1 2 758.313 ms 1207.128 ms Rejected: p50 59.2% above p25; job failed
2 1 757.718 ms 1004.276 ms Rejected: p50 32.5% above p25
2 2 747.362 ms 760.665 ms Accepted; workflow rerun succeeded

Workflow attempt 2 used the same azure-windows-2 / VMSSVAOAN000001 runner and the same source revisions. Its Windows/WHP job and the overall run completed successfully.

Snapshot-generation samples

Workflow attempt 1, lifecycle measurement 1:

769.860, 750.549, 785.176, 778.553, 1058.199,
1258.866, 1274.070, 1233.511, 1266.400, 1247.438

Workflow attempt 1, lifecycle measurement 2:

748.134, 1221.497, 1266.751, 1266.911, 1249.468,
1239.632, 1192.759, 733.743, 758.313, 764.249

Workflow attempt 2, lifecycle measurement 1:

757.719, 1233.202, 1235.240, 1252.613, 1240.896,
1292.089, 730.264, 770.037, 749.024, 775.352

Workflow attempt 2, lifecycle measurement 2:

773.696, 776.425, 764.066, 764.715, 751.555,
745.271, 767.387, 747.362, 757.264, 735.547

Profile localization

The rejected attempt-1 artifact contained both raw lifecycle profiles. The slow regime tracks capture.mapped_memory_flush almost one-for-one:

  • Fast flushes: approximately 0.67-0.72 seconds
  • Slow flushes: approximately 1.11-1.21 seconds
  • Remaining snapshot-generation time: generally approximately 0.06-0.08 seconds in both regimes

Cold boot and restore did not show the split:

  • Lifecycle measurement 1: cold-start p50 444.458 ms; restore p50 121.454 ms
  • Lifecycle measurement 2: cold-start p50 434.712 ms; restore p50 112.034 ms
  • Guest exits completed without teardown timeouts

This localizes the failure to host-side mapped-RAM flushing rather than guest boot, snapshot restore, or a general OpenVMM lifecycle failure.

The failed job uploaded artifact 10661007084, but the workflow rerun reused the run-scoped artifact name with overwrite: true. The failed artifact now returns 404 and has been replaced by artifact 10662446972, which contains the rerun's rejected and accepted lifecycle measurements. Job logs retain the failed sample values, but not the raw failed profiles. Workflow-attempt-scoped artifact retention remains an observability gap related to #119.

Recurrence

I scanned every CI run and every workflow attempt since the snapshot stability guard landed: 215 run IDs and 152 completed, assigned Windows/WHP platform jobs. Ten job attempts (6.6%) contained a validator-detected instability. One additional earlier series was missed by the original p50/p25-only detector and failed the historical performance gate instead.

Runner Completed WHP jobs Detected incidents Rate
azure-windows-1 19 1 5.3%
azure-windows-2 28 6 21.4%
azure-windows-3 71 3 4.2%
azure-windows-4 18 0 0%
azure-windows-5 14 0 0%
azure-windows-vmssvaoan000004 2 0 0%

The old-detector miss also ran on azure-windows-3, so the behavior is not exclusive to one runner. However, the observed rate on azure-windows-2 is materially higher and should be investigated.

Known occurrences

Run/job Runner Evidence and outcome
35180745983 attempt 1 azure-windows-3 p50 61.4% above p25; failed before bounded remeasurement existed. Workflow attempt 2 succeeded.
35382585623 azure-windows-2 p50 33.7% above p25; failed before bounded remeasurement existed. Led to #117.
35393916034 azure-windows-3 Two fast and eight slow samples; old validator missed the split and the performance gate failed. Led to #118.
35397304565 azure-windows-3 p50 34.3% above p25; internal remeasurement succeeded.
35404802205 azure-windows-2 p50 48.0% above p25; internal remeasurement succeeded.
35416685879 azure-windows-2 p50 37.1% above p25; internal remeasurement succeeded. The job later failed for unrelated release provenance.
35562857677 azure-windows-1 Both measurements rejected: 29.0% internal gap, then p50 60.4% above p25. Led to #159.
35605532024 azure-windows-3 55.9% internal gap; internal remeasurement succeeded.
35612178865 attempt 1 azure-windows-2 Both measurements rejected: p50 31.2%, then 61.2%, above p25. Workflow attempt 2 succeeded on azure-windows-4.
35648438246 attempt 1 azure-windows-2 Both measurements rejected: p50 47.2%, then 59.2%, above p25.
35648438246 attempt 2 azure-windows-2 First measurement rejected at 32.5% above p25; second measurement stable and accepted.

Of the validator-detected incidents:

  • Two predated bounded remeasurement and failed on the first unstable result.
  • Five recovered on the one allowed internal remeasurement. One of those jobs later failed for an unrelated reason.
  • Three produced two consecutive unstable measurements and directly failed the benchmark job.

Root-cause hypothesis

This is a recurring transient infrastructure condition in which mapped RAM flushes take approximately 0.4-0.5 seconds longer. Candidate triggers include managed-disk throttling, host writeback pressure, antivirus/scanning activity, or unrelated storage contention. The external trigger is not yet proven.

The evidence does not currently support an NVX or OpenVMM source regression:

  • It occurs across unrelated heads and multiple runners.
  • The same exact source and artifacts can fail and then pass without modification.
  • In the current run, the same runner moved between slow and fast regimes within minutes.
  • The slow samples are localized to mapped_memory_flush; other lifecycle phases remain stable.

The stability thresholds and single bounded remeasurement are working as intended. This issue should not be addressed by loosening the guard, adding unbounded retries, or publishing rejected data.

Related work

Investigation and mitigation

  • Use the telemetry planned in ci: improve Windows benchmark provenance and storage diagnostics #119 to correlate mapped_memory_flush with disk latency, queue depth, throughput, writeback, throttling, and scanning activity.
  • Inspect Azure host and managed-disk metrics for the timestamps above, especially azure-windows-2 / VMSSVAOAN000001.
  • Determine why azure-windows-2 shows a 21.4% detected incidence rate versus 0-5.3% on the other established runners.
  • Decide whether the affected instance should be drained/reimaged while diagnosis continues.
  • If storage configuration is causal, apply one consistent disk SKU, caching policy, and scratch-data placement across the Windows runner pool.
  • Preserve benchmark artifacts per workflow attempt so a manual rerun cannot delete the failed raw profiles.
  • Keep the current stability guard and one bounded remeasurement unchanged while collecting evidence.
  • Define and complete a post-mitigation observation window that covers every Windows runner and demonstrates that consecutive-instability job failures no longer recur.

Acceptance criteria

  • The external trigger for the slow mapped_memory_flush regime is identified with runner telemetry.
  • A runner-pool or host remediation is applied and documented.
  • Failed and successful workflow attempts retain enough provenance and raw profile data for comparison.
  • Rejected lifecycle series cannot enter performance history.
  • A representative post-fix observation window completes without two consecutive unstable lifecycle measurements.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

confirmedIssue affects multiple people.

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions