Skip to content

Commit cc8dc84

Browse files
vishesh92shwstppr
andauthored
server: fix resource reservation leakage (#9169)
Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent be552fd commit cc8dc84

File tree

10 files changed

+190
-44
lines changed

10 files changed

+190
-44
lines changed

api/src/main/java/com/cloud/user/ResourceLimitService.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ public interface ResourceLimitService {
3838
static final ConfigKey<Long> MaxProjectSecondaryStorage = new ConfigKey<>("Project Defaults", Long.class, "max.project.secondary.storage", "400",
3939
"The default maximum secondary storage space (in GiB) that can be used for a project", false);
4040
static final ConfigKey<Long> ResourceCountCheckInterval = new ConfigKey<>("Advanced", Long.class, "resourcecount.check.interval", "300",
41-
"Time (in seconds) to wait before running resource recalculation and fixing task. Default is 300 seconds, Setting this to 0 disables execution of the task", true);
41+
"Time (in seconds) to wait before running resource recalculation and fixing tasks like stale resource reservation cleanup" +
42+
". Default is 300 seconds, Setting this to 0 disables execution of the task", true);
43+
static final ConfigKey<Long> ResourceReservationCleanupDelay = new ConfigKey<>("Advanced", Long.class, "resource.reservation.cleanup.delay", "3600",
44+
"Time (in seconds) after which a resource reservation gets deleted. Default is 3600 seconds, Setting this to 0 disables execution of the task", true);
4245
static final ConfigKey<String> ResourceLimitHostTags = new ConfigKey<>("Advanced", String.class, "resource.limit.host.tags", "",
4346
"A comma-separated list of tags for host resource limits", true);
4447
static final ConfigKey<String> ResourceLimitStorageTags = new ConfigKey<>("Advanced", String.class, "resource.limit.storage.tags", "",

api/src/main/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@
3434
import com.cloud.configuration.ResourceCount;
3535
import com.cloud.user.Account;
3636

37-
@APICommand(name = "updateResourceCount", description = "Recalculate and update resource count for an account or domain.", responseObject = ResourceCountResponse.class,
38-
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
37+
@APICommand(name = "updateResourceCount",
38+
description = "Recalculate and update resource count for an account or domain. " +
39+
"This also executes some cleanup tasks before calculating resource counts.",
40+
responseObject = ResourceCountResponse.class,
41+
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
3942
public class UpdateResourceCountCmd extends BaseCmd {
4043

4144

api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
import com.cloud.configuration.Resource;
2424

25+
import java.util.Date;
26+
2527
/**
2628
* an interface defining an {code}AutoClosable{code} reservation object
2729
*/
@@ -39,4 +41,6 @@
3941
String getTag();
4042

4143
Long getReservedAmount();
44+
45+
Date getCreated();
4246
}

engine/schema/src/main/java/org/apache/cloudstack/reservation/ReservationVO.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@
2525
import javax.persistence.Id;
2626
import javax.persistence.Table;
2727

28+
import com.cloud.utils.db.GenericDao;
2829
import org.apache.cloudstack.user.ResourceReservation;
2930

3031
import com.cloud.configuration.Resource;
3132
import com.cloud.utils.exception.CloudRuntimeException;
33+
import org.apache.cloudstack.utils.identity.ManagementServerNode;
34+
35+
import java.util.Date;
3236

3337
@Entity
3438
@Table(name = "resource_reservation")
@@ -57,6 +61,12 @@ public class ReservationVO implements ResourceReservation {
5761
@Column(name = "amount")
5862
long amount;
5963

64+
@Column(name = "mgmt_server_id")
65+
Long managementServerId;
66+
67+
@Column(name = GenericDao.CREATED_COLUMN)
68+
private Date created;
69+
6070
protected ReservationVO() {
6171
}
6272

@@ -69,6 +79,7 @@ public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resour
6979
this.resourceType = resourceType;
7080
this.tag = tag;
7181
this.amount = delta;
82+
this.managementServerId = ManagementServerNode.getManagementServerId();
7283
}
7384

7485
public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, Long delta) {
@@ -114,4 +125,16 @@ public void setResourceId(long resourceId) {
114125
this.resourceId = resourceId;
115126
}
116127

128+
@Override
129+
public Date getCreated() {
130+
return created;
131+
}
132+
133+
public void setCreated(Date created) {
134+
this.created = created;
135+
}
136+
137+
public Long getManagementServerId() {
138+
return managementServerId;
139+
}
117140
}

engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDao.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@
2323
import com.cloud.configuration.Resource;
2424
import com.cloud.utils.db.GenericDao;
2525

26+
import java.util.Date;
2627
import java.util.List;
2728

2829
public interface ReservationDao extends GenericDao<ReservationVO, Long> {
2930
long getAccountReservation(Long account, Resource.ResourceType resourceType, String tag);
3031
long getDomainReservation(Long domain, Resource.ResourceType resourceType, String tag);
3132
void setResourceId(Resource.ResourceType type, Long resourceId);
32-
List<Long> getResourceIds(long accountId, Resource.ResourceType type);
3333
List<ReservationVO> getReservationsForAccount(long accountId, Resource.ResourceType type, String tag);
3434
void removeByIds(List<Long> reservationIds);
35+
36+
int removeByMsId(long managementServerId);
37+
38+
int removeStaleReservations(Long accountId, Resource.ResourceType resourceType, String tag, Date createdBefore);
3539
}

engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDaoImpl.java

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
//
1919
package org.apache.cloudstack.reservation.dao;
2020

21+
import java.util.Date;
2122
import java.util.List;
22-
import java.util.stream.Collectors;
2323

2424
import org.apache.cloudstack.context.CallContext;
2525
import org.apache.cloudstack.reservation.ReservationVO;
@@ -42,6 +42,8 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
4242
private static final String ACCOUNT_ID = "accountId";
4343
private static final String DOMAIN_ID = "domainId";
4444
private static final String IDS = "ids";
45+
private static final String MS_ID = "managementServerId";
46+
private static final String CREATED = "created";
4547
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeSearch;
4648
private final SearchBuilder<ReservationVO> listAccountAndTypeSearch;
4749
private final SearchBuilder<ReservationVO> listAccountAndTypeAndNoTagSearch;
@@ -50,6 +52,7 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
5052
private final SearchBuilder<ReservationVO> listDomainAndTypeAndNoTagSearch;
5153
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeAndNoTagSearch;
5254
private final SearchBuilder<ReservationVO> listIdsSearch;
55+
private final SearchBuilder<ReservationVO> listMsIdSearch;
5356

5457
public ReservationDaoImpl() {
5558

@@ -71,12 +74,14 @@ public ReservationDaoImpl() {
7174
listAccountAndTypeSearch.and(ACCOUNT_ID, listAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
7275
listAccountAndTypeSearch.and(RESOURCE_TYPE, listAccountAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
7376
listAccountAndTypeSearch.and(RESOURCE_TAG, listAccountAndTypeSearch.entity().getTag(), SearchCriteria.Op.EQ);
77+
listAccountAndTypeSearch.and(CREATED, listAccountAndTypeSearch.entity().getCreated(), SearchCriteria.Op.LT);
7478
listAccountAndTypeSearch.done();
7579

7680
listAccountAndTypeAndNoTagSearch = createSearchBuilder();
7781
listAccountAndTypeAndNoTagSearch.and(ACCOUNT_ID, listAccountAndTypeAndNoTagSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
7882
listAccountAndTypeAndNoTagSearch.and(RESOURCE_TYPE, listAccountAndTypeAndNoTagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
7983
listAccountAndTypeAndNoTagSearch.and(RESOURCE_TAG, listAccountAndTypeAndNoTagSearch.entity().getTag(), SearchCriteria.Op.NULL);
84+
listAccountAndTypeAndNoTagSearch.and(CREATED, listAccountAndTypeAndNoTagSearch.entity().getCreated(), SearchCriteria.Op.LT);
8085
listAccountAndTypeAndNoTagSearch.done();
8186

8287
listDomainAndTypeSearch = createSearchBuilder();
@@ -94,18 +99,24 @@ public ReservationDaoImpl() {
9499
listIdsSearch = createSearchBuilder();
95100
listIdsSearch.and(IDS, listIdsSearch.entity().getId(), SearchCriteria.Op.IN);
96101
listIdsSearch.done();
102+
103+
listMsIdSearch = createSearchBuilder();
104+
listMsIdSearch.and(MS_ID, listMsIdSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ);
105+
listMsIdSearch.done();
97106
}
98107

99108
@Override
100109
public long getAccountReservation(Long accountId, Resource.ResourceType resourceType, String tag) {
101110
long total = 0;
102-
SearchCriteria<ReservationVO> sc = tag == null ?
103-
listAccountAndTypeAndNoTagSearch.create() : listAccountAndTypeSearch.create();
104-
sc.setParameters(ACCOUNT_ID, accountId);
105-
sc.setParameters(RESOURCE_TYPE, resourceType);
106-
if (tag != null) {
111+
SearchCriteria<ReservationVO> sc;
112+
if (tag == null) {
113+
sc = listAccountAndTypeAndNoTagSearch.create();
114+
} else {
115+
sc = listAccountAndTypeSearch.create();
107116
sc.setParameters(RESOURCE_TAG, tag);
108117
}
118+
sc.setParameters(ACCOUNT_ID, accountId);
119+
sc.setParameters(RESOURCE_TYPE, resourceType);
109120
List<ReservationVO> reservations = listBy(sc);
110121
for (ReservationVO reservation : reservations) {
111122
total += reservation.getReservedAmount();
@@ -116,13 +127,15 @@ public long getAccountReservation(Long accountId, Resource.ResourceType resource
116127
@Override
117128
public long getDomainReservation(Long domainId, Resource.ResourceType resourceType, String tag) {
118129
long total = 0;
119-
SearchCriteria<ReservationVO> sc = tag == null ?
120-
listDomainAndTypeAndNoTagSearch.create() : listDomainAndTypeSearch.create();
121-
sc.setParameters(DOMAIN_ID, domainId);
122-
sc.setParameters(RESOURCE_TYPE, resourceType);
123-
if (tag != null) {
130+
SearchCriteria<ReservationVO> sc;
131+
if (tag == null) {
132+
sc = listDomainAndTypeAndNoTagSearch.create();
133+
} else {
134+
sc = listDomainAndTypeSearch.create();
124135
sc.setParameters(RESOURCE_TAG, tag);
125136
}
137+
sc.setParameters(DOMAIN_ID, domainId);
138+
sc.setParameters(RESOURCE_TYPE, resourceType);
126139
List<ReservationVO> reservations = listBy(sc);
127140
for (ReservationVO reservation : reservations) {
128141
total += reservation.getReservedAmount();
@@ -149,23 +162,17 @@ public void setResourceId(Resource.ResourceType type, Long resourceId) {
149162
}
150163
}
151164

152-
@Override
153-
public List<Long> getResourceIds(long accountId, Resource.ResourceType type) {
154-
SearchCriteria<ReservationVO> sc = listResourceByAccountAndTypeSearch.create();
155-
sc.setParameters(ACCOUNT_ID, accountId);
156-
sc.setParameters(RESOURCE_TYPE, type);
157-
return listBy(sc).stream().map(ReservationVO::getResourceId).collect(Collectors.toList());
158-
}
159-
160165
@Override
161166
public List<ReservationVO> getReservationsForAccount(long accountId, Resource.ResourceType type, String tag) {
162-
SearchCriteria<ReservationVO> sc = tag == null ?
163-
listResourceByAccountAndTypeAndNoTagSearch.create() : listResourceByAccountAndTypeSearch.create();
164-
sc.setParameters(ACCOUNT_ID, accountId);
165-
sc.setParameters(RESOURCE_TYPE, type);
166-
if (tag != null) {
167+
SearchCriteria<ReservationVO> sc;
168+
if (tag == null) {
169+
sc = listResourceByAccountAndTypeAndNoTagSearch.create();
170+
} else {
171+
sc = listResourceByAccountAndTypeSearch.create();
167172
sc.setParameters(RESOURCE_TAG, tag);
168173
}
174+
sc.setParameters(ACCOUNT_ID, accountId);
175+
sc.setParameters(RESOURCE_TYPE, type);
169176
return listBy(sc);
170177
}
171178

@@ -177,4 +184,28 @@ public void removeByIds(List<Long> reservationIds) {
177184
remove(sc);
178185
}
179186
}
187+
188+
@Override
189+
public int removeByMsId(long managementServerId) {
190+
SearchCriteria<ReservationVO> sc = listMsIdSearch.create();
191+
sc.setParameters(MS_ID, managementServerId);
192+
return remove(sc);
193+
}
194+
195+
@Override
196+
public int removeStaleReservations(Long accountId, Resource.ResourceType resourceType, String tag,
197+
Date createdBefore) {
198+
SearchCriteria<ReservationVO> sc;
199+
if (tag == null) {
200+
sc = listAccountAndTypeAndNoTagSearch.create();
201+
} else {
202+
sc = listAccountAndTypeSearch.create();
203+
sc.setParameters(RESOURCE_TAG, tag);
204+
}
205+
sc.setParameters(ACCOUNT_ID, accountId);
206+
sc.setParameters(RESOURCE_TYPE, resourceType);
207+
sc.setParameters(CREATED, createdBefore);
208+
return remove(sc);
209+
}
210+
180211
}

engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ DROP INDEX `i_resource_count__type_domaintId`,
2929
ADD UNIQUE INDEX `i_resource_count__type_tag_accountId` (`type`,`tag`,`account_id`),
3030
ADD UNIQUE INDEX `i_resource_count__type_tag_domaintId` (`type`,`tag`,`domain_id`);
3131

32-
33-
ALTER TABLE `cloud`.`resource_reservation`
34-
ADD COLUMN `resource_id` bigint unsigned NULL;
35-
3632
ALTER TABLE `cloud`.`resource_reservation`
3733
MODIFY COLUMN `amount` bigint NOT NULL;
3834

35+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'resource_id', 'bigint unsigned NULL COMMENT "id of the resource" ');
36+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'mgmt_server_id', 'bigint unsigned NULL COMMENT "management server id" ');
37+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'created', 'datetime DEFAULT NULL COMMENT "date when the reservation was created" ');
38+
39+
UPDATE `cloud`.`resource_reservation` SET `created` = now() WHERE created IS NULL;
40+
3941

4042
-- Update Default System offering for Router to 512MiB
4143
UPDATE `cloud`.`service_offering` SET ram_size = 512 WHERE unique_name IN ("Cloud.Com-SoftwareRouter", "Cloud.Com-SoftwareRouter-Local",

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

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,28 @@ public static String getResourceReservationContextParameterKey(final ResourceTyp
6262
return String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName());
6363
}
6464

65+
private void removeAllReservations() {
66+
if (CollectionUtils.isEmpty(reservations)) {
67+
return;
68+
}
69+
CallContext.current().removeContextParameter(getContextParameterKey());
70+
for (ResourceReservation reservation : reservations) {
71+
reservationDao.remove(reservation.getId());
72+
}
73+
this.reservations = null;
74+
}
75+
6576
protected void checkLimitAndPersistReservations(Account account, ResourceType resourceType, Long resourceId, List<String> resourceLimitTags, Long amount) throws ResourceAllocationException {
66-
checkLimitAndPersistReservation(account, resourceType, resourceId, null, amount);
67-
if (CollectionUtils.isNotEmpty(resourceLimitTags)) {
68-
for (String tag : resourceLimitTags) {
69-
checkLimitAndPersistReservation(account, resourceType, resourceId, tag, amount);
77+
try {
78+
checkLimitAndPersistReservation(account, resourceType, resourceId, null, amount);
79+
if (CollectionUtils.isNotEmpty(resourceLimitTags)) {
80+
for (String tag : resourceLimitTags) {
81+
checkLimitAndPersistReservation(account, resourceType, resourceId, tag, amount);
82+
}
7083
}
84+
} catch (ResourceAllocationException rae) {
85+
removeAllReservations();
86+
throw rae;
7187
}
7288
}
7389

@@ -147,14 +163,7 @@ protected void setQuotaLimitLock(GlobalLock quotaLimitLock) {
147163

148164
@Override
149165
public void close() throws Exception {
150-
if (CollectionUtils.isEmpty(reservations)) {
151-
return;
152-
}
153-
CallContext.current().removeContextParameter(getContextParameterKey());
154-
for (ResourceReservation reservation : reservations) {
155-
reservationDao.remove(reservation.getId());
156-
}
157-
reservations = null;
166+
removeAllReservations();
158167
}
159168

160169
public Account getAccount() {

0 commit comments

Comments
 (0)