| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
Codecov Report❌ Patch coverage is 0% with 42 lines in your changes missing coverage. Please review.
@@ Coverage Diff @@
## 4.22 #14188 +/- ##
=========================================
Coverage 17.93% 17.94%
- Complexity 16142 16148 +6
=========================================
Files 5928 5928
Lines 535205 535247 +42
Branches 65501 65503 +2
=========================================
+ Hits 95989 96028 +39
- Misses 428286 428290 +4
+ Partials 10930 10929 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness.
|
Sorry, something went wrong.
getPhysicalDisk() asks libvirt for the volume. A volume that was just created by the management server is not in this host's libvirt pool cache, so the lookup misses and getVolume() falls back to refreshing the whole pool. Refreshing an RBD pool opens and stats every image in it, so the cost grows with the number of volumes in the pool and is paid on every VM start. On a pool holding 950 images that is 11.7 seconds added to each start, against 0.005 seconds to list the image names. Look RBD volumes up directly through librbd instead. Creating, cloning, resizing, copying and deleting RBD volumes in this class already use librbd directly; only the lookup went through libvirt. Because this runs on every VM start, the connection is handled more carefully than at the existing call sites: - the rados connection is shut down in a finally, so a client is not left for the finalizer. KVMStoragePoolManager.getPhysicalDisk retries a missing volume 100 times, so leaking here would mean 101 live librados clients for one absent volume. - the image is opened read only, since it is only stat'ed, and so cannot take the exclusive lock. - the image is closed through a helper that does not throw, so a failed close cannot discard a successful lookup or hide the original error. - the cephx key is only set when the pool has a user. librados aborts the process if it is handed a null value. Signed-off-by: Brad House <bhouse@nexthop.ai>
|
@weizhouapache a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
Sorry, something went wrong.
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 19299 |
Sorry, something went wrong.
|
@weizhouapache a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests |
Sorry, something went wrong.
|
[SF] Trillian test result (tid-17030)
|
Sorry, something went wrong.
|
@vladimirpetrov @sureshanaparti could you take a look at this one? We'd like this fix to make it into the upcoming 4.22.2 release. |
Sorry, something went wrong.
There was a problem hiding this comment.
code lgtm
Sorry, something went wrong.
|
run several tests on ceph storage pools, no issues found |
Sorry, something went wrong.
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Updates KVM RBD volume discovery to avoid libvirt storage pool refreshes during VM start by querying Ceph directly via librbd.
Changes:
| File | Description |
|---|---|
| plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java | Adds a librbd-backed RBD volume lookup to avoid expensive full pool refreshes on VM start. |
🧠 Review effort: Lite
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
Sorry, something went wrong.
apache#14188) getPhysicalDisk() asks libvirt for the volume. A volume that was just created by the management server is not in this host's libvirt pool cache, so the lookup misses and getVolume() falls back to refreshing the whole pool. Refreshing an RBD pool opens and stats every image in it, so the cost grows with the number of volumes in the pool and is paid on every VM start. On a pool holding 950 images that is 11.7 seconds added to each start, against 0.005 seconds to list the image names. Look RBD volumes up directly through librbd instead. Creating, cloning, resizing, copying and deleting RBD volumes in this class already use librbd directly; only the lookup went through libvirt. Because this runs on every VM start, the connection is handled more carefully than at the existing call sites: - the rados connection is shut down in a finally, so a client is not left for the finalizer. KVMStoragePoolManager.getPhysicalDisk retries a missing volume 100 times, so leaking here would mean 101 live librados clients for one absent volume. - the image is opened read only, since it is only stat'ed, and so cannot take the exclusive lock. - the image is closed through a helper that does not throw, so a failed close cannot discard a successful lookup or hide the original error. - the cephx key is only set when the pool has a user. librados aborts the process if it is handed a null value.
apache#14188) getPhysicalDisk() asks libvirt for the volume. A volume that was just created by the management server is not in this host's libvirt pool cache, so the lookup misses and getVolume() falls back to refreshing the whole pool. Refreshing an RBD pool opens and stats every image in it, so the cost grows with the number of volumes in the pool and is paid on every VM start. On a pool holding 950 images that is 11.7 seconds added to each start, against 0.005 seconds to list the image names. Look RBD volumes up directly through librbd instead. Creating, cloning, resizing, copying and deleting RBD volumes in this class already use librbd directly; only the lookup went through libvirt. Because this runs on every VM start, the connection is handled more carefully than at the existing call sites: - the rados connection is shut down in a finally, so a client is not left for the finalizer. KVMStoragePoolManager.getPhysicalDisk retries a missing volume 100 times, so leaking here would mean 101 live librados clients for one absent volume. - the image is opened read only, since it is only stat'ed, and so cannot take the exclusive lock. - the image is closed through a helper that does not throw, so a failed close cannot discard a successful lookup or hide the original error. - the cephx key is only set when the pool has a user. librados aborts the process if it is handed a null value.
Commit 26e7c3f (apache#13763, let libvirt remove RBD snapshots on volume delete) removed the com.ceph.rados.exceptions.ErrorCode import together with its last uses. Commit 08a706d (apache#14188, look RBD volumes up through librbd instead of refreshing the pool) brought the uses back without the import, so plugins/hypervisors/kvm failed to compile.
The cherry-pick of PR apache#14188 (look RBD volumes up through librbd instead of refreshing the pool) referenced com.ceph.rados.exceptions.ErrorCode without importing it, so plugins/hypervisors/kvm did not compile.
getRbdPhysicalDisk came in with apache#14188 after this change branched, so it still built its own Rados handle with only client_mount_timeout set. The lookup that runs on every deploy and VM start would then block forever during an OSD outage. Open through CephUtil.connect(), so it gets the same operation timeouts as every other Ceph handle in the plugin, and release the IO context and the cluster handle from the finally with the non-throwing helpers, so a failure to release cannot mask the exception on its way out. The cephx guard that this method carried is now inside CephUtil.connect() and covers every caller. Signed-off-by: Brad House <bhouse@nexthop.ai>
LibvirtResizeVolumeCommandWrapper gets the disk with pool.getPhysicalDisk() and then looks the volume up through libvirt with storageVolLookupByPath() to resize it. Before apache#14188, getPhysicalDisk() went through LibvirtStorageAdaptor.getVolume(), which refreshes the libvirt pool when the volume is not found, so the volume was in libvirt's cache by the time the wrapper looked it up. apache#14188 made getPhysicalDisk() stat RBD images through librbd, so nothing refreshes the pool any more. RBD volumes are created through librbd, not libvirt, so a new volume is not in libvirt's cache on a host until something refreshes the pool there, and resizing it fails with: Storage volume not found: no storage vol with matching path '<pool>/<uuid>' When the lookup by path fails with VIR_ERR_NO_STORAGE_VOL on an RBD pool, refresh the pool and look the volume up once more. That is what getVolume() did for this path before apache#14188, and the refresh only happens when the volume is not cached. Other errors and other pool types are handled as before. Signed-off-by: Brad House <bhouse@nexthop.ai> (cherry picked from commit cf319a0)
LibvirtResizeVolumeCommandWrapper gets the disk with pool.getPhysicalDisk() and then looks the volume up through libvirt with storageVolLookupByPath() to resize it. Before apache#14188, getPhysicalDisk() went through LibvirtStorageAdaptor.getVolume(), which refreshes the libvirt pool when the volume is not found, so the volume was in libvirt's cache by the time the wrapper looked it up. apache#14188 made getPhysicalDisk() stat RBD images through librbd, so nothing refreshes the pool any more. RBD volumes are created through librbd, not libvirt, so a new volume is not in libvirt's cache on a host until something refreshes the pool there, and resizing it fails with: Storage volume not found: no storage vol with matching path '<pool>/<uuid>' When the lookup by path fails with VIR_ERR_NO_STORAGE_VOL on an RBD pool, refresh the pool and look the volume up once more. That is what getVolume() did for this path before apache#14188, and the refresh only happens when the volume is not cached. Other errors and other pool types are handled as before. Signed-off-by: Brad House <bhouse@nexthop.ai> (cherry picked from commit cf319a0)
| Back | FazBrowse Home | New Git URL |
Description
Starting a VM whose root disk is on RBD primary storage refreshes the entire libvirt storage pool. The cost grows with the number of volumes in the Ceph pool, and it is paid on every single VM start.
Why it happens
LibvirtStorageAdaptor.getPhysicalDisk() asks libvirt for the volume. The volume was just created by the management server, so this host's libvirt has never seen it and the lookup misses. getVolume() then refreshes the whole pool:
The existing comment in getVolume() already describes the case: "This can happen when a volume has just been created on a different host and since then the libvirt storage pool has not been refreshed." For a cluster that creates VMs continuously, that is every start.
Measured cost
Ceph pool holding 950 RBD images, measured on the hypervisor:
Running four of these at once does not slow them down (9.9 - 11.1 s each), so this is not Ceph contention. It is 950 serial round trips.
Agent-side StartCommand on a host in a 12-node cluster running short-lived VMs:
The stall is 0 when the volume happens to be cached, which is what points at the refresh rather than at fixed work.
Fix
Look RBD volumes up directly through librbd and skip libvirt entirely.
This makes getPhysicalDisk() consistent with the rest of the class. Creating, cloning, resizing, copying and deleting RBD volumes in LibvirtStorageAdaptor already use Rados/Rbd directly. Only the lookup went through libvirt.
What this does not fix
getStorageStats still calls pool.refresh() explicitly, so the full walk of the pool still happens on the storage stats poll. That is a periodic background poll rather than something on the VM start path, so it is left alone here.
Connection handling
Because this runs on every VM start rather than occasionally, the rados connection is handled more carefully than at the existing call sites in this class:
Types of changes
How Has This Been Tested?
On the one non-obvious point, the librbd lookup reports rbd stat size as both size and virtual size, where the libvirt path reported allocation and capacity separately. These are the same number for RBD: libvirt derives allocation as obj_size * num_objs. Checked against libvirt 10.6.0 on three RBD volumes of different sizes:
Consumers of those two values (LibvirtGetVolumeStatsCommandWrapper, the template and volume sizes reported back by KVMStorageProcessor, and the snapshot pre-check in validateAvailableSizeOnPoolToTakeVolumeSnapshot) therefore see no change.