Skip to content

Commit 603fec5

Browse files
Submitting multiple dynamic VM Scaling API commands for the same instance can result in two usage events in the same second causing a compound key violation in usage service
Root cause: Even though dynamic scaling job is handled in vmworkjob queue which ensures serilizing multiple jobs but the database updating and generating usage events are out of the job queue. Solution: Moved all updations into the job queue
1 parent 2e3390f commit 603fec5

7 files changed

Lines changed: 88 additions & 106 deletions

File tree

engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ void advanceReboot(String vmUuid, Map<VirtualMachineProfile.Param, Object> param
152152
* @param serviceOfferingId
153153
* @return
154154
*/
155-
boolean upgradeVmDb(long vmId, long serviceOfferingId);
155+
boolean upgradeVmDb(long vmId, ServiceOffering newServiceOffering, ServiceOffering currentServiceOffering);
156156

157157
/**
158158
* @param vm
@@ -201,7 +201,7 @@ NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile request
201201
boolean replugNic(Network network, NicTO nic, VirtualMachineTO vm, ReservationContext context, DeployDestination dest) throws ConcurrentOperationException,
202202
ResourceUnavailableException, InsufficientCapacityException;
203203

204-
VirtualMachine reConfigureVm(String vmUuid, ServiceOffering newServiceOffering, boolean sameHost) throws ResourceUnavailableException, ConcurrentOperationException,
204+
VirtualMachine reConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, ServiceOffering newServiceOffering, Map<String, String> customParameters, boolean sameHost) throws ResourceUnavailableException, ConcurrentOperationException,
205205
InsufficientServerCapacityException;
206206

207207
void findHostAndMigrate(String vmUuid, Long newSvcOfferingId, DeploymentPlanner.ExcludeList excludeHostList) throws InsufficientCapacityException,

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
import com.cloud.agent.api.PrepareForMigrationAnswer;
4343
import com.cloud.agent.api.to.DpdkTO;
44+
import com.cloud.event.UsageEventVO;
4445
import com.cloud.offering.NetworkOffering;
4546
import com.cloud.offerings.dao.NetworkOfferingDetailsDao;
4647
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
@@ -232,6 +233,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
232233

233234
private static final String VM_SYNC_ALERT_SUBJECT = "VM state sync alert";
234235

236+
@Inject
237+
private UserVmManager _userVmMgr;
235238
@Inject
236239
private DataStoreManager dataStoreMgr;
237240
@Inject
@@ -3406,13 +3409,20 @@ public void checkIfCanUpgrade(final VirtualMachine vmInstance, final ServiceOffe
34063409
}
34073410

34083411
@Override
3409-
public boolean upgradeVmDb(final long vmId, final long serviceOfferingId) {
3410-
final VMInstanceVO vmForUpdate = _vmDao.createForUpdate();
3411-
vmForUpdate.setServiceOfferingId(serviceOfferingId);
3412-
final ServiceOffering newSvcOff = _entityMgr.findById(ServiceOffering.class, serviceOfferingId);
3412+
public boolean upgradeVmDb(final long vmId, final ServiceOffering newServiceOffering, ServiceOffering currentServiceOffering) {
3413+
3414+
final VMInstanceVO vmForUpdate = _vmDao.findById(vmId);
3415+
vmForUpdate.setServiceOfferingId(newServiceOffering.getId());
3416+
final ServiceOffering newSvcOff = _entityMgr.findById(ServiceOffering.class, newServiceOffering.getId());
34133417
vmForUpdate.setHaEnabled(newSvcOff.isOfferHA());
34143418
vmForUpdate.setLimitCpuUse(newSvcOff.getLimitCpuUse());
34153419
vmForUpdate.setServiceOfferingId(newSvcOff.getId());
3420+
if (newServiceOffering.isDynamic()) {
3421+
saveCustomOfferingDetails(vmId, newServiceOffering);
3422+
}
3423+
if (currentServiceOffering.isDynamic() && !newServiceOffering.isDynamic()) {
3424+
removeCustomOfferingDetails(vmId);
3425+
}
34163426
return _vmDao.update(vmId, vmForUpdate);
34173427
}
34183428

@@ -4087,8 +4097,8 @@ public boolean unplugNic(final Network network, final NicTO nic, final VirtualMa
40874097
}
40884098

40894099
@Override
4090-
public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering oldServiceOffering,
4091-
final boolean reconfiguringOnExistingHost)
4100+
public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering oldServiceOffering, final ServiceOffering newServiceOffering,
4101+
Map<String, String> customParameters, final boolean reconfiguringOnExistingHost)
40924102
throws ResourceUnavailableException, InsufficientServerCapacityException, ConcurrentOperationException {
40934103

40944104
final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
@@ -4098,14 +4108,14 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old
40984108
final VirtualMachine vm = _vmDao.findByUuid(vmUuid);
40994109
placeHolder = createPlaceHolderWork(vm.getId());
41004110
try {
4101-
return orchestrateReConfigureVm(vmUuid, oldServiceOffering, reconfiguringOnExistingHost);
4111+
return orchestrateReConfigureVm(vmUuid, oldServiceOffering, newServiceOffering, reconfiguringOnExistingHost);
41024112
} finally {
41034113
if (placeHolder != null) {
41044114
_workJobDao.expunge(placeHolder.getId());
41054115
}
41064116
}
41074117
} else {
4108-
final Outcome<VirtualMachine> outcome = reconfigureVmThroughJobQueue(vmUuid, oldServiceOffering, reconfiguringOnExistingHost);
4118+
final Outcome<VirtualMachine> outcome = reconfigureVmThroughJobQueue(vmUuid, oldServiceOffering, newServiceOffering, customParameters, reconfiguringOnExistingHost);
41094119

41104120
VirtualMachine vm = null;
41114121
try {
@@ -4134,14 +4144,12 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old
41344144
}
41354145
}
41364146

4137-
private VMInstanceVO orchestrateReConfigureVm(final String vmUuid, final ServiceOffering oldServiceOffering, final boolean reconfiguringOnExistingHost) throws ResourceUnavailableException,
4138-
ConcurrentOperationException {
4147+
private VMInstanceVO orchestrateReConfigureVm(final String vmUuid, final ServiceOffering oldServiceOffering, final ServiceOffering newServiceOffering,
4148+
final boolean reconfiguringOnExistingHost) throws ResourceUnavailableException, ConcurrentOperationException {
41394149
final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
4150+
upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
41404151

4141-
final long newServiceofferingId = vm.getServiceOfferingId();
4142-
final ServiceOffering newServiceOffering = _offeringDao.findById(vm.getId(), newServiceofferingId);
41434152
final HostVO hostVo = _hostDao.findById(vm.getHostId());
4144-
41454153
final Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(hostVo.getClusterId());
41464154
final Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(hostVo.getClusterId());
41474155
final long minMemory = (long)(newServiceOffering.getRamSize() / memoryOvercommitRatio);
@@ -4168,7 +4176,7 @@ private VMInstanceVO orchestrateReConfigureVm(final String vmUuid, final Service
41684176
if (reconfiguringOnExistingHost) {
41694177
vm.setServiceOfferingId(oldServiceOffering.getId());
41704178
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); //release the old capacity
4171-
vm.setServiceOfferingId(newServiceofferingId);
4179+
vm.setServiceOfferingId(newServiceOffering.getId());
41724180
_capacityMgr.allocateVmCapacity(vm, false); // lock the new capacity
41734181
}
41744182

@@ -4177,7 +4185,9 @@ private VMInstanceVO orchestrateReConfigureVm(final String vmUuid, final Service
41774185
s_logger.error("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
41784186
throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
41794187
}
4180-
4188+
if (vm.getType().equals(VirtualMachine.Type.User)) {
4189+
_userVmMgr.generateUsageEvent(vm, vm.isDisplayVm(), EventTypes.EVENT_VM_DYNAMIC_SCALE);
4190+
}
41814191
success = true;
41824192
} catch (final OperationTimedoutException e) {
41834193
throw new AgentUnavailableException("Operation timed out on reconfiguring " + vm, dstHostId);
@@ -4186,7 +4196,7 @@ private VMInstanceVO orchestrateReConfigureVm(final String vmUuid, final Service
41864196
} finally {
41874197
if (!success) {
41884198
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); // release the new capacity
4189-
vm.setServiceOfferingId(oldServiceOffering.getId());
4199+
upgradeVmDb(vm.getId(), oldServiceOffering, newServiceOffering); // rollback
41904200
_capacityMgr.allocateVmCapacity(vm, false); // allocate the old capacity
41914201
}
41924202
}
@@ -4195,6 +4205,33 @@ private VMInstanceVO orchestrateReConfigureVm(final String vmUuid, final Service
41954205

41964206
}
41974207

4208+
private void removeCustomOfferingDetails(long vmId) {
4209+
Map<String, String> details = userVmDetailsDao.listDetailsKeyPairs(vmId);
4210+
details.remove(UsageEventVO.DynamicParameters.cpuNumber.name());
4211+
details.remove(UsageEventVO.DynamicParameters.cpuSpeed.name());
4212+
details.remove(UsageEventVO.DynamicParameters.memory.name());
4213+
List<UserVmDetailVO> detailList = new ArrayList<UserVmDetailVO>();
4214+
for(Map.Entry<String, String> entry: details.entrySet()) {
4215+
UserVmDetailVO detailVO = new UserVmDetailVO(vmId, entry.getKey(), entry.getValue(), true);
4216+
detailList.add(detailVO);
4217+
}
4218+
userVmDetailsDao.saveDetails(detailList);
4219+
}
4220+
4221+
private void saveCustomOfferingDetails(long vmId, ServiceOffering serviceOffering) {
4222+
//save the custom values to the database.
4223+
Map<String, String> details = userVmDetailsDao.listDetailsKeyPairs(vmId);
4224+
details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString());
4225+
details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString());
4226+
details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString());
4227+
List<UserVmDetailVO> detailList = new ArrayList<UserVmDetailVO>();
4228+
for (Map.Entry<String, String> entry: details.entrySet()) {
4229+
UserVmDetailVO detailVO = new UserVmDetailVO(vmId, entry.getKey(), entry.getValue(), true);
4230+
detailList.add(detailVO);
4231+
}
4232+
userVmDetailsDao.saveDetails(detailList);
4233+
}
4234+
41984235
@Override
41994236
public String getConfigComponentName() {
42004237
return VirtualMachineManager.class.getSimpleName();
@@ -5090,7 +5127,7 @@ public Outcome<VirtualMachine> removeVmFromNetworkThroughJobQueue(
50905127
}
50915128

50925129
public Outcome<VirtualMachine> reconfigureVmThroughJobQueue(
5093-
final String vmUuid, final ServiceOffering newServiceOffering, final boolean reconfiguringOnExistingHost) {
5130+
final String vmUuid, final ServiceOffering oldServiceOffering, final ServiceOffering newServiceOffering, Map<String, String> customParameters, final boolean reconfiguringOnExistingHost) {
50945131

50955132
final CallContext context = CallContext.current();
50965133
final User user = context.getCallingUser();
@@ -5121,7 +5158,7 @@ public Outcome<VirtualMachine> reconfigureVmThroughJobQueue(
51215158

51225159
// save work context info (there are some duplications)
51235160
final VmWorkReconfigure workInfo = new VmWorkReconfigure(user.getId(), account.getId(), vm.getId(),
5124-
VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, newServiceOffering.getId(), reconfiguringOnExistingHost);
5161+
VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, oldServiceOffering.getId(), newServiceOffering.getId(), customParameters, reconfiguringOnExistingHost);
51255162
workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
51265163

51275164
_jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
@@ -5280,10 +5317,14 @@ private Pair<JobInfo.Status, String> orchestrateReconfigure(final VmWorkReconfig
52805317
s_logger.info("Unable to find vm " + work.getVmId());
52815318
}
52825319
assert vm != null;
5320+
ServiceOfferingVO oldServiceOffering = _offeringDao.findById(work.getOldServiceOfferingId());
5321+
ServiceOfferingVO newServiceOffering = _offeringDao.findById(work.getNewServiceOfferingId());
5322+
if (newServiceOffering.isDynamic()) {
5323+
// update the service offering object with the custom parameters like cpu, memory
5324+
newServiceOffering = _offeringDao.getcomputeOffering(newServiceOffering, work.getCustomParameters());
5325+
}
52835326

5284-
final ServiceOffering newServiceOffering = _offeringDao.findById(vm.getId(), work.getNewServiceOfferingId());
5285-
5286-
reConfigureVm(vm.getUuid(), newServiceOffering,
5327+
reConfigureVm(vm.getUuid(), oldServiceOffering, newServiceOffering, work.getCustomParameters(),
52875328
work.isSameHost());
52885329
return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, null);
52895330
}

engine/orchestration/src/main/java/com/cloud/vm/VmWorkReconfigure.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,40 @@
1717
package com.cloud.vm;
1818

1919

20+
import java.util.Map;
21+
2022
public class VmWorkReconfigure extends VmWork {
2123
private static final long serialVersionUID = -4517030323758086615L;
2224

25+
Long oldServiceOfferingId;
2326
Long newServiceOfferingId;
27+
28+
Map<String, String> customParameters;
2429
boolean sameHost;
2530

26-
public VmWorkReconfigure(long userId, long accountId, long vmId, String handlerName,
27-
Long newServiceOfferingId, boolean sameHost) {
31+
public VmWorkReconfigure(long userId, long accountId, long vmId, String handlerName, Long oldServiceOfferingId,
32+
Long newServiceOfferingId, Map<String, String> customParameters, boolean sameHost) {
2833

2934
super(userId, accountId, vmId, handlerName);
3035

36+
this.oldServiceOfferingId = oldServiceOfferingId;
3137
this.newServiceOfferingId = newServiceOfferingId;
38+
this.customParameters = customParameters;
3239
this.sameHost = sameHost;
3340
}
3441

42+
public Long getOldServiceOfferingId() {
43+
return oldServiceOfferingId;
44+
}
45+
3546
public Long getNewServiceOfferingId() {
3647
return newServiceOfferingId;
3748
}
3849

50+
public Map<String, String> getCustomParameters() {
51+
return customParameters;
52+
}
53+
3954
public boolean isSameHost() {
4055
return sameHost;
4156
}

server/src/main/java/com/cloud/server/ManagementServerImpl.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4086,15 +4086,7 @@ private VirtualMachine upgradeStoppedSystemVm(final Long systemVmId, final Long
40864086
}
40874087
_itMgr.checkIfCanUpgrade(systemVm, newServiceOffering);
40884088

4089-
final boolean result = _itMgr.upgradeVmDb(systemVmId, serviceOfferingId);
4090-
4091-
if (newServiceOffering.isDynamic()) {
4092-
//save the custom values to the database.
4093-
_userVmMgr.saveCustomOfferingDetails(systemVmId, newServiceOffering);
4094-
}
4095-
if (currentServiceOffering.isDynamic() && !newServiceOffering.isDynamic()) {
4096-
_userVmMgr.removeCustomOfferingDetails(systemVmId);
4097-
}
4089+
final boolean result = _itMgr.upgradeVmDb(systemVmId, newServiceOffering, currentServiceOffering);
40984090

40994091
if (result) {
41004092
return _vmInstanceDao.findById(systemVmId);

server/src/main/java/com/cloud/vm/UserVmManager.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import com.cloud.exception.ManagementServerException;
3333
import com.cloud.exception.ResourceUnavailableException;
3434
import com.cloud.exception.VirtualMachineMigrationException;
35-
import com.cloud.offering.ServiceOffering;
3635
import com.cloud.service.ServiceOfferingVO;
3736
import com.cloud.storage.Storage.StoragePoolType;
3837
import com.cloud.user.Account;
@@ -117,10 +116,6 @@ UserVm updateVirtualMachine(long id, String displayName, String group, Boolean h
117116
//find a common place for all the scaling and upgrading code of both user and systemvms.
118117
void validateCustomParameters(ServiceOfferingVO serviceOffering, Map<String, String> customParameters);
119118

120-
public void saveCustomOfferingDetails(long vmId, ServiceOffering serviceOffering);
121-
122-
public void removeCustomOfferingDetails(long vmId);
123-
124119
void generateUsageEvent(VirtualMachine vm, boolean isDisplay, String eventType);
125120

126121
void persistDeviceBusInfo(UserVmVO paramUserVmVO, String paramString);

0 commit comments

Comments
 (0)