Skip to content

Commit

Permalink
PP-11205 Archive services
Browse files Browse the repository at this point in the history
- Archive services without any transactions in the last 7 years (or based on config number of days).
- Criteria for archiving service is
  - transaction exists (for any linked gateway account) and is older than 7 years
  -  OR no transactions exist and the created date is older than 7 years

- It is possible that the service doesn't have any transactions and created_date is null. These will be dealt with separately.
  • Loading branch information
kbottla committed Nov 6, 2023
1 parent 85eadbb commit 871bbd3
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@ public class ExpungeAndArchiveDataConfig {
@NotNull
private int expungeUserDataAfterDays;

@NotNull
private int archiveServicesAfterDays;

public boolean isExpungeAndArchiveHistoricalDataEnabled() {
return expungeAndArchiveHistoricalDataEnabled;
}

public int getExpungeUserDataAfterDays() {
return expungeUserDataAfterDays;
}

public int getArchiveServicesAfterDays() {
return archiveServicesAfterDays;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public class LedgerSearchTransactionsResponse {
@JsonProperty("results")
private List<LedgerTransaction> transactions;

public LedgerSearchTransactionsResponse(List<LedgerTransaction> transactions) {
this.transactions = transactions;
}

public List<LedgerTransaction> getTransactions() {
return transactions;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public LedgerTransaction() {
// empty constructor
}

public LedgerTransaction(String transactionId, String reference) {
public LedgerTransaction(String transactionId, String reference, ZonedDateTime createdDate) {
this.transactionId = transactionId;
this.reference = reference;
this.createdDate = createdDate;
}


public String getReference() {
return reference;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@
import org.slf4j.LoggerFactory;
import uk.gov.pay.adminusers.app.config.AdminUsersConfig;
import uk.gov.pay.adminusers.app.config.ExpungeAndArchiveDataConfig;
import uk.gov.pay.adminusers.client.ledger.model.LedgerSearchTransactionsResponse;
import uk.gov.pay.adminusers.client.ledger.service.LedgerService;
import uk.gov.pay.adminusers.persistence.dao.ForgottenPasswordDao;
import uk.gov.pay.adminusers.persistence.dao.InviteDao;
import uk.gov.pay.adminusers.persistence.dao.ServiceDao;
import uk.gov.pay.adminusers.persistence.dao.UserDao;
import uk.gov.pay.adminusers.persistence.entity.ServiceEntity;

import java.time.Clock;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

import static java.time.ZoneOffset.UTC;
import static java.time.temporal.ChronoUnit.DAYS;
Expand All @@ -23,6 +32,8 @@ public class ExpungeAndArchiveHistoricalDataService {
private final UserDao userDao;
private final InviteDao inviteDao;
private final ForgottenPasswordDao forgottenPasswordDao;
private final ServiceDao serviceDao;
private final LedgerService ledgerService;
private final ExpungeAndArchiveDataConfig expungeAndArchiveDataConfig;
private final Clock clock;

Expand All @@ -35,11 +46,15 @@ public class ExpungeAndArchiveHistoricalDataService {
@Inject
public ExpungeAndArchiveHistoricalDataService(UserDao userDao, InviteDao inviteDao,
ForgottenPasswordDao forgottenPasswordDao,
ServiceDao serviceDao,
LedgerService ledgerService,
AdminUsersConfig adminUsersConfig,
Clock clock) {
this.userDao = userDao;
this.inviteDao = inviteDao;
this.forgottenPasswordDao = forgottenPasswordDao;
this.serviceDao = serviceDao;
this.ledgerService = ledgerService;
expungeAndArchiveDataConfig = adminUsersConfig.getExpungeAndArchiveDataConfig();
this.clock = clock;
}
Expand All @@ -55,10 +70,14 @@ public void expungeAndArchiveHistoricalData() {
int noOfInvitesDeleted = inviteDao.deleteInvites(deleteUsersAndRelatedDataBeforeDate);
int noOfForgottenPasswordsDeleted = forgottenPasswordDao.deleteForgottenPasswords(deleteUsersAndRelatedDataBeforeDate);

int noOfServicesArchived = archiveServices();

LOGGER.info("Completed expunging and archiving historical data",
kv("no_of_users_deleted", noOfUsersDeleted),
kv("no_of_forgotten_passwords_deleted", noOfForgottenPasswordsDeleted),
kv("no_of_invites_deleted", noOfInvitesDeleted));
kv("no_of_invites_deleted", noOfInvitesDeleted),
kv("no_of_services_archived", noOfServicesArchived)
);
} else {
LOGGER.info("Expunging and archiving historical data is not enabled");
}
Expand All @@ -67,10 +86,64 @@ public void expungeAndArchiveHistoricalData() {
}
}

private int archiveServices() {
ZonedDateTime archiveServicesBeforeDate = getArchiveServicesBeforeDate();
List<ServiceEntity> servicesToCheckForArchiving = serviceDao.findServicesToCheckForArchiving(archiveServicesBeforeDate);

AtomicInteger numberOfServicesArchived = new AtomicInteger();
servicesToCheckForArchiving.forEach(serviceEntity -> {

if (canArchiveService(serviceEntity)) {
numberOfServicesArchived.getAndIncrement();
serviceEntity.setArchived(true);

serviceDao.merge(serviceEntity);
}
});

return numberOfServicesArchived.get();
}

private boolean canArchiveService(ServiceEntity serviceEntity) {
Optional<ZonedDateTime> mayBeLastTransactionDateForService = getLastTransactionDateForService(serviceEntity);
ZonedDateTime archiveServicesBeforeDate = getArchiveServicesBeforeDate();

if (mayBeLastTransactionDateForService.isPresent()) {
return mayBeLastTransactionDateForService.get().isBefore(archiveServicesBeforeDate);
} else if (serviceEntity.getCreatedDate() != null) {
return serviceEntity.getCreatedDate().isBefore(archiveServicesBeforeDate);
}

return false;
}

private Optional<ZonedDateTime> getLastTransactionDateForService(ServiceEntity serviceEntity) {
return serviceEntity.getGatewayAccountIds()
.stream()
.map(gatewayAccountIdEntity -> getLastTransactionDateForGatewayAccount(gatewayAccountIdEntity.getGatewayAccountId()))
.filter(Objects::nonNull)
.max(Comparator.comparing(ZonedDateTime::toEpochSecond));
}

private ZonedDateTime getLastTransactionDateForGatewayAccount(String gatewayAccountId) {
LedgerSearchTransactionsResponse searchTransactions = ledgerService.searchTransactions(gatewayAccountId, 1);

if (searchTransactions != null && !searchTransactions.getTransactions().isEmpty()) {
return searchTransactions.getTransactions().get(0).getCreatedDate();
}

return null;
}

private ZonedDateTime getArchiveServicesBeforeDate() {
return clock.instant()
.minus(expungeAndArchiveDataConfig.getArchiveServicesAfterDays(), DAYS)
.atZone(UTC);
}

private ZonedDateTime getDeleteUsersAndRelatedDataBeforeDate() {
ZonedDateTime expungeAndArchiveDateBeforeDate = clock.instant()
return clock.instant()
.minus(expungeAndArchiveDataConfig.getExpungeUserDataAfterDays(), DAYS)
.atZone(UTC);
return expungeAndArchiveDateBeforeDate;
}
}
1 change: 1 addition & 0 deletions src/main/resources/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,4 @@ ecsContainerMetadataUriV4: ${ECS_CONTAINER_METADATA_URI_V4:-}
expungeAndArchiveDataConfig:
expungeAndArchiveHistoricalDataEnabled: ${EXPUNGE_AND_ARCHIVE_HISTORICAL_DATA_ENABLED:-false}
expungeUserDataAfterDays: ${EXPUNGE_USER_DATA_AFTER_DAYS:-1460}
archiveServicesAfterDays: ${EXPUNGE_ARCHIVE_SERVICES_AFTER_DAYS:-2555}
Loading

0 comments on commit 871bbd3

Please sign in to comment.