Skip to content
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

Add troubleshooting log for partition routing. #6719

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2097,6 +2097,7 @@ public enum Pointcut implements IPointcut {
* This hook is an alternative to {@link #STORAGE_PARTITION_IDENTIFY_READ} and {@link #STORAGE_PARTITION_IDENTIFY_CREATE}
* and can be used in cases where a partition interceptor does not need knowledge of the specific resources being
* accessed/read/written in order to determine the appropriate partition.
* If registered, then neither STORAGE_PARTITION_IDENTIFY_READ, nor STORAGE_PARTITION_IDENTIFY_CREATE will be called.
* </p>
* <p>
* This hook will only be called if
Expand Down
52 changes: 35 additions & 17 deletions hapi-fhir-base/src/main/java/ca/uhn/fhir/util/Logs.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,16 @@
import org.slf4j.LoggerFactory;

public class Logs {
private static final Logger ourBatchTroubleshootingLog =
LoggerFactory.getLogger("ca.uhn.fhir.log.batch_troubleshooting");
private static final Logger ourNarrativeGenerationTroubleshootingLog =
LoggerFactory.getLogger("ca.uhn.fhir.log.narrative_generation_troubleshooting");

private static final Logger ourTerminologyTroubleshootingLog =
LoggerFactory.getLogger("ca.uhn.fhir.log.terminology_troubleshooting");

private static final Logger ourSubscriptionTroubleshootingLog =
LoggerFactory.getLogger("ca.cdr.log.subscription_troubleshooting");

private static final Logger ourSubscriptionTopicLog =
LoggerFactory.getLogger("ca.uhn.fhir.log.subscription_topic_troubleshooting");

public static Logger getBatchTroubleshootingLog() {
return ourBatchTroubleshootingLog;
}

public static Logger getNarrativeGenerationTroubleshootingLog() {
return ourBatchTroubleshootingLog;
return ourNarrativeGenerationTroubleshootingLog;
}

public static Logger getTerminologyTroubleshootingLog() {
return ourTerminologyTroubleshootingLog;
public static Logger getPartitionTroubleshootingLog() {
return ourPartitionTroubleshootingLog;
}

public static Logger getSubscriptionTroubleshootingLog() {
Expand All @@ -56,4 +42,36 @@ public static Logger getSubscriptionTroubleshootingLog() {
public static Logger getSubscriptionTopicLog() {
return ourSubscriptionTopicLog;
}

public static Logger getTerminologyTroubleshootingLog() {
return ourTerminologyTroubleshootingLog;
}

public static final String CA_UHN_FHIR_LOG_BATCH_TROUBLESHOOTING = "ca.uhn.fhir.log.batch_troubleshooting";
public static final String CA_UHN_FHIR_LOG_NARRATIVE_GENERATION_TROUBLESHOOTING =
"ca.uhn.fhir.log.narrative_generation_troubleshooting";
public static final String CA_UHN_FHIR_LOG_PARTITION_TROUBLESHOOTING = "ca.uhn.fhir.log.partition_troubleshooting";
public static final String CA_UHN_FHIR_LOG_TERMINOLOGY_TROUBLESHOOTING =
"ca.uhn.fhir.log.terminology_troubleshooting";
public static final String CA_CDR_LOG_SUBSCRIPTION_TROUBLESHOOTING = "ca.cdr.log.subscription_troubleshooting";
public static final String CA_UHN_FHIR_LOG_SUBSCRIPTION_TOPIC_TROUBLESHOOTING =
"ca.uhn.fhir.log.subscription_topic_troubleshooting";

private static final Logger ourBatchTroubleshootingLog =
LoggerFactory.getLogger(CA_UHN_FHIR_LOG_BATCH_TROUBLESHOOTING);

private static final Logger ourNarrativeGenerationTroubleshootingLog =
LoggerFactory.getLogger(CA_UHN_FHIR_LOG_NARRATIVE_GENERATION_TROUBLESHOOTING);

private static final Logger ourPartitionTroubleshootingLog =
LoggerFactory.getLogger(CA_UHN_FHIR_LOG_PARTITION_TROUBLESHOOTING);

private static final Logger ourTerminologyTroubleshootingLog =
LoggerFactory.getLogger(CA_UHN_FHIR_LOG_TERMINOLOGY_TROUBLESHOOTING);

private static final Logger ourSubscriptionTroubleshootingLog =
LoggerFactory.getLogger(CA_CDR_LOG_SUBSCRIPTION_TROUBLESHOOTING);

private static final Logger ourSubscriptionTopicLog =
LoggerFactory.getLogger(CA_UHN_FHIR_LOG_SUBSCRIPTION_TOPIC_TROUBLESHOOTING);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
type: add
issue: 6719
title: "Added a troubleshooting log for request partition routing."
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,7 @@
<logger name="org.hibernate.search.backend.lucene.infostream" level="TRACE"/> -->

<!-- <logger name="ca.uhn.fhir.log.terminology_troubleshooting" level="TRACE"/> -->
<logger name="ca.uhn.fhir.log.partition_troubleshooting" level="info"/>


</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.util.Logs;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
Expand All @@ -50,6 +52,7 @@

public abstract class BaseRequestPartitionHelperSvc implements IRequestPartitionHelperSvc {

public static final Logger ourLog = Logs.getPartitionTroubleshootingLog();
private final HashSet<Object> myNonPartitionableResourceNames;

@Autowired
Expand All @@ -61,7 +64,7 @@ public abstract class BaseRequestPartitionHelperSvc implements IRequestPartition
@Autowired
PartitionSettings myPartitionSettings;

public BaseRequestPartitionHelperSvc() {
protected BaseRequestPartitionHelperSvc() {
myNonPartitionableResourceNames = new HashSet<>();

// Infrastructure
Expand Down Expand Up @@ -117,33 +120,83 @@ public RequestPartitionId determineReadPartitionForRequest(
&& systemRequestHasExplicitPartition((SystemRequestDetails) requestDetails)
&& !nonPartitionableResource) {
requestPartitionId = getSystemRequestPartitionId((SystemRequestDetails) requestDetails, false);
logSystemRequestResolution(theRequest);

} else if ((requestDetails instanceof SystemRequestDetails) && nonPartitionableResource) {
requestPartitionId = RequestPartitionId.fromPartitionId(myPartitionSettings.getDefaultPartitionId());
logNonPartitionableType(resourceType);
} else {
// TODO mb: why is this path different than create?
// Here, a non-partitionable resource is still delivered to the pointcuts.
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, requestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
HookParams params = new HookParams()
.add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails);
requestPartitionId = (RequestPartitionId)
compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params);
requestPartitionId = callAnyPointcut(compositeBroadcaster, requestDetails);
} else if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_READ)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_READ
HookParams params = new HookParams()
.add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails)
.add(ReadPartitionIdRequestDetails.class, theDetails);
requestPartitionId = (RequestPartitionId)
compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_READ, params);
requestPartitionId = callReadPointcut(compositeBroadcaster, requestDetails, theDetails);
}
}

validateRequestPartitionNotNull(
requestPartitionId, Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, Pointcut.STORAGE_PARTITION_IDENTIFY_READ);

return validateAndNormalizePartition(requestPartitionId, requestDetails, resourceType);
RequestPartitionId resultRequestPartitionId =
validateAndNormalizePartition(requestPartitionId, requestDetails, resourceType);
logTroubleshootingResult("read", resourceType, theRequest, resultRequestPartitionId);

return resultRequestPartitionId;
}

private static RequestPartitionId callAnyPointcut(
IInterceptorBroadcaster compositeBroadcaster, RequestDetails requestDetails) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
HookParams params = new HookParams()
.add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails);

