consoleproxy: fix race where the capacity scanner restarts a console proxy while it is being destroyed - #14175
Open
nagaboinaramgopal wants to merge 1 commit into
Conversation
destroyProxy stops the console proxy and then expunges it. The console proxy scanner takes proxies in Starting, Stopped, Migrating or Stopping state from its stopped pool and starts them, with no coordination with a destroy in progress. When a scan runs while a destroy has the proxy in Stopping or Stopped, the scanner starts it again and the destroy fails with "Unable to expunge the vm because it is not in the correct state", or the scanner recycles it with a second destroy of its own. Hold the existing console proxy allocation lock while the scanner assigns and starts a proxy, and while destroyProxy stops and expunges one. If destroyProxy cannot get the lock in time it logs a warning and destroys the proxy as before.
nagaboinaramgopal
marked this pull request as ready for review
September 15, 2026 18:11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When a console proxy is destroyed (
destroySystemVm, HA, or the scanner recycling a proxy it failed to start),destroyProxycalls expunge, which stops the VM and only then moves it to Expunging. The console proxy scanner (allocCapacity) takes a proxy in Starting, Stopped, Migrating or Stopping state from its stopped pool and starts it, and nothing ties that to a destroy in progress. If a scan runs while the destroy has the proxy in Stopping or Stopped, the scanner starts it again and the destroy fails with:In Trillian runs this shows up as
test_02_list_cpvm_vmandtest_04_cpvm_internalsintest_ssvm.pyfailing with "Check list response returns a valid list" (for example tid-16943 on #13090 and tid-16931 on #14038): the destroy in the first pass of the file loses the race, the file is rerun, and the rerun lists Running console proxies before the restarted one is back.The scanner already takes the console proxy allocation lock (
consoleproxy.alloc) when it creates a new proxy. This takes the same lock for the whole scan, so picking and starting a stopped proxy is covered too, and indestroyProxyaround the stop and expunge.GlobalLockis reentrant, so the scanner recycling a proxy it failed to start still works. IfdestroyProxycannot get the lock within the existing 180 second timeout it logs a warning and destroys the proxy as before.Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
N/A
How Has This Been Tested?
Unit tests in
ConsoleProxyManagerImplTest:destroyProxyholds the lock around the expunge, still destroys when the lock is busy, the scan is skipped while the lock is busy, and a stopped proxy is picked and started while the lock is held. Three of the four fail without the change (the busy lock destroy case keeps the old behaviour on purpose), and the class passes with it.Live on a KVM zone, with the changed classes swapped into the management server. The scanner skips a zone while a proxy is Starting or Stopping, so the window is between the proxy reaching Stopped and the destroy moving it to Expunging. On this zone that is about 30 ms (the Trillian logs above show about 2 seconds), so the race did not show up on its own: 0 of 15 destroys at the default 30 second scan interval, and 0 of 8 at a 1 second interval.
To make the window reproducible, a database trigger on the test zone delayed the completion of the stop job that belongs to a
destroySystemVmjob by 3 seconds, which keeps the proxy in Stopped for about 3 seconds before the destroy moves it to Expunging (measured 3.03 to 3.06 seconds on every destroy). Withconsoleproxy.capacityscan.intervalat 1000 ms and 5 destroys each:On this zone the scanner's start was queued behind the destroy's own stop job, so the destroy still completed and the queued start failed afterwards. On Trillian the stop job had already finished, the start ran first and the destroy failed as shown above.
How did you try to break it?
Destroyed the proxy repeatedly with a one second scan interval so every destroy overlaps several scans. The scanner path that recycles a proxy it could not start calls
destroyProxywhile it already holds the lock, which works because the lock is reentrant; the HA path also goes throughdestroyProxy. The "Unmatched Global lock consoleproxy.alloc reference usage detected" warning logged at management server shutdown was already there before this change.