-
Notifications
You must be signed in to change notification settings - Fork 275
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
Refactor operation tracker ctor and introduce custom percentiles #1159
Changes from 1 commit
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 |
---|---|---|
|
@@ -13,6 +13,12 @@ | |
*/ | ||
package com.github.ambry.config; | ||
|
||
import com.github.ambry.utils.Utils; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
|
||
/** | ||
* Configuration parameters required by a {@link com.github.ambry.router.Router}. | ||
* <p/> | ||
|
@@ -232,6 +238,15 @@ public class RouterConfig { | |
@Default("false") | ||
public final boolean routerUseGetBlobOperationForBlobInfo; | ||
|
||
/** | ||
* The custom percentiles of Histogram in operation tracker to be reported. This allows router to emit metrics of | ||
* arbitrary percentiles (i.e. 97th, 93th etc). An example of this config is "0.91,0.93,0.97"(comma separated), each | ||
* value should fall in {@code [0..1]}. | ||
*/ | ||
@Config("router.operation.tracker.custom.percentiles") | ||
@Default("") | ||
public final List<Double> routerOperationTrackerCustomPercentiles; | ||
|
||
/** | ||
* Create a RouterConfig instance. | ||
* @param verifiableProperties the properties map to refer to. | ||
|
@@ -289,5 +304,9 @@ public RouterConfig(VerifiableProperties verifiableProperties) { | |
verifiableProperties.getIntInRange("router.ttl.update.success.target", 2, 1, Integer.MAX_VALUE); | ||
routerUseGetBlobOperationForBlobInfo = | ||
verifiableProperties.getBoolean("router.use.get.blob.operation.for.blob.info", false); | ||
List<String> customPercentiles = | ||
Utils.splitString(verifiableProperties.getString("router.operation.tracker.custom.percentiles", ""), ","); | ||
routerOperationTrackerCustomPercentiles = | ||
Collections.unmodifiableList(customPercentiles.stream().map(Double::valueOf).collect(Collectors.toList())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we convert string to list when it's needed? Previous example: storeCompactionTriggers There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got you. It's OK. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,7 +66,7 @@ abstract class GetOperation { | |
|
||
/** | ||
* Construct a GetOperation | ||
* @param routerConfig the {@link RouterConfig} containing the configs for put operations. | ||
* @param routerConfig the {@link RouterConfig} containing the configs for get operations. | ||
* @param routerMetrics The {@link NonBlockingRouterMetrics} to be used for reporting metrics. | ||
* @param clusterMap the {@link ClusterMap} of the cluster | ||
* @param responseHandler the {@link ResponseHandler} responsible for failure detection. | ||
|
@@ -237,23 +237,19 @@ protected GetRequest createGetRequest(BlobId blobId, MessageFormatFlags flag, Ge | |
/** | ||
* Gets an {@link OperationTracker} based on the config and {@code partitionId}. | ||
* @param partitionId the {@link PartitionId} for which a tracker is required. | ||
* @param operationClass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment |
||
* @return an {@link OperationTracker} based on the config and {@code partitionId}. | ||
*/ | ||
protected OperationTracker getOperationTracker(PartitionId partitionId, byte datacenterId) { | ||
protected OperationTracker getOperationTracker(PartitionId partitionId, byte datacenterId, Class operationClass) { | ||
OperationTracker operationTracker; | ||
String trackerType = routerConfig.routerGetOperationTrackerType; | ||
String originatingDcName = clusterMap.getDatacenterName(datacenterId); | ||
if (trackerType.equals(SimpleOperationTracker.class.getSimpleName())) { | ||
operationTracker = new SimpleOperationTracker(routerConfig.routerDatacenterName, partitionId, | ||
routerConfig.routerGetCrossDcEnabled, originatingDcName, | ||
routerConfig.routerGetIncludeNonOriginatingDcReplicas, routerConfig.routerGetReplicasRequired, | ||
routerConfig.routerGetSuccessTarget, routerConfig.routerGetRequestParallelism); | ||
operationTracker = new SimpleOperationTracker(routerConfig, operationClass, partitionId, originatingDcName, true); | ||
} else if (trackerType.equals(AdaptiveOperationTracker.class.getSimpleName())) { | ||
operationTracker = new AdaptiveOperationTracker(routerConfig.routerDatacenterName, partitionId, | ||
routerConfig.routerGetCrossDcEnabled, originatingDcName, | ||
routerConfig.routerGetIncludeNonOriginatingDcReplicas, routerConfig.routerGetReplicasRequired, | ||
routerConfig.routerGetSuccessTarget, routerConfig.routerGetRequestParallelism, time, localColoTracker, | ||
crossColoTracker, pastDueCounter, routerConfig.routerLatencyToleranceQuantile); | ||
operationTracker = | ||
new AdaptiveOperationTracker(routerConfig, operationClass, partitionId, originatingDcName, localColoTracker, | ||
crossColoTracker, pastDueCounter, routerMetrics, time); | ||
} else { | ||
throw new IllegalArgumentException("Unrecognized tracker type: " + trackerType); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
|
||
import com.github.ambry.clustermap.PartitionId; | ||
import com.github.ambry.clustermap.ReplicaId; | ||
import com.github.ambry.config.RouterConfig; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.Iterator; | ||
|
@@ -57,9 +58,10 @@ | |
* | ||
*/ | ||
class SimpleOperationTracker implements OperationTracker { | ||
protected final String datacenterName; | ||
protected final int successTarget; | ||
protected final int parallelism; | ||
protected final LinkedList<ReplicaId> replicaPool = new LinkedList<ReplicaId>(); | ||
protected final LinkedList<ReplicaId> replicaPool = new LinkedList<>(); | ||
|
||
protected int totalReplicaCount = 0; | ||
protected int inflightCount = 0; | ||
|
@@ -72,25 +74,43 @@ class SimpleOperationTracker implements OperationTracker { | |
/** | ||
* Constructor for an {@code SimpleOperationTracker}. | ||
* | ||
* @param datacenterName The datacenter where the router is located. | ||
* @param routerConfig The {@link RouterConfig} containing the configs for operation tracker. | ||
* @param operationClass The operation class in which operation tracker is used. | ||
* @param partitionId The partition on which the operation is performed. | ||
* @param crossColoEnabled {@code true} if requests can be sent to remote replicas, {@code false} | ||
* otherwise. | ||
* @param originatingDcName The original DC where blob was put. | ||
* @param includeNonOriginatingDcReplicas if take the option to include remote non originating DC replicas. | ||
* @param replicasRequired The number of replicas required for the operation. | ||
* @param successTarget The number of successful responses required to succeed the operation. | ||
* @param parallelism The maximum number of inflight requests at any point of time. | ||
* @param shuffleReplicas Indicates if the replicas need to be shuffled. | ||
*/ | ||
SimpleOperationTracker(String datacenterName, PartitionId partitionId, boolean crossColoEnabled, | ||
String originatingDcName, boolean includeNonOriginatingDcReplicas, int replicasRequired, int successTarget, | ||
int parallelism, boolean shuffleReplicas) { | ||
SimpleOperationTracker(RouterConfig routerConfig, Class operationClass, PartitionId partitionId, | ||
String originatingDcName, boolean shuffleReplicas) { | ||
// populate tracker parameters based on operation type | ||
boolean crossColoEnabled = false; | ||
boolean includeNonOriginatingDcReplicas = true; | ||
int replicasRequired = Integer.MAX_VALUE; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how is this local variable used? |
||
if (GetOperation.class.isAssignableFrom(operationClass)) { | ||
successTarget = routerConfig.routerGetSuccessTarget; | ||
parallelism = routerConfig.routerGetRequestParallelism; | ||
crossColoEnabled = routerConfig.routerGetCrossDcEnabled; | ||
includeNonOriginatingDcReplicas = routerConfig.routerGetIncludeNonOriginatingDcReplicas; | ||
replicasRequired = routerConfig.routerGetReplicasRequired; | ||
} else if (operationClass == PutOperation.class) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One other option is to make an enum to represent the operation tracker types (i.e. GET, PUT, DELETE, TTL_UPDATE). This is a little more explicit and resilient to new classes/name changes. |
||
successTarget = routerConfig.routerPutSuccessTarget; | ||
parallelism = routerConfig.routerPutRequestParallelism; | ||
} else if (operationClass == DeleteOperation.class) { | ||
successTarget = routerConfig.routerDeleteSuccessTarget; | ||
parallelism = routerConfig.routerDeleteRequestParallelism; | ||
crossColoEnabled = true; | ||
} else if (operationClass == TtlUpdateOperation.class) { | ||
successTarget = routerConfig.routerTtlUpdateSuccessTarget; | ||
parallelism = routerConfig.routerTtlUpdateRequestParallelism; | ||
crossColoEnabled = true; | ||
} else { | ||
throw new IllegalArgumentException("Unrecognized operation: " + operationClass.getSimpleName()); | ||
} | ||
if (parallelism < 1) { | ||
throw new IllegalArgumentException("Parallelism has to be > 0. Configured to be " + parallelism); | ||
} | ||
this.successTarget = successTarget; | ||
this.parallelism = parallelism; | ||
datacenterName = routerConfig.routerDatacenterName; | ||
|
||
// Order the replicas so that local healthy replicas are ordered and returned first, | ||
// then the remote healthy ones, and finally the possibly down ones. | ||
List<? extends ReplicaId> replicas = partitionId.getReplicaIds(); | ||
|
@@ -149,25 +169,6 @@ class SimpleOperationTracker implements OperationTracker { | |
this.otIterator = new OpTrackerIterator(); | ||
} | ||
|
||
/** | ||
* Constructor for an {@code SimpleOperationTracker}, which shuffles replicas. | ||
* | ||
* @param datacenterName The datacenter where the router is located. | ||
* @param partitionId The partition on which the operation is performed. | ||
* @param crossColoEnabled {@code true} if requests can be sent to remote replicas, {@code false} otherwise. | ||
* @param originatingDcName The original DC where blob was put. null if DC unknown. | ||
* @param includeNonOriginatingDcReplicas if take the option to include remote non originating DC replicas. | ||
* @param replicasRequired The number of replicas required for the operation. | ||
* @param successTarget The number of successful responses required to succeed the operation. | ||
* @param parallelism The maximum number of inflight requests at any point of time. | ||
*/ | ||
SimpleOperationTracker(String datacenterName, PartitionId partitionId, boolean crossColoEnabled, | ||
String originatingDcName, boolean includeNonOriginatingDcReplicas, int replicasRequired, int successTarget, | ||
int parallelism) { | ||
this(datacenterName, partitionId, crossColoEnabled, originatingDcName, includeNonOriginatingDcReplicas, | ||
replicasRequired, successTarget, parallelism, true); | ||
} | ||
|
||
@Override | ||
public boolean hasSucceeded() { | ||
return succeededCount >= successTarget; | ||
|
@@ -220,7 +221,14 @@ private boolean hasFailed() { | |
} | ||
|
||
/** | ||
* Helper function to catch a potential race condition in {@link SimpleOperationTracker#SimpleOperationTracker(String, PartitionId, boolean, String, boolean, int, int, int, boolean)}. | ||
* @return the success target number of this operation tracker. | ||
*/ | ||
public int getSuccessTarget() { | ||
return successTarget; | ||
} | ||
|
||
/** | ||
* Helper function to catch a potential race condition in {@link SimpleOperationTracker#SimpleOperationTracker(RouterConfig, Class, PartitionId, String, boolean)}. | ||
* | ||
* @param partitionId The partition on which the operation is performed. | ||
* @param examinedReplicas All replicas examined. | ||
|
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.
how will these custom percentiles be used by operation tracker in the future? Right now, they are only registered in the
MetricRegistry
for viewing.