return callAndLog(compositeBroadcaster, Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params);
}

private static RequestPartitionId callCreatePointcut(
IInterceptorBroadcaster compositeBroadcaster,
RequestDetails requestDetails,
@Nonnull IBaseResource theResource) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_CREATE
HookParams params = new HookParams()
.add(IBaseResource.class, theResource)
.add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails);

return callAndLog(compositeBroadcaster, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params);
}

private static RequestPartitionId callAndLog(
IInterceptorBroadcaster compositeBroadcaster, Pointcut pointcut, HookParams params) {
RequestPartitionId result =
(RequestPartitionId) compositeBroadcaster.callHooksAndReturnObject(pointcut, params);

if (ourLog.isTraceEnabled()) {
ourLog.trace(
"{}: result={} hooks={}", pointcut, result, compositeBroadcaster.getInvokersForPointcut(pointcut));
}
return result;
}

private static RequestPartitionId callReadPointcut(
IInterceptorBroadcaster compositeBroadcaster,
RequestDetails requestDetails,
@Nonnull ReadPartitionIdRequestDetails theDetails) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_READ
HookParams params = new HookParams()
.add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails)
.add(ReadPartitionIdRequestDetails.class, theDetails);

return callAndLog(compositeBroadcaster, Pointcut.STORAGE_PARTITION_IDENTIFY_READ, params);
}

private static void logNonPartitionableType(String theResourceType) {
ourLog.trace("Partitioning: resource type {} must be on the DEFAULT partition.", theResourceType);
}

