Skip to content

Commit f52a619

Browse files
committed
Events listing and deletion optimizations
1 parent 34b8870 commit f52a619

File tree

14 files changed

+384
-221
lines changed

14 files changed

+384
-221
lines changed

api/src/main/java/org/apache/cloudstack/api/command/user/event/ArchiveEventsCmd.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.apache.cloudstack.context.CallContext;
3232

3333
import com.cloud.event.Event;
34-
import com.cloud.exception.InvalidParameterValueException;
3534
import com.cloud.user.Account;
3635

3736
@APICommand(name = "archiveEvents", description = "Archive one or more events.", responseObject = SuccessResponse.class, entityType = {Event.class},
@@ -82,6 +81,22 @@ public String getType() {
8281
return type;
8382
}
8483

84+
public void setIds(List<Long> ids) {
85+
this.ids = ids;
86+
}
87+
88+
public void setEndDate(Date endDate) {
89+
this.endDate = endDate;
90+
}
91+
92+
public void setStartDate(Date startDate) {
93+
this.startDate = startDate;
94+
}
95+
96+
public void setType(String type) {
97+
this.type = type;
98+
}
99+
85100
/////////////////////////////////////////////////////
86101
/////////////// API Implementation///////////////////
87102
/////////////////////////////////////////////////////
@@ -97,17 +112,12 @@ public long getEntityOwnerId() {
97112

98113
@Override
99114
public void execute() {
100-
if (ids == null && type == null && endDate == null) {
101-
throw new InvalidParameterValueException("either ids, type or enddate must be specified");
102-
} else if (startDate != null && endDate == null) {
103-
throw new InvalidParameterValueException("enddate must be specified with startdate parameter");
104-
}
105115
boolean result = _mgr.archiveEvents(this);
106116
if (result) {
107117
SuccessResponse response = new SuccessResponse(getCommandName());
108118
setResponseObject(response);
109-
} else {
110-
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to archive Events, one or more parameters has invalid values");
119+
return;
111120
}
121+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to archive Events. One or more parameters have invalid values.");
112122
}
113123
}

api/src/main/java/org/apache/cloudstack/api/command/user/event/DeleteEventsCmd.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.apache.cloudstack.context.CallContext;
3232

3333
import com.cloud.event.Event;
34-
import com.cloud.exception.InvalidParameterValueException;
3534
import com.cloud.user.Account;
3635

3736
@APICommand(name = "deleteEvents", description = "Delete one or more events.", responseObject = SuccessResponse.class, entityType = {Event.class},
@@ -82,6 +81,22 @@ public String getType() {
8281
return type;
8382
}
8483

84+
public void setIds(List<Long> ids) {
85+
this.ids = ids;
86+
}
87+
88+
public void setEndDate(Date endDate) {
89+
this.endDate = endDate;
90+
}
91+
92+
public void setStartDate(Date startDate) {
93+
this.startDate = startDate;
94+
}
95+
96+
public void setType(String type) {
97+
this.type = type;
98+
}
99+
85100
// ///////////////////////////////////////////////////
86101
// ///////////// API Implementation///////////////////
87102
// ///////////////////////////////////////////////////
@@ -97,17 +112,12 @@ public long getEntityOwnerId() {
97112

98113
@Override
99114
public void execute() {
100-
if (ids == null && type == null && endDate == null) {
101-
throw new InvalidParameterValueException("either ids, type or enddate must be specified");
102-
} else if (startDate != null && endDate == null) {
103-
throw new InvalidParameterValueException("enddate must be specified with startdate parameter");
104-
}
105115
boolean result = _mgr.deleteEvents(this);
106116
if (result) {
107117
SuccessResponse response = new SuccessResponse(getCommandName());
108118
setResponseObject(response);
109119
} else {
110-
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to delete Events, one or more parameters has invalid values");
120+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to delete any events. One or more parameters have invalid values.");
111121
}
112122
}
113123
}

api/src/main/java/org/apache/cloudstack/api/command/user/event/ListEventsCmd.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ public String getState() {
134134

135135
@Override
136136
public void execute() {
137-
138137
ListResponse<EventResponse> response = _queryService.searchForEvents(this);
139138
response.setResponseName(getCommandName());
140139
setResponseObject(response);

engine/schema/src/main/java/com/cloud/event/dao/EventDao.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,12 @@
2020
import java.util.List;
2121

2222
import com.cloud.event.EventVO;
23-
import com.cloud.utils.db.Filter;
2423
import com.cloud.utils.db.GenericDao;
25-
import com.cloud.utils.db.SearchCriteria;
2624

2725
public interface EventDao extends GenericDao<EventVO, Long> {
28-
public List<EventVO> searchAllEvents(SearchCriteria<EventVO> sc, Filter filter);
29-
30-
public List<EventVO> listOlderEvents(Date oldTime);
31-
32-
EventVO findCompletedEvent(long startId);
33-
34-
public List<EventVO> listToArchiveOrDeleteEvents(List<Long> ids, String type, Date startDate, Date endDate, List<Long> accountIds);
35-
36-
public void archiveEvents(List<EventVO> events);
26+
long archiveEvents(List<Long> ids, String type, Date startDate, Date endDate, Long accountId, List<Long> domainIds,
27+
long limitPerQuery);
3728

29+
long purgeAll(List<Long> ids, Date startDate, Date endDate, Date limitDate, String type, Long accountId, List<Long> domainIds,
30+
long limitPerQuery);
3831
}

engine/schema/src/main/java/com/cloud/event/dao/EventDaoImpl.java

Lines changed: 66 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,20 @@
1616
// under the License.
1717
package com.cloud.event.dao;
1818

19+
import java.sql.PreparedStatement;
20+
import java.sql.SQLException;
1921
import java.util.Date;
2022
import java.util.List;
23+
import java.util.stream.Collectors;
2124

2225

26+
import com.cloud.utils.db.DB;
27+
import com.cloud.utils.db.Filter;
28+
import com.cloud.utils.exception.CloudRuntimeException;
29+
import org.apache.commons.collections4.CollectionUtils;
2330
import org.springframework.stereotype.Component;
2431

25-
import com.cloud.event.Event.State;
2632
import com.cloud.event.EventVO;
27-
import com.cloud.utils.db.Filter;
2833
import com.cloud.utils.db.GenericDaoBase;
2934
import com.cloud.utils.db.SearchBuilder;
3035
import com.cloud.utils.db.SearchCriteria;
@@ -33,83 +38,90 @@
3338

3439
@Component
3540
public class EventDaoImpl extends GenericDaoBase<EventVO, Long> implements EventDao {
36-
protected final SearchBuilder<EventVO> CompletedEventSearch;
41+
3742
protected final SearchBuilder<EventVO> ToArchiveOrDeleteEventSearch;
3843

3944
public EventDaoImpl() {
40-
CompletedEventSearch = createSearchBuilder();
41-
CompletedEventSearch.and("state", CompletedEventSearch.entity().getState(), SearchCriteria.Op.EQ);
42-
CompletedEventSearch.and("startId", CompletedEventSearch.entity().getStartId(), SearchCriteria.Op.EQ);
43-
CompletedEventSearch.and("archived", CompletedEventSearch.entity().getArchived(), Op.EQ);
44-
CompletedEventSearch.done();
45-
4645
ToArchiveOrDeleteEventSearch = createSearchBuilder();
46+
ToArchiveOrDeleteEventSearch.select("id", SearchCriteria.Func.NATIVE, ToArchiveOrDeleteEventSearch.entity().getId());
4747
ToArchiveOrDeleteEventSearch.and("id", ToArchiveOrDeleteEventSearch.entity().getId(), Op.IN);
4848
ToArchiveOrDeleteEventSearch.and("type", ToArchiveOrDeleteEventSearch.entity().getType(), Op.EQ);
49-
ToArchiveOrDeleteEventSearch.and("accountIds", ToArchiveOrDeleteEventSearch.entity().getAccountId(), Op.IN);
49+
ToArchiveOrDeleteEventSearch.and("accountId", ToArchiveOrDeleteEventSearch.entity().getAccountId(), Op.EQ);
50+
ToArchiveOrDeleteEventSearch.and("domainIds", ToArchiveOrDeleteEventSearch.entity().getDomainId(), Op.IN);
5051
ToArchiveOrDeleteEventSearch.and("createdDateB", ToArchiveOrDeleteEventSearch.entity().getCreateDate(), Op.BETWEEN);
5152
ToArchiveOrDeleteEventSearch.and("createdDateL", ToArchiveOrDeleteEventSearch.entity().getCreateDate(), Op.LTEQ);
53+
ToArchiveOrDeleteEventSearch.and("createdDateLT", ToArchiveOrDeleteEventSearch.entity().getCreateDate(), Op.LT);
5254
ToArchiveOrDeleteEventSearch.and("archived", ToArchiveOrDeleteEventSearch.entity().getArchived(), Op.EQ);
5355
ToArchiveOrDeleteEventSearch.done();
5456
}
5557

56-
@Override
57-
public List<EventVO> searchAllEvents(SearchCriteria<EventVO> sc, Filter filter) {
58-
return listIncludingRemovedBy(sc, filter);
59-
}
60-
61-
@Override
62-
public List<EventVO> listOlderEvents(Date oldTime) {
63-
if (oldTime == null)
64-
return null;
65-
SearchCriteria<EventVO> sc = createSearchCriteria();
66-
sc.addAnd("createDate", SearchCriteria.Op.LT, oldTime);
67-
sc.addAnd("archived", SearchCriteria.Op.EQ, false);
68-
return listIncludingRemovedBy(sc, null);
69-
}
70-
71-
@Override
72-
public EventVO findCompletedEvent(long startId) {
73-
SearchCriteria<EventVO> sc = CompletedEventSearch.create();
74-
sc.setParameters("state", State.Completed);
75-
sc.setParameters("startId", startId);
76-
sc.setParameters("archived", false);
77-
return findOneIncludingRemovedBy(sc);
78-
}
79-
80-
@Override
81-
public List<EventVO> listToArchiveOrDeleteEvents(List<Long> ids, String type, Date startDate, Date endDate, List<Long> accountIds) {
58+
private SearchCriteria<EventVO> createEventSearchCriteria(List<Long> ids, String type, Date startDate, Date endDate,
59+
Date limitDate, Long accountId, List<Long> domainIds) {
8260
SearchCriteria<EventVO> sc = ToArchiveOrDeleteEventSearch.create();
83-
if (ids != null) {
84-
sc.setParameters("id", ids.toArray(new Object[ids.size()]));
61+
62+
if (CollectionUtils.isNotEmpty(ids)) {
63+
sc.setParameters("id", ids.toArray(new Object[0]));
8564
}
86-
if (type != null) {
87-
sc.setParameters("type", type);
65+
if (CollectionUtils.isNotEmpty(domainIds)) {
66+
sc.setParameters("domainIds", domainIds.toArray(new Object[0]));
8867
}
8968
if (startDate != null && endDate != null) {
9069
sc.setParameters("createdDateB", startDate, endDate);
9170
} else if (endDate != null) {
9271
sc.setParameters("createdDateL", endDate);
9372
}
94-
if (accountIds != null && !accountIds.isEmpty()) {
95-
sc.setParameters("accountIds", accountIds.toArray(new Object[accountIds.size()]));
96-
}
73+
sc.setParametersIfNotNull("accountId", accountId);
74+
sc.setParametersIfNotNull("createdDateLT", limitDate);
75+
sc.setParametersIfNotNull("type", type);
9776
sc.setParameters("archived", false);
98-
return search(sc, null);
77+
78+
return sc;
9979
}
10080

10181
@Override
102-
public void archiveEvents(List<EventVO> events) {
103-
if (events != null && !events.isEmpty()) {
104-
TransactionLegacy txn = TransactionLegacy.currentTxn();
105-
txn.start();
106-
for (EventVO event : events) {
107-
event = lockRow(event.getId(), true);
108-
event.setArchived(true);
109-
update(event.getId(), event);
110-
txn.commit();
82+
public long archiveEvents(List<Long> ids, String type, Date startDate, Date endDate, Long accountId, List<Long> domainIds,
83+
long limitPerQuery) {
84+
SearchCriteria<EventVO> sc = createEventSearchCriteria(ids, type, startDate, endDate, null, accountId, domainIds);
85+
Filter filter = null;
86+
if (limitPerQuery > 0) {
87+
filter = new Filter(limitPerQuery);
88+
}
89+
90+
long archived;
91+
long totalArchived = 0L;
92+
93+
do {
94+
List<EventVO> events = search(sc, filter);
95+
if (events.isEmpty()) {
96+
break;
11197
}
112-
txn.close();
98+
99+
archived = archiveEventsInternal(events);
100+
totalArchived += archived;
101+
} while (limitPerQuery > 0 && archived >= limitPerQuery);
102+
103+
return totalArchived;
104+
}
105+
106+
@DB
107+
private long archiveEventsInternal(List<EventVO> events) {
108+
final String idsAsString = events.stream()
109+
.map(e -> Long.toString(e.getId()))
110+
.collect(Collectors.joining(","));
111+
final String query = String.format("UPDATE event SET archived=true WHERE id IN (%s)", idsAsString);
112+
113+
try (TransactionLegacy txn = TransactionLegacy.currentTxn();
114+
PreparedStatement pstmt = txn.prepareStatement(query)) {
115+
return pstmt.executeUpdate();
116+
} catch (SQLException e) {
117+
throw new CloudRuntimeException(e);
113118
}
114119
}
120+
121+
@Override
122+
public long purgeAll(List<Long> ids, Date startDate, Date endDate, Date limitDate, String type, Long accountId,
123+
List<Long> domainIds, long limitPerQuery) {
124+
SearchCriteria<EventVO> sc = createEventSearchCriteria(ids, type, startDate, endDate, limitDate, accountId, domainIds);
125+
return batchExpunge(sc, limitPerQuery);
126+
}
115127
}

engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeUtils.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ public class DbUpgradeUtils {
2525

2626
public static void addIndexIfNeeded(Connection conn, String tableName, String... columnNames) {
2727
String indexName = dao.generateIndexName(tableName, columnNames);
28+
addIndexWithNameIfNeeded(conn, tableName, indexName, columnNames);
29+
}
2830

31+
public static void addIndexWithNameIfNeeded(Connection conn, String tableName, String indexName, String... columnNames) {
2932
if (!dao.indexExists(conn, tableName, indexName)) {
3033
dao.createIndex(conn, tableName, indexName, columnNames);
3134
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package com.cloud.upgrade.dao;
18+
19+
import com.cloud.utils.exception.CloudRuntimeException;
20+
21+
import java.io.InputStream;
22+
import java.sql.Connection;
23+
24+
public class Upgrade42010to42020 implements DbUpgrade {
25+
26+
@Override
27+
public String[] getUpgradableVersionRange() {
28+
return new String[]{"4.20.1.0", "4.20.2.0"};
29+
}
30+
31+
@Override
32+
public String getUpgradedVersion() {
33+
return "4.20.2.0";
34+
}
35+
36+
@Override
37+
public boolean supportsRollingUpgrade() {
38+
return false;
39+
}
40+
41+
@Override
42+
public InputStream[] getPrepareScripts() {
43+
final String scriptFile = "META-INF/db/schema-42010to42020.sql";
44+
final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
45+
if (script == null) {
46+
throw new CloudRuntimeException("Unable to find " + scriptFile);
47+
}
48+
49+
return new InputStream[]{script};
50+
}
51+
52+
@Override
53+
public void performDataMigration(Connection conn) {
54+
addIndexes(conn);
55+
}
56+
57+
private void addIndexes(Connection conn) {
58+
DbUpgradeUtils.addIndexWithNameIfNeeded(conn, "event", "i_event__multiple_columns_for_generic_search",
59+
"account_id", "domain_id", "archived", "display", "resource_type", "resource_id", "start_id", "type", "level",
60+
"created", "id");
61+
}
62+
63+
@Override
64+
public InputStream[] getCleanupScripts() {
65+
final String scriptFile = "META-INF/db/schema-42010to42020-cleanup.sql";
66+
final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
67+
if (script == null) {
68+
throw new CloudRuntimeException("Unable to find " + scriptFile);
69+
}
70+
71+
return new InputStream[]{script};
72+
}
73+
}

0 commit comments

Comments
 (0)