Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@
--;
-- Schema upgrade cleanup from 4.22.0.0 to 4.22.1.0
--;

-- Entries remaining on `cloud`.`resource_reservation` during the upgrade process are stale, so delete them.
-- This script was added to normalize volume/primary storage reservations that got stuck due to a bug on VM deployment,
-- but it is more interesting to introduce a smarter logic to clean these stale reservations in the future without the need
-- for upgrades (for instance, by having a heartbeat_time column for the reservations and automatically cleaning old entries).
DELETE FROM `cloud`.`resource_reservation`;
Comment on lines +22 to +26
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

The upgrade cleanup deletes all rows from cloud.resource_reservation, even though the described bug affects stale volume/primary_storage reservations. This broad delete could also wipe unrelated in-flight reservations (e.g., public IP/template/etc.) if an operator mistakenly runs the upgrade while other management servers are still operating. Consider restricting the DELETE to the affected resource types (and optionally to rows older than a conservative threshold) to reduce blast radius while still fixing the reported issue.

Suggested change
-- Entries remaining on `cloud`.`resource_reservation` during the upgrade process are stale, so delete them.
-- This script was added to normalize volume/primary storage reservations that got stuck due to a bug on VM deployment,
-- but it is more interesting to introduce a smarter logic to clean these stale reservations in the future without the need
-- for upgrades (for instance, by having a heartbeat_time column for the reservations and automatically cleaning old entries).
DELETE FROM `cloud`.`resource_reservation`;
-- Entries remaining on `cloud`.`resource_reservation` during the upgrade process for the affected resource types are stale, so delete them.
-- This script was added to normalize volume/primary storage reservations that got stuck due to a bug on VM deployment,
-- so scope the cleanup to those reservation types to avoid removing unrelated in-flight reservations.
-- It would still be more interesting to introduce a smarter logic to clean these stale reservations in the future without the need
-- for upgrades (for instance, by having a heartbeat_time column for the reservations and automatically cleaning old entries).
DELETE FROM `cloud`.`resource_reservation`
WHERE `resource_type` IN ('volume', 'primary_storage');

Copilot uses AI. Check for mistakes.
13 changes: 5 additions & 8 deletions server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -4301,22 +4301,20 @@ protected List<String> getResourceLimitStorageTags(long diskOfferingId) {
return resourceLimitService.getResourceLimitStorageTags(diskOfferingVO);
}

private List<CheckedReservation> reserveStorageResourcesForVm(Account owner, Long diskOfferingId, Long diskSize, List<VmDiskInfo> dataDiskInfoList, Long rootDiskOfferingId, ServiceOfferingVO offering, Long rootDiskSize) throws ResourceAllocationException {
List <CheckedReservation> checkedReservations = new ArrayList<>();

private void reserveStorageResourcesForVm(List<CheckedReservation> checkedReservations, Account owner, Long diskOfferingId, Long diskSize, List<VmDiskInfo> dataDiskInfoList, Long rootDiskOfferingId, ServiceOfferingVO offering, Long rootDiskSize) throws ResourceAllocationException {
List<String> rootResourceLimitStorageTags = getResourceLimitStorageTags(rootDiskOfferingId != null ? rootDiskOfferingId : offering.getDiskOfferingId());
CheckedReservation rootVolumeReservation = new CheckedReservation(owner, ResourceType.volume, rootResourceLimitStorageTags, 1L, reservationDao, resourceLimitService);
checkedReservations.add(rootVolumeReservation);
CheckedReservation rootPrimaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, rootResourceLimitStorageTags, rootDiskSize, reservationDao, resourceLimitService);
checkedReservations.add(rootPrimaryStorageReservation);

if (diskOfferingId != null) {
List<String> additionalResourceLimitStorageTags = diskOfferingId != null ? getResourceLimitStorageTags(diskOfferingId) : null;
List<String> additionalResourceLimitStorageTags = getResourceLimitStorageTags(diskOfferingId);
DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId);
Long size = verifyAndGetDiskSize(diskOffering, diskSize);
CheckedReservation additionalVolumeReservation = diskOfferingId != null ? new CheckedReservation(owner, ResourceType.volume, additionalResourceLimitStorageTags, 1L, reservationDao, resourceLimitService) : null;
CheckedReservation additionalVolumeReservation = new CheckedReservation(owner, ResourceType.volume, additionalResourceLimitStorageTags, 1L, reservationDao, resourceLimitService);
checkedReservations.add(additionalVolumeReservation);
CheckedReservation additionalPrimaryStorageReservation = diskOfferingId != null ? new CheckedReservation(owner, ResourceType.primary_storage, additionalResourceLimitStorageTags, size, reservationDao, resourceLimitService) : null;
CheckedReservation additionalPrimaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, additionalResourceLimitStorageTags, size, reservationDao, resourceLimitService);
checkedReservations.add(additionalPrimaryStorageReservation);

}
Expand All @@ -4332,7 +4330,6 @@ private List<CheckedReservation> reserveStorageResourcesForVm(Account owner, Lon
checkedReservations.add(additionalPrimaryStorageReservation);
}
}
return checkedReservations;
}

private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, String displayName, Account owner,
Expand All @@ -4347,7 +4344,7 @@ private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, Stri
List<CheckedReservation> checkedReservations = new ArrayList<>();

try {
checkedReservations = reserveStorageResourcesForVm(owner, diskOfferingId, diskSize, dataDiskInfoList, rootDiskOfferingId, offering, volumesSize);
reserveStorageResourcesForVm(checkedReservations, owner, diskOfferingId, diskSize, dataDiskInfoList, rootDiskOfferingId, offering, volumesSize);
Comment on lines 4344 to +4347
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

This change fixes a subtle leak path where partially-created CheckedReservations could be lost if an exception is thrown mid-reservation. There are existing UserVmManagerImplTest tests that use MockedConstruction<CheckedReservation>; adding a focused unit test that forces CheckedReservation construction to throw after the first/second reservation and then asserts that previously-created reservations are still closed (i.e., close() invoked) would help prevent regressions.

Copilot uses AI. Check for mistakes.

// verify security group ids
if (securityGroupIdList != null) {
Expand Down
Loading