@Override
Expand All @@ -157,16 +210,12 @@ public RequestPartitionId determineGenericPartitionForRequest(RequestDetails the
if (theRequestDetails instanceof SystemRequestDetails
&& systemRequestHasExplicitPartition((SystemRequestDetails) theRequestDetails)) {
requestPartitionId = getSystemRequestPartitionId((SystemRequestDetails) theRequestDetails);
logSystemRequestResolution(theRequestDetails);
} else {
IInterceptorBroadcaster compositeBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(
myInterceptorBroadcaster, theRequestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
HookParams params = new HookParams()
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
requestPartitionId = (RequestPartitionId)
compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params);
requestPartitionId = callAnyPointcut(compositeBroadcaster, theRequestDetails);
}
}

Expand All @@ -175,10 +224,13 @@ && systemRequestHasExplicitPartition((SystemRequestDetails) theRequestDetails))
// validateRequestPartitionNotNull(requestPartitionId, Pointcut.STORAGE_PARTITION_IDENTIFY_ANY);

if (requestPartitionId != null) {
return validateAndNormalizePartition(
requestPartitionId = validateAndNormalizePartition(
requestPartitionId, theRequestDetails, theRequestDetails.getResourceName());
}
return null;

logTroubleshootingResult("generic", theRequestDetails.getResourceName(), theRequestDetails, requestPartitionId);

return requestPartitionId;
}

/**
Expand Down Expand Up @@ -247,30 +299,22 @@ public RequestPartitionId determineCreatePartitionForRequest(
&& systemRequestHasExplicitPartition((SystemRequestDetails) theRequest)) {
requestPartitionId =
getSystemRequestPartitionId((SystemRequestDetails) theRequest, nonPartitionableResource);

logSystemRequestResolution(theRequest);
} else {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, requestDetails);
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_ANY
HookParams params = new HookParams()
.add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails);
requestPartitionId = (RequestPartitionId)
compositeBroadcaster.callHooksAndReturnObject(Pointcut.STORAGE_PARTITION_IDENTIFY_ANY, params);
requestPartitionId = callAnyPointcut(compositeBroadcaster, requestDetails);
} else if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE)) {
// Interceptor call: STORAGE_PARTITION_IDENTIFY_CREATE
HookParams params = new HookParams()
.add(IBaseResource.class, theResource)
.add(RequestDetails.class, requestDetails)
.addIfMatchesType(ServletRequestDetails.class, requestDetails);
requestPartitionId = (RequestPartitionId) compositeBroadcaster.callHooksAndReturnObject(
Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params);
requestPartitionId = callCreatePointcut(compositeBroadcaster, requestDetails, theResource);
}
}

// If the interceptors haven't selected a partition, and its a non-partitionable resource anyhow, send
// to DEFAULT
if (nonPartitionableResource && requestPartitionId == null) {
logNonPartitionableType(theResourceType);
requestPartitionId = RequestPartitionId.defaultPartition();
}

Expand All @@ -280,7 +324,12 @@ && systemRequestHasExplicitPartition((SystemRequestDetails) theRequest)) {
Pointcut.STORAGE_PARTITION_IDENTIFY_ANY);
validatePartitionForCreate(requestPartitionId, theResourceType);

return validateAndNormalizePartition(requestPartitionId, requestDetails, theResourceType);
RequestPartitionId resultRequestPartitionId =
validateAndNormalizePartition(requestPartitionId, requestDetails, theResourceType);

logTroubleshootingResult("create", theResourceType, theRequest, resultRequestPartitionId);

return resultRequestPartitionId;
}

private boolean systemRequestHasExplicitPartition(@Nonnull SystemRequestDetails theRequest) {
Expand Down Expand Up @@ -336,6 +385,8 @@ && hasDefaultPartitionId(retVal)) {
retVal = RequestPartitionId.fromPartitionIds(partitionIds);
}

ourLog.trace("Partition normalization: {} -> {}", theRequestPartitionId, retVal);

return retVal;
}

Expand Down Expand Up @@ -418,4 +469,23 @@ private static void validateSinglePartitionIdOrName(@Nullable List<?> thePartiti
+ thePartitionIds);
}
}

private static void logTroubleshootingResult(
String theAction,
String theResourceType,
@Nullable RequestDetails theRequest,
RequestPartitionId theResult) {
String tenantId = theRequest != null ? theRequest.getTenantId() : null;
ourLog.debug(
"Partitioning: action={} resource type={} with request tenant ID={} routed to partition ID={}",
theAction,
theResourceType,
tenantId,
theResult);
}

private void logSystemRequestResolution(RequestDetails theRequest) {
assert theRequest instanceof SystemRequestDetails;
ourLog.trace("Partitioning: request is a system request with partition routing information.");
}
}
Loading