Skip to content

Commit cee7a71

Browse files
authored
server: clear resource reservation and increment resource count in a transaction (#7724)
This PR addresses rare case of potential overlap of resource reservation and resource count. For different resource types there could be some delay between incrementing of the resource count and clearing of the earlier done reservation. This may result in failures when there are parallel deployments happening. Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent 56d98ea commit cee7a71

3 files changed

Lines changed: 41 additions & 15 deletions

File tree

api/src/main/java/org/apache/cloudstack/context/CallContext.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ public void putContextParameter(Object key, Object value) {
9292
context.put(key, value);
9393
}
9494

95+
public void removeContextParameter(Object key) {
96+
context.remove(key);
97+
}
98+
9599
/**
96100
* @param key any not null key object
97101
* @return the value of the key from context map
@@ -234,6 +238,7 @@ public static CallContext register(CallContext parent, ApiCommandResourceType ev
234238
CallContext callContext = register(parent.getCallingUserId(), parent.getCallingAccountId());
235239
callContext.setStartEventId(parent.getStartEventId());
236240
callContext.setEventResourceType(eventResourceType);
241+
callContext.putContextParameters(parent.getContextParameters());
237242
return callContext;
238243
}
239244

server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@
1818
//
1919
package com.cloud.resourcelimit;
2020

21+
import org.apache.cloudstack.context.CallContext;
22+
import org.apache.cloudstack.reservation.ReservationVO;
23+
import org.apache.cloudstack.reservation.dao.ReservationDao;
24+
import org.apache.cloudstack.user.ResourceReservation;
25+
import org.apache.log4j.Logger;
26+
import org.jetbrains.annotations.NotNull;
27+
2128
import com.cloud.configuration.Resource.ResourceType;
2229
import com.cloud.exception.ResourceAllocationException;
2330
import com.cloud.user.Account;
2431
import com.cloud.user.ResourceLimitService;
2532
import com.cloud.utils.db.GlobalLock;
26-
import org.apache.cloudstack.user.ResourceReservation;
2733
import com.cloud.utils.exception.CloudRuntimeException;
28-
import org.apache.cloudstack.reservation.ReservationVO;
29-
import org.apache.cloudstack.reservation.dao.ReservationDao;
30-
import org.apache.log4j.Logger;
31-
import org.jetbrains.annotations.NotNull;
3234

3335

3436
public class CheckedReservation implements AutoCloseable, ResourceReservation {
@@ -42,6 +44,10 @@ public class CheckedReservation implements AutoCloseable, ResourceReservation {
4244
private Long amount;
4345
private ResourceReservation reservation;
4446

47+
private String getContextParameterKey() {
48+
return String.format("%s-%s", ResourceReservation.class.getSimpleName(), resourceType.getName());
49+
}
50+
4551
/**
4652
* - check if adding a reservation is allowed
4753
* - create DB entry for reservation
@@ -70,6 +76,7 @@ public CheckedReservation(Account account, ResourceType resourceType, Long amoun
7076
resourceLimitService.checkResourceLimit(account,resourceType,amount);
7177
ReservationVO reservationVO = new ReservationVO(account.getAccountId(), account.getDomainId(), resourceType, amount);
7278
this.reservation = reservationDao.persist(reservationVO);
79+
CallContext.current().putContextParameter(getContextParameterKey(), reservationVO.getId());
7380
} catch (NullPointerException npe) {
7481
throw new CloudRuntimeException("not enough means to check limits", npe);
7582
} finally {
@@ -97,7 +104,8 @@ protected void setQuotaLimitLock(GlobalLock quotaLimitLock) {
97104

98105
@Override
99106
public void close() throws Exception {
100-
if (this.reservation != null){
107+
if (this.reservation != null) {
108+
CallContext.current().removeContextParameter(getContextParameterKey());
101109
reservationDao.remove(reservation.getId());
102110
reservation = null;
103111
}

server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// under the License.
1717
package com.cloud.resourcelimit;
1818

19+
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
20+
1921
import java.util.ArrayList;
2022
import java.util.Arrays;
2123
import java.util.EnumMap;
@@ -103,13 +105,11 @@
103105
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
104106
import com.cloud.utils.db.TransactionStatus;
105107
import com.cloud.utils.exception.CloudRuntimeException;
106-
import com.cloud.vm.VirtualMachineManager;
107108
import com.cloud.vm.VirtualMachine.State;
109+
import com.cloud.vm.VirtualMachineManager;
108110
import com.cloud.vm.dao.UserVmDao;
109111
import com.cloud.vm.dao.VMInstanceDao;
110112

111-
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
112-
113113
@Component
114114
public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLimitService, Configurable {
115115
public static final Logger s_logger = Logger.getLogger(ResourceLimitManagerImpl.class);
@@ -173,6 +173,23 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
173173
Map<ResourceType, Long> domainResourceLimitMap = new EnumMap<ResourceType, Long>(ResourceType.class);
174174
Map<ResourceType, Long> projectResourceLimitMap = new EnumMap<ResourceType, Long>(ResourceType.class);
175175

176+
protected void removeResourceReservationIfNeededAndIncrementResourceCount(final long accountId, final ResourceType type, final long numToIncrement) {
177+
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<CloudRuntimeException>() {
178+
@Override
179+
public void doInTransactionWithoutResult(TransactionStatus status) throws CloudRuntimeException {
180+
181+
Object obj = CallContext.current().getContextParameter(String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName()));
182+
if (obj instanceof Long) {
183+
reservationDao.remove((long)obj);
184+
}
185+
if (!updateResourceCountForAccount(accountId, type, true, numToIncrement)) {
186+
// we should fail the operation (resource creation) when failed to update the resource count
187+
throw new CloudRuntimeException("Failed to increment resource count of type " + type + " for account id=" + accountId);
188+
}
189+
}
190+
});
191+
}
192+
176193
@Override
177194
public boolean start() {
178195
if (_resourceCountCheckInterval > 0) {
@@ -270,12 +287,8 @@ public void incrementResourceCount(long accountId, ResourceType type, Long... de
270287
return;
271288
}
272289

273-
long numToIncrement = (delta.length == 0) ? 1 : delta[0].longValue();
274-
275-
if (!updateResourceCountForAccount(accountId, type, true, numToIncrement)) {
276-
// we should fail the operation (resource creation) when failed to update the resource count
277-
throw new CloudRuntimeException("Failed to increment resource count of type " + type + " for account id=" + accountId);
278-
}
290+
final long numToIncrement = (delta.length == 0) ? 1 : delta[0].longValue();
291+
removeResourceReservationIfNeededAndIncrementResourceCount(accountId, type, numToIncrement);
279292
}
280293

281294
@Override

0 commit comments

Comments
 (0)