Recover from failed merge again. This time with feeling!

This commit is contained in:
Ken Stevens 2019-07-24 13:46:20 -04:00
parent 64e83fa3df
commit e05387da02
5 changed files with 36 additions and 45 deletions

View File

@ -1166,9 +1166,14 @@ public enum Pointcut {
/** /**
* Invoked before a resource is about to be expunged via the <code>$expunge</code> operation. * Invoked before a resource is about to be expunged via the <code>$expunge</code> operation.
* <p> * <p>
* Hooks will be passed a reference to a counter containing the current number of records that have been deleted.
* If the hook deletes any records, the hook is expected to increment this counter by the number of records deleted.
* </p>
* <p>
* Hooks may accept the following parameters: * Hooks may accept the following parameters:
* </p> * </p>
* <ul> * <ul>
* <li>java.util.concurrent.atomic.AtomicInteger - The counter holding the number of records deleted.</li>
* <li>org.hl7.fhir.instance.model.api.IIdType - The ID of the resource that is about to be deleted</li> * <li>org.hl7.fhir.instance.model.api.IIdType - The ID of the resource that is about to be deleted</li>
* <li>org.hl7.fhir.instance.model.api.IBaseResource - The resource that is about to be deleted</li> * <li>org.hl7.fhir.instance.model.api.IBaseResource - The resource that is about to be deleted</li>
* <li> * <li>
@ -1193,6 +1198,7 @@ public enum Pointcut {
// Return type // Return type
void.class, void.class,
// Params // Params
"java.util.concurrent.atomic.AtomicInteger",
"org.hl7.fhir.instance.model.api.IIdType", "org.hl7.fhir.instance.model.api.IIdType",
"org.hl7.fhir.instance.model.api.IBaseResource", "org.hl7.fhir.instance.model.api.IBaseResource",
"ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.api.server.RequestDetails",

View File

@ -32,6 +32,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Interceptor @Interceptor
@ -43,7 +44,7 @@ public class BinaryStorageInterceptor {
private FhirContext myCtx; private FhirContext myCtx;
@Hook(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE) @Hook(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE)
public void expungeResource(IBaseResource theResource) { public void expungeResource(AtomicInteger theCounter, IBaseResource theResource) {
Class<? extends IBase> binaryType = myCtx.getElementDefinition("base64Binary").getImplementingClass(); Class<? extends IBase> binaryType = myCtx.getElementDefinition("base64Binary").getImplementingClass();
List<? extends IBase> binaryElements = myCtx.newTerser().getAllPopulatedChildElementsOfType(theResource, binaryType); List<? extends IBase> binaryElements = myCtx.newTerser().getAllPopulatedChildElementsOfType(theResource, binaryType);
@ -57,6 +58,7 @@ public class BinaryStorageInterceptor {
for (String next : attachmentIds) { for (String next : attachmentIds) {
myBinaryStorageSvc.expungeBlob(theResource.getIdElement(), next); myBinaryStorageSvc.expungeBlob(theResource.getIdElement(), next);
theCounter.incrementAndGet();
} }
} }

View File

@ -73,8 +73,6 @@ public class ExpungeOperation implements Callable<ExpungeOutcome> {
public ExpungeOutcome call() { public ExpungeOutcome call() {
final IdDt id; final IdDt id;
callHooks();
if (expungeLimitReached()) { if (expungeLimitReached()) {
return expungeOutcome(); return expungeOutcome();
} }
@ -97,30 +95,6 @@ public class ExpungeOperation implements Callable<ExpungeOutcome> {
return expungeOutcome(); return expungeOutcome();
} }
private void callHooks() {
final AtomicInteger counter = new AtomicInteger();
if (myResourceId == null) {
return;
}
IdDt id;
if (myVersion == null) {
id = new IdDt(myResourceName, myResourceId);
} else {
id = new IdDt(myResourceName, myResourceId.toString(), myVersion.toString());
}
// Notify Interceptors about pre-action call
HookParams hooks = new HookParams()
.add(AtomicInteger.class, counter)
.add(IdDt.class, id)
.add(RequestDetails.class, myRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, myRequestDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequestDetails, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, hooks);
myRemainingCount.addAndGet(-1 * counter.get());
}
private void expungeDeletedResources() { private void expungeDeletedResources() {
Slice<Long> resourceIds = findHistoricalVersionsOfDeletedResources(); Slice<Long> resourceIds = findHistoricalVersionsOfDeletedResources();

View File

@ -139,27 +139,36 @@ class ResourceExpungeService implements IResourceExpungeService {
} }
} }
private void expungeHistoricalVersion(RequestDetails theRequestDetails, Long theNextVersionId) { private void expungeHistoricalVersion(RequestDetails theRequestDetails, Long theNextVersionId, AtomicInteger theRemainingCount) {
ResourceHistoryTable version = myResourceHistoryTableDao.findById(theNextVersionId).orElseThrow(IllegalArgumentException::new); ResourceHistoryTable version = myResourceHistoryTableDao.findById(theNextVersionId).orElseThrow(IllegalArgumentException::new);
IdDt id = version.getIdDt(); IdDt id = version.getIdDt();
ourLog.info("Deleting resource version {}", id.getValue()); ourLog.info("Deleting resource version {}", id.getValue());
// Interceptor call: STORAGE_PRESTORAGE_EXPUNGE_RESOURCE callHooks(theRequestDetails, theRemainingCount, version, id);
myResourceHistoryTagDao.deleteAll(version.getTags());
myResourceHistoryTableDao.delete(version);
theRemainingCount.decrementAndGet();
}
private void callHooks(RequestDetails theRequestDetails, AtomicInteger theRemainingCount, ResourceHistoryTable theVersion, IdDt theId) {
final AtomicInteger counter = new AtomicInteger();
if (JpaInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, myInterceptorBroadcaster, theRequestDetails)) { if (JpaInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, myInterceptorBroadcaster, theRequestDetails)) {
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(id.getResourceType()); IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType());
IBaseResource resource = resourceDao.toResource(version, false); IBaseResource resource = resourceDao.toResource(theVersion, false);
HookParams params = new HookParams() HookParams params = new HookParams()
.add(IIdType.class, id) .add(AtomicInteger.class, counter)
.add(IIdType.class, theId)
.add(IBaseResource.class, resource) .add(IBaseResource.class, resource)
.add(RequestDetails.class, theRequestDetails) .add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); .addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, params); JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, params);
} }
theRemainingCount.addAndGet(-1 * counter.get());
myResourceHistoryTagDao.deleteAll(version.getTags());
myResourceHistoryTableDao.delete(version);
} }
@Override @Override
@Transactional @Transactional
public void expungeHistoricalVersionsOfIds(RequestDetails theRequestDetails, List<Long> theResourceIds, AtomicInteger theRemainingCount) { public void expungeHistoricalVersionsOfIds(RequestDetails theRequestDetails, List<Long> theResourceIds, AtomicInteger theRemainingCount) {
@ -175,8 +184,8 @@ class ResourceExpungeService implements IResourceExpungeService {
@Transactional @Transactional
public void expungeHistoricalVersions(RequestDetails theRequestDetails, List<Long> theHistoricalIds, AtomicInteger theRemainingCount) { public void expungeHistoricalVersions(RequestDetails theRequestDetails, List<Long> theHistoricalIds, AtomicInteger theRemainingCount) {
for (Long next : theHistoricalIds) { for (Long next : theHistoricalIds) {
expungeHistoricalVersion(theRequestDetails, next); expungeHistoricalVersion(theRequestDetails, next, theRemainingCount);
if (theRemainingCount.decrementAndGet() <= 0) { if (theRemainingCount.get() <= 0) {
return; return;
} }
} }
@ -187,7 +196,7 @@ class ResourceExpungeService implements IResourceExpungeService {
ResourceHistoryTable currentVersion = myResourceHistoryTableDao.findForIdAndVersion(resource.getId(), resource.getVersion()); ResourceHistoryTable currentVersion = myResourceHistoryTableDao.findForIdAndVersion(resource.getId(), resource.getVersion());
if (currentVersion != null) { if (currentVersion != null) {
expungeHistoricalVersion(theRequestDetails, currentVersion.getId()); expungeHistoricalVersion(theRequestDetails, currentVersion.getId(), theRemainingCount);
} }
ourLog.info("Expunging current version of resource {}", resource.getIdDt().getValue()); ourLog.info("Expunging current version of resource {}", resource.getIdDt().getValue());
@ -203,8 +212,6 @@ class ResourceExpungeService implements IResourceExpungeService {
} }
myResourceTableDao.delete(resource); myResourceTableDao.delete(resource);
theRemainingCount.decrementAndGet();
} }
@Override @Override
@ -230,8 +237,8 @@ class ResourceExpungeService implements IResourceExpungeService {
Slice<Long> versionIds = myResourceHistoryTableDao.findForResourceId(page, resource.getId(), resource.getVersion()); Slice<Long> versionIds = myResourceHistoryTableDao.findForResourceId(page, resource.getId(), resource.getVersion());
ourLog.debug("Found {} versions of resource {} to expunge", versionIds.getNumberOfElements(), resource.getIdDt().getValue()); ourLog.debug("Found {} versions of resource {} to expunge", versionIds.getNumberOfElements(), resource.getIdDt().getValue());
for (Long nextVersionId : versionIds) { for (Long nextVersionId : versionIds) {
expungeHistoricalVersion(theRequestDetails, nextVersionId); expungeHistoricalVersion(theRequestDetails, nextVersionId, theRemainingCount);
if (theRemainingCount.decrementAndGet() <= 0) { if (theRemainingCount.get() <= 0) {
return; return;
} }
} }

View File

@ -82,14 +82,16 @@ public class ExpungeHookTest {
public void expungeResourceHook() throws InterruptedException { public void expungeResourceHook() throws InterruptedException {
IIdType expungeId = myPatientDao.create(new Patient()).getId(); IIdType expungeId = myPatientDao.create(new Patient()).getId();
assertNotNull(myPatientDao.read(expungeId)); assertNotNull(myPatientDao.read(expungeId));
myExpungeResourceLatch.setExpectedCount(1);
myPatientDao.delete(expungeId); myPatientDao.delete(expungeId);
ExpungeOptions options = new ExpungeOptions(); ExpungeOptions options = new ExpungeOptions();
options.setExpungeDeletedResources(true); options.setExpungeDeletedResources(true);
myExpungeService.expunge("Patient", expungeId.getIdPartAsLong(), expungeId.getVersionIdPartAsLong(), options, null);
myExpungeResourceLatch.setExpectedCount(2);
myExpungeService.expunge("Patient", expungeId.getIdPartAsLong(), null, options, null);
HookParams hookParams = myExpungeResourceLatch.awaitExpected().get(0); HookParams hookParams = myExpungeResourceLatch.awaitExpected().get(0);
IdDt hookId = hookParams.get(IdDt.class);
IIdType hookId = hookParams.get(IIdType.class);
assertEquals(expungeId.getValue(), hookId.getValue()); assertEquals(expungeId.getValue(), hookId.getValue());
} }
} }