Rework into new person deleting service

This commit is contained in:
Tadgh 2020-07-29 12:17:07 -07:00
parent 0e88f359f5
commit 102f936156
8 changed files with 128 additions and 96 deletions

View File

@ -67,7 +67,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@Service @Service
class ResourceExpungeService implements IResourceExpungeService { public class ResourceExpungeService implements IResourceExpungeService {
private static final Logger ourLog = LoggerFactory.getLogger(ResourceExpungeService.class); private static final Logger ourLog = LoggerFactory.getLogger(ResourceExpungeService.class);
@Autowired @Autowired

View File

@ -22,13 +22,13 @@ package ca.uhn.fhir.jpa.empi.config;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.IEmpiBatchService; import ca.uhn.fhir.empi.api.IEmpiBatchService;
import ca.uhn.fhir.empi.api.IEmpiResetSvc; import ca.uhn.fhir.empi.api.IEmpiChannelSubmitterSvc;
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc; import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
import ca.uhn.fhir.empi.api.IEmpiLinkSvc; import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc; import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc; import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
import ca.uhn.fhir.empi.api.IEmpiPersonMergerSvc; import ca.uhn.fhir.empi.api.IEmpiPersonMergerSvc;
import ca.uhn.fhir.empi.api.IEmpiChannelSubmitterSvc; import ca.uhn.fhir.empi.api.IEmpiResetSvc;
import ca.uhn.fhir.empi.api.IEmpiSettings; import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs; import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.provider.EmpiProviderLoader; import ca.uhn.fhir.empi.provider.EmpiProviderLoader;
@ -44,16 +44,18 @@ import ca.uhn.fhir.jpa.empi.dao.EmpiLinkFactory;
import ca.uhn.fhir.jpa.empi.interceptor.EmpiStorageInterceptor; import ca.uhn.fhir.jpa.empi.interceptor.EmpiStorageInterceptor;
import ca.uhn.fhir.jpa.empi.interceptor.IEmpiStorageInterceptor; import ca.uhn.fhir.jpa.empi.interceptor.IEmpiStorageInterceptor;
import ca.uhn.fhir.jpa.empi.svc.EmpiBatchSvcImpl; import ca.uhn.fhir.jpa.empi.svc.EmpiBatchSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiChannelSubmitterSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiEidUpdateService; import ca.uhn.fhir.jpa.empi.svc.EmpiEidUpdateService;
import ca.uhn.fhir.jpa.empi.svc.EmpiResetSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkQuerySvcImpl; import ca.uhn.fhir.jpa.empi.svc.EmpiLinkQuerySvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkSvcImpl; import ca.uhn.fhir.jpa.empi.svc.EmpiLinkSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkUpdaterSvcImpl; import ca.uhn.fhir.jpa.empi.svc.EmpiLinkUpdaterSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiMatchFinderSvcImpl; import ca.uhn.fhir.jpa.empi.svc.EmpiMatchFinderSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiMatchLinkSvc; import ca.uhn.fhir.jpa.empi.svc.EmpiMatchLinkSvc;
import ca.uhn.fhir.jpa.empi.svc.EmpiPersonDeletingSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiPersonMergerSvcImpl; import ca.uhn.fhir.jpa.empi.svc.EmpiPersonMergerSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiChannelSubmitterSvcImpl; import ca.uhn.fhir.jpa.empi.svc.EmpiResetSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiResourceDaoSvc; import ca.uhn.fhir.jpa.empi.svc.EmpiResourceDaoSvc;
import ca.uhn.fhir.jpa.empi.svc.IEmpiPersonDeletingSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchCriteriaBuilderSvc; import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchCriteriaBuilderSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchSvc; import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiPersonFindingSvc; import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiPersonFindingSvc;
@ -180,8 +182,13 @@ public class EmpiConsumerConfig {
} }
@Bean @Bean
IEmpiResetSvc empiResetSvc() { IEmpiResetSvc empiResetSvc(EmpiLinkDaoSvc theEmpiLinkDaoSvc, IEmpiPersonDeletingSvc theEmpiPersonDeletingSvcImpl ) {
return new EmpiResetSvcImpl(); return new EmpiResetSvcImpl(theEmpiLinkDaoSvc, theEmpiPersonDeletingSvcImpl);
}
@Bean
IEmpiPersonDeletingSvc empiPersonDeletingSvc() {
return new EmpiPersonDeletingSvcImpl();
} }
@Bean @Bean

View File

@ -0,0 +1,87 @@
package ca.uhn.fhir.jpa.empi.svc;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
import ca.uhn.fhir.jpa.dao.expunge.ExpungeService;
import ca.uhn.fhir.model.primitive.IdDt;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.List;
import static org.slf4j.LoggerFactory.getLogger;
@Service
public class EmpiPersonDeletingSvcImpl implements IEmpiPersonDeletingSvc {
private static final Logger ourLog = getLogger(EmpiPersonDeletingSvcImpl.class);
@Autowired
private DaoRegistry myDaoRegistry;
@Autowired
private ExpungeService myExpungeService;
@Autowired
private PlatformTransactionManager myPlatformTransactionManager;
/**
* Function which will delete all resources by their PIDs, and also delete any resources that were undeletable due to
* VersionConflictException
* @param theLongs
*/
@Override
public void deleteResourcesAndHandleConflicts(List<Long> theLongs) {
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.execute((tx) ->{
DeleteConflictList
deleteConflictList = new DeleteConflictList();
theLongs.stream().forEach(pid -> deleteCascade(pid, deleteConflictList));
IFhirResourceDao personDao = myDaoRegistry.getResourceDao("Person");
while (!deleteConflictList.isEmpty()) {
deleteConflictBatch(deleteConflictList, personDao);
}
return null;
});
}
/**
* Use the expunge service to expunge all historical and current versions of the resources associated to the PIDs.
*/
@Override
public void expungeHistoricalAndCurrentVersionsOfIds(List<Long> theLongs) {
ExpungeOptions options = new ExpungeOptions();
options.setExpungeDeletedResources(true);
options.setExpungeOldVersions(true);
//myResourceExpungeService.expungeHistoricalVersionsOfIds(null, theLongs, new AtomicInteger(Integer.MAX_VALUE - 1));
theLongs.stream()
.forEach(personId -> {
myExpungeService.expunge("Person", personId, null, options, null);
});
//myResourceExpungeService.expungeCurrentVersionOfResources(null, theLongs, new AtomicInteger(Integer.MAX_VALUE - 1));
}
private void deleteCascade(Long pid, DeleteConflictList theDeleteConflictList) {
ourLog.debug("About to cascade delete: " + pid);
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao("Person");
resourceDao.delete(new IdType("Person/" + pid), theDeleteConflictList, null, null);
}
private void deleteConflictBatch(DeleteConflictList theDcl, IFhirResourceDao<IBaseResource> theDao) {
DeleteConflictList newBatch = new DeleteConflictList();
for (DeleteConflict next: theDcl) {
IdDt nextSource = next.getSourceId();
ourLog.info("Have delete conflict {} - Cascading delete", next);
theDao.delete(nextSource.toVersionless(), newBatch, null, null);
}
theDcl.removeIf(x -> true);
theDcl.addAll(newBatch);
}
}

View File

@ -22,26 +22,14 @@ package ca.uhn.fhir.jpa.empi.svc;
import ca.uhn.fhir.empi.api.IEmpiResetSvc; import ca.uhn.fhir.empi.api.IEmpiResetSvc;
import ca.uhn.fhir.empi.util.EmpiUtil; import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.dao.expunge.IResourceExpungeService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc; import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.PostConstruct;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/** /**
* This class is in charge of Clearing out existing EMPI links, as well as deleting all persons related to those EMPI Links. * This class is in charge of Clearing out existing EMPI links, as well as deleting all persons related to those EMPI Links.
@ -50,96 +38,36 @@ import java.util.concurrent.atomic.AtomicInteger;
public class EmpiResetSvcImpl implements IEmpiResetSvc { public class EmpiResetSvcImpl implements IEmpiResetSvc {
private static final Logger ourLog = LoggerFactory.getLogger(EmpiResetSvcImpl.class); private static final Logger ourLog = LoggerFactory.getLogger(EmpiResetSvcImpl.class);
@Autowired final EmpiLinkDaoSvc myEmpiLinkDaoSvc;
EmpiLinkDaoSvc myEmpiLinkDaoSvc; final IEmpiPersonDeletingSvc myEmpiPersonDeletingSvcImpl;
@Autowired
private IResourceExpungeService myResourceExpungeService;
@Autowired
private DaoRegistry myDaoRegistry;
@Autowired
private PlatformTransactionManager myTransactionManager;
private TransactionTemplate myTxTemplate; @Autowired
public EmpiResetSvcImpl(EmpiLinkDaoSvc theEmpiLinkDaoSvc, IEmpiPersonDeletingSvc theEmpiPersonDeletingSvcImpl) {
myEmpiLinkDaoSvc = theEmpiLinkDaoSvc;
myEmpiPersonDeletingSvcImpl = theEmpiPersonDeletingSvcImpl;
}
@Override @Override
public long expungeAllEmpiLinksOfTargetType(String theResourceType) { public long expungeAllEmpiLinksOfTargetType(String theResourceType) {
throwExceptionIfInvalidTargetType(theResourceType); throwExceptionIfInvalidTargetType(theResourceType);
List<Long> longs = myEmpiLinkDaoSvc.deleteAllEmpiLinksOfTypeAndReturnPersonPids(theResourceType); List<Long> longs = myEmpiLinkDaoSvc.deleteAllEmpiLinksOfTypeAndReturnPersonPids(theResourceType);
deleteResourcesAndHandleConflicts(longs); myEmpiPersonDeletingSvcImpl.deleteResourcesAndHandleConflicts(longs);
expungeHistoricalAndCurrentVersiondsOfIds(longs); myEmpiPersonDeletingSvcImpl.expungeHistoricalAndCurrentVersionsOfIds(longs);
return longs.size(); return longs.size();
} }
/**
* Function which will delete all resources by their PIDs, and also delete any resources that were undeletable due to
* VersionConflictException
* @param theLongs
*/
private void deleteResourcesAndHandleConflicts(List<Long> theLongs) {
DeleteConflictList
deleteConflictList = new DeleteConflictList();
myTxTemplate.execute(tx -> {
theLongs.stream().forEach(pid -> deleteCascade(pid, deleteConflictList));
return null;
});
IFhirResourceDao personDao = myDaoRegistry.getResourceDao("Person");
while (!deleteConflictList.isEmpty()) {
myTxTemplate.execute(tx -> {
deleteConflictBatch(deleteConflictList, personDao);
return null;
});
}
}
private void throwExceptionIfInvalidTargetType(String theResourceType) { private void throwExceptionIfInvalidTargetType(String theResourceType) {
if (!EmpiUtil.supportedTargetType(theResourceType)) { if (!EmpiUtil.supportedTargetType(theResourceType)) {
throw new InvalidRequestException(ProviderConstants.EMPI_CLEAR + " does not support resource type: " + theResourceType); throw new InvalidRequestException(ProviderConstants.EMPI_CLEAR + " does not support resource type: " + theResourceType);
} }
} }
@PostConstruct
public void start() {
myTxTemplate = new TransactionTemplate(myTransactionManager);
}
/**
* TODO GGG this operation likely won't scale very well. Consider adding slicing
*/
@Override @Override
public long expungeAllEmpiLinks() { public long removeAllEmpiLinks() {
List<Long> longs = myEmpiLinkDaoSvc.deleteAllEmpiLinksAndReturnPersonPids(); List<Long> personPids = myEmpiLinkDaoSvc.deleteAllEmpiLinksAndReturnPersonPids();
deleteResourcesAndHandleConflicts(longs); myEmpiPersonDeletingSvcImpl.deleteResourcesAndHandleConflicts(personPids);
expungeHistoricalAndCurrentVersiondsOfIds(longs); myEmpiPersonDeletingSvcImpl.expungeHistoricalAndCurrentVersionsOfIds(personPids);
return longs.size(); return personPids.size();
}
/**
* Use the expunge service to expunge all historical and current versions of the resources associated to the PIDs.
*/
private void expungeHistoricalAndCurrentVersiondsOfIds(List<Long> theLongs) {
myResourceExpungeService.expungeHistoricalVersionsOfIds(null, theLongs, new AtomicInteger(Integer.MAX_VALUE - 1));
myResourceExpungeService.expungeCurrentVersionOfResources(null, theLongs, new AtomicInteger(Integer.MAX_VALUE - 1));
}
private void deleteConflictBatch(DeleteConflictList theDcl, IFhirResourceDao<IBaseResource> theDao) {
DeleteConflictList newBatch = new DeleteConflictList();
for (DeleteConflict next: theDcl) {
IdDt nextSource = next.getSourceId();
ourLog.info("Have delete conflict {} - Cascading delete", next);
theDao.delete(nextSource.toVersionless(), newBatch, null, null);
}
theDcl.removeIf(x -> true);
theDcl.addAll(newBatch);
}
private void deleteCascade(Long pid, DeleteConflictList theDeleteConflictList) {
ourLog.debug("About to cascade delete: " + pid);
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao("Person");
myTxTemplate.executeWithoutResult((tx) -> resourceDao.delete(new IdType("Person/" + pid), theDeleteConflictList, null, null));
} }
} }

View File

@ -0,0 +1,10 @@
package ca.uhn.fhir.jpa.empi.svc;
import java.util.List;
public interface IEmpiPersonDeletingSvc {
void deleteResourcesAndHandleConflicts(List<Long> theLongs);
void expungeHistoricalAndCurrentVersionsOfIds(List<Long> theLongs);
}

View File

@ -37,5 +37,5 @@ public interface IEmpiResetSvc {
* *
* @return the count of deleted EMPI links * @return the count of deleted EMPI links
*/ */
long expungeAllEmpiLinks(); long removeAllEmpiLinks();
} }

View File

@ -181,7 +181,7 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
public Parameters clearEmpiLinks(@OperationParam(name=ProviderConstants.EMPI_CLEAR_TARGET_TYPE, min = 0, max = 1) StringType theTargetType) { public Parameters clearEmpiLinks(@OperationParam(name=ProviderConstants.EMPI_CLEAR_TARGET_TYPE, min = 0, max = 1) StringType theTargetType) {
long resetCount; long resetCount;
if (theTargetType == null || StringUtils.isBlank(theTargetType.getValue())) { if (theTargetType == null || StringUtils.isBlank(theTargetType.getValue())) {
resetCount = myEmpiResetSvc.expungeAllEmpiLinks(); resetCount = myEmpiResetSvc.removeAllEmpiLinks();
} else { } else {
resetCount = myEmpiResetSvc.expungeAllEmpiLinksOfTargetType(theTargetType.getValueNotNull()); resetCount = myEmpiResetSvc.expungeAllEmpiLinksOfTargetType(theTargetType.getValueNotNull());
} }

View File

@ -134,7 +134,7 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
public Parameters clearEmpiLinks(@OperationParam(name=ProviderConstants.EMPI_CLEAR_TARGET_TYPE, min = 0, max = 1) StringType theTargetType) { public Parameters clearEmpiLinks(@OperationParam(name=ProviderConstants.EMPI_CLEAR_TARGET_TYPE, min = 0, max = 1) StringType theTargetType) {
long resetCount; long resetCount;
if (theTargetType == null || StringUtils.isBlank(theTargetType.getValue())) { if (theTargetType == null || StringUtils.isBlank(theTargetType.getValue())) {
resetCount = myEmpiExpungeSvc.expungeAllEmpiLinks(); resetCount = myEmpiExpungeSvc.removeAllEmpiLinks();
} else { } else {
resetCount = myEmpiExpungeSvc.expungeAllEmpiLinksOfTargetType(theTargetType.getValueNotNull()); resetCount = myEmpiExpungeSvc.expungeAllEmpiLinksOfTargetType(theTargetType.getValueNotNull());
} }