-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Events listing, deletion and archival optimizations #11443
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f52a619
f4f2eac
568b329
e803bdb
ecf7fcb
d11afd7
7012147
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,15 +16,20 @@ | |||||||||||||||||||||||||||||||||||||||||||
| // under the License. | ||||||||||||||||||||||||||||||||||||||||||||
| package com.cloud.event.dao; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| import java.sql.PreparedStatement; | ||||||||||||||||||||||||||||||||||||||||||||
| import java.sql.SQLException; | ||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Date; | ||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||
| import java.util.stream.Collectors; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| import com.cloud.utils.db.DB; | ||||||||||||||||||||||||||||||||||||||||||||
| import com.cloud.utils.db.Filter; | ||||||||||||||||||||||||||||||||||||||||||||
| import com.cloud.utils.exception.CloudRuntimeException; | ||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.commons.collections4.CollectionUtils; | ||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Component; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| import com.cloud.event.Event.State; | ||||||||||||||||||||||||||||||||||||||||||||
| import com.cloud.event.EventVO; | ||||||||||||||||||||||||||||||||||||||||||||
| import com.cloud.utils.db.Filter; | ||||||||||||||||||||||||||||||||||||||||||||
| import com.cloud.utils.db.GenericDaoBase; | ||||||||||||||||||||||||||||||||||||||||||||
| import com.cloud.utils.db.SearchBuilder; | ||||||||||||||||||||||||||||||||||||||||||||
| import com.cloud.utils.db.SearchCriteria; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -33,83 +38,90 @@ | |||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| @Component | ||||||||||||||||||||||||||||||||||||||||||||
| public class EventDaoImpl extends GenericDaoBase<EventVO, Long> implements EventDao { | ||||||||||||||||||||||||||||||||||||||||||||
| protected final SearchBuilder<EventVO> CompletedEventSearch; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| protected final SearchBuilder<EventVO> ToArchiveOrDeleteEventSearch; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| public EventDaoImpl() { | ||||||||||||||||||||||||||||||||||||||||||||
| CompletedEventSearch = createSearchBuilder(); | ||||||||||||||||||||||||||||||||||||||||||||
| CompletedEventSearch.and("state", CompletedEventSearch.entity().getState(), SearchCriteria.Op.EQ); | ||||||||||||||||||||||||||||||||||||||||||||
| CompletedEventSearch.and("startId", CompletedEventSearch.entity().getStartId(), SearchCriteria.Op.EQ); | ||||||||||||||||||||||||||||||||||||||||||||
| CompletedEventSearch.and("archived", CompletedEventSearch.entity().getArchived(), Op.EQ); | ||||||||||||||||||||||||||||||||||||||||||||
| CompletedEventSearch.done(); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch = createSearchBuilder(); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.select("id", SearchCriteria.Func.NATIVE, ToArchiveOrDeleteEventSearch.entity().getId()); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.and("id", ToArchiveOrDeleteEventSearch.entity().getId(), Op.IN); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.and("type", ToArchiveOrDeleteEventSearch.entity().getType(), Op.EQ); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.and("accountIds", ToArchiveOrDeleteEventSearch.entity().getAccountId(), Op.IN); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.and("accountId", ToArchiveOrDeleteEventSearch.entity().getAccountId(), Op.EQ); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.and("domainIds", ToArchiveOrDeleteEventSearch.entity().getDomainId(), Op.IN); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.and("createdDateB", ToArchiveOrDeleteEventSearch.entity().getCreateDate(), Op.BETWEEN); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.and("createdDateL", ToArchiveOrDeleteEventSearch.entity().getCreateDate(), Op.LTEQ); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.and("createdDateLT", ToArchiveOrDeleteEventSearch.entity().getCreateDate(), Op.LT); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.and("archived", ToArchiveOrDeleteEventSearch.entity().getArchived(), Op.EQ); | ||||||||||||||||||||||||||||||||||||||||||||
| ToArchiveOrDeleteEventSearch.done(); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||
| public List<EventVO> searchAllEvents(SearchCriteria<EventVO> sc, Filter filter) { | ||||||||||||||||||||||||||||||||||||||||||||
| return listIncludingRemovedBy(sc, filter); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||
| public List<EventVO> listOlderEvents(Date oldTime) { | ||||||||||||||||||||||||||||||||||||||||||||
| if (oldTime == null) | ||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||
| SearchCriteria<EventVO> sc = createSearchCriteria(); | ||||||||||||||||||||||||||||||||||||||||||||
| sc.addAnd("createDate", SearchCriteria.Op.LT, oldTime); | ||||||||||||||||||||||||||||||||||||||||||||
| sc.addAnd("archived", SearchCriteria.Op.EQ, false); | ||||||||||||||||||||||||||||||||||||||||||||
| return listIncludingRemovedBy(sc, null); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||
| public EventVO findCompletedEvent(long startId) { | ||||||||||||||||||||||||||||||||||||||||||||
| SearchCriteria<EventVO> sc = CompletedEventSearch.create(); | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("state", State.Completed); | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("startId", startId); | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("archived", false); | ||||||||||||||||||||||||||||||||||||||||||||
| return findOneIncludingRemovedBy(sc); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||
| public List<EventVO> listToArchiveOrDeleteEvents(List<Long> ids, String type, Date startDate, Date endDate, List<Long> accountIds) { | ||||||||||||||||||||||||||||||||||||||||||||
| private SearchCriteria<EventVO> createEventSearchCriteria(List<Long> ids, String type, Date startDate, Date endDate, | ||||||||||||||||||||||||||||||||||||||||||||
| Date limitDate, Long accountId, List<Long> domainIds) { | ||||||||||||||||||||||||||||||||||||||||||||
| SearchCriteria<EventVO> sc = ToArchiveOrDeleteEventSearch.create(); | ||||||||||||||||||||||||||||||||||||||||||||
| if (ids != null) { | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("id", ids.toArray(new Object[ids.size()])); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if (CollectionUtils.isNotEmpty(ids)) { | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("id", ids.toArray(new Object[0])); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| if (type != null) { | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("type", type); | ||||||||||||||||||||||||||||||||||||||||||||
| if (CollectionUtils.isNotEmpty(domainIds)) { | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("domainIds", domainIds.toArray(new Object[0])); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| if (startDate != null && endDate != null) { | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("createdDateB", startDate, endDate); | ||||||||||||||||||||||||||||||||||||||||||||
| } else if (endDate != null) { | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("createdDateL", endDate); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| if (accountIds != null && !accountIds.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("accountIds", accountIds.toArray(new Object[accountIds.size()])); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParametersIfNotNull("accountId", accountId); | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParametersIfNotNull("createdDateLT", limitDate); | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParametersIfNotNull("type", type); | ||||||||||||||||||||||||||||||||||||||||||||
| sc.setParameters("archived", false); | ||||||||||||||||||||||||||||||||||||||||||||
| return search(sc, null); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| return sc; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||
| public void archiveEvents(List<EventVO> events) { | ||||||||||||||||||||||||||||||||||||||||||||
| if (events != null && !events.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||
| TransactionLegacy txn = TransactionLegacy.currentTxn(); | ||||||||||||||||||||||||||||||||||||||||||||
| txn.start(); | ||||||||||||||||||||||||||||||||||||||||||||
| for (EventVO event : events) { | ||||||||||||||||||||||||||||||||||||||||||||
| event = lockRow(event.getId(), true); | ||||||||||||||||||||||||||||||||||||||||||||
| event.setArchived(true); | ||||||||||||||||||||||||||||||||||||||||||||
| update(event.getId(), event); | ||||||||||||||||||||||||||||||||||||||||||||
| txn.commit(); | ||||||||||||||||||||||||||||||||||||||||||||
| public long archiveEvents(List<Long> ids, String type, Date startDate, Date endDate, Long accountId, List<Long> domainIds, | ||||||||||||||||||||||||||||||||||||||||||||
| long limitPerQuery) { | ||||||||||||||||||||||||||||||||||||||||||||
| SearchCriteria<EventVO> sc = createEventSearchCriteria(ids, type, startDate, endDate, null, accountId, domainIds); | ||||||||||||||||||||||||||||||||||||||||||||
| Filter filter = null; | ||||||||||||||||||||||||||||||||||||||||||||
| if (limitPerQuery > 0) { | ||||||||||||||||||||||||||||||||||||||||||||
| filter = new Filter(limitPerQuery); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| long archived; | ||||||||||||||||||||||||||||||||||||||||||||
| long totalArchived = 0L; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| do { | ||||||||||||||||||||||||||||||||||||||||||||
| List<EventVO> events = search(sc, filter); | ||||||||||||||||||||||||||||||||||||||||||||
| if (events.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| txn.close(); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| archived = archiveEventsInternal(events); | ||||||||||||||||||||||||||||||||||||||||||||
| totalArchived += archived; | ||||||||||||||||||||||||||||||||||||||||||||
| } while (limitPerQuery > 0 && archived >= limitPerQuery); | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
81
to
+101
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| return totalArchived; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| @DB | ||||||||||||||||||||||||||||||||||||||||||||
| private long archiveEventsInternal(List<EventVO> events) { | ||||||||||||||||||||||||||||||||||||||||||||
| final String idsAsString = events.stream() | ||||||||||||||||||||||||||||||||||||||||||||
| .map(e -> Long.toString(e.getId())) | ||||||||||||||||||||||||||||||||||||||||||||
| .collect(Collectors.joining(",")); | ||||||||||||||||||||||||||||||||||||||||||||
| final String query = String.format("UPDATE event SET archived=true WHERE id IN (%s)", idsAsString); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| try (TransactionLegacy txn = TransactionLegacy.currentTxn(); | ||||||||||||||||||||||||||||||||||||||||||||
| PreparedStatement pstmt = txn.prepareStatement(query)) { | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+108
to
+114
|
||||||||||||||||||||||||||||||||||||||||||||
| final String idsAsString = events.stream() | |
| .map(e -> Long.toString(e.getId())) | |
| .collect(Collectors.joining(",")); | |
| final String query = String.format("UPDATE event SET archived=true WHERE id IN (%s)", idsAsString); | |
| try (TransactionLegacy txn = TransactionLegacy.currentTxn(); | |
| PreparedStatement pstmt = txn.prepareStatement(query)) { | |
| if (CollectionUtils.isEmpty(events)) { | |
| return 0L; | |
| } | |
| final String placeholders = events.stream() | |
| .map(event -> "?") | |
| .collect(Collectors.joining(",")); | |
| final String query = String.format("UPDATE event SET archived=true WHERE id IN (%s)", placeholders); | |
| try (TransactionLegacy txn = TransactionLegacy.currentTxn(); | |
| PreparedStatement pstmt = txn.prepareStatement(query)) { | |
| for (int i = 0; i < events.size(); i++) { | |
| pstmt.setLong(i + 1, events.get(i).getId()); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This suggestion does not make sense to me
Copilot
AI
Apr 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New batching semantics in archiveEvents(...)/purgeAll(...) are non-trivial (limit handling, iteration/termination, criteria composition) but there are currently no unit tests covering these paths (unlike other DAO batch-expunge usages in engine/schema/src/test). Consider adding a focused DAO unit test validating that multiple batches are processed correctly and that limitPerQuery <= 0 behaves as intended.
Uh oh!
There was an error while loading. Please reload this page.