-
Notifications
You must be signed in to change notification settings - Fork 558
Avoid memory allocation by reusing TrackerClient in DegraderStrategy #1121
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
base: master
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -46,6 +46,7 @@ | |
| import java.net.URI; | ||
| import java.net.URISyntaxException; | ||
| import java.util.ArrayList; | ||
| import java.util.Collection; | ||
| import java.util.Collections; | ||
| import java.util.HashMap; | ||
| import java.util.HashSet; | ||
|
|
@@ -65,7 +66,7 @@ | |
|
|
||
| /** | ||
| * Implementation of {@link LoadBalancerStrategy} with additional supports partitioning of services whereas | ||
| * the the prior implementations do not. | ||
| * the prior implementations do not. | ||
| * | ||
| * @author David Hoa ([email protected]) | ||
| * @author Oby Sumampouw ([email protected]) | ||
|
|
@@ -116,26 +117,6 @@ public String getName() | |
| return DEGRADER_STRATEGY_NAME; | ||
| } | ||
|
|
||
| private List<DegraderTrackerClient> castToDegraderTrackerClients(Map<URI, TrackerClient> trackerClients) | ||
| { | ||
| List<DegraderTrackerClient> degraderTrackerClients = new ArrayList<>(trackerClients.size()); | ||
|
|
||
| for (TrackerClient trackerClient: trackerClients.values()) | ||
| { | ||
| if (trackerClient instanceof DegraderTrackerClient) | ||
| { | ||
| degraderTrackerClients.add((DegraderTrackerClient) trackerClient); | ||
| } | ||
| else | ||
| { | ||
| warn(_log, | ||
| "Client passed to DegraderV3 not an instance of DegraderTrackerClient, will not load balance to it.", | ||
| trackerClient); | ||
| } | ||
| } | ||
|
|
||
| return degraderTrackerClients; | ||
| } | ||
|
|
||
| @Override | ||
| public TrackerClient getTrackerClient(Request request, | ||
|
|
@@ -165,17 +146,19 @@ public TrackerClient getTrackerClient(Request request, | |
|
|
||
| if (trackerClients == null || trackerClients.size() == 0) | ||
| { | ||
| warn(_log, | ||
| "getTrackerClient called with null/empty trackerClients, so returning null"); | ||
|
|
||
| warn(_log, "getTrackerClient called with null/empty trackerClients, so returning null"); | ||
| return null; | ||
| } | ||
|
|
||
| List<DegraderTrackerClient> degraderTrackerClients = castToDegraderTrackerClients(trackerClients); | ||
| Collection<TrackerClient> trackerClientList = trackerClients.values(); | ||
|
Author
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. castToDegraderTrackerClients return an empty list if trackerClients cannot be cast to |
||
| if (!(trackerClientList.iterator().next() instanceof DegraderTrackerClient)) | ||
| { | ||
| trackerClients = Collections.emptyMap(); | ||
| } | ||
|
|
||
| // only one thread will be allowed to enter updatePartitionState for any partition | ||
| TimingContextUtil.markTiming(requestContext, TIMING_KEY); | ||
| checkUpdatePartitionState(clusterGenerationId, partitionId, degraderTrackerClients, shouldForceUpdate); | ||
| checkUpdatePartitionState(clusterGenerationId, partitionId, trackerClients, shouldForceUpdate); | ||
| TimingContextUtil.markTiming(requestContext, TIMING_KEY); | ||
|
|
||
| Ring<URI> ring = _state.getRing(partitionId); | ||
|
|
@@ -184,19 +167,19 @@ public TrackerClient getTrackerClient(Request request, | |
| Set<URI> excludedUris = ExcludedHostHints.getRequestContextExcludedHosts(requestContext); | ||
| if (excludedUris == null) | ||
| { | ||
| excludedUris = new HashSet<>(); | ||
| excludedUris = Collections.emptySet(); | ||
|
Author
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. Avoid another unnecessary memory allocation. |
||
| } | ||
|
|
||
| //no valid target host header was found in the request | ||
| DegraderTrackerClient client; | ||
| if (targetHostUri == null) | ||
| { | ||
| client = findValidClientFromRing(request, ring, degraderTrackerClients, excludedUris, requestContext); | ||
| client = (DegraderTrackerClient) findValidClientFromRing(request, ring, trackerClients, excludedUris, requestContext); | ||
| } | ||
| else | ||
| { | ||
| debug(_log, "Degrader honoring target host header in request, skipping hashing. URI: ", targetHostUri); | ||
| client = searchClientFromUri(targetHostUri, degraderTrackerClients); | ||
| client = searchClientFromUri(targetHostUri, trackerClients); | ||
| if (client == null) | ||
| { | ||
| warn(_log, "No client found for ", targetHostUri, ". Target host specified is no longer part of cluster"); | ||
|
|
@@ -237,7 +220,8 @@ public TrackerClient getTrackerClient(Request request, | |
| return client; | ||
| } | ||
|
|
||
| private DegraderTrackerClient findValidClientFromRing(Request request, Ring<URI> ring, List<DegraderTrackerClient> trackerClients, Set<URI> excludedUris, RequestContext requestContext) | ||
| private TrackerClient findValidClientFromRing(Request request, Ring<URI> ring, Map<URI, TrackerClient> trackerClients, | ||
| Set<URI> excludedUris, RequestContext requestContext) | ||
| { | ||
| // Compute the hash code | ||
| int hashCode = _hashFunction.hash(request); | ||
|
|
@@ -247,17 +231,10 @@ private DegraderTrackerClient findValidClientFromRing(Request request, Ring<URI> | |
| warn(_log, "Can not find hash ring to use"); | ||
| } | ||
|
|
||
| Map<URI, DegraderTrackerClient> trackerClientMap = new HashMap<>(trackerClients.size()); | ||
|
Author
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. Another memory allocation followed by O(N) complexity. |
||
|
|
||
| for (DegraderTrackerClient trackerClient : trackerClients) | ||
| { | ||
| trackerClientMap.put(trackerClient.getUri(), trackerClient); | ||
| } | ||
|
|
||
| // we operate only on URIs to ensure that we never hold on to an old tracker client | ||
| // that the cluster manager has removed | ||
| URI mostWantedURI = ring.get(hashCode); | ||
| DegraderTrackerClient client = trackerClientMap.get(mostWantedURI); | ||
| TrackerClient client = trackerClients.get(mostWantedURI); | ||
|
|
||
| if (client != null && !excludedUris.contains(mostWantedURI)) | ||
| { | ||
|
|
@@ -280,7 +257,7 @@ private DegraderTrackerClient findValidClientFromRing(Request request, Ring<URI> | |
| while (iterator.hasNext()) | ||
| { | ||
| targetHostUri = iterator.next(); | ||
| client = trackerClientMap.get(targetHostUri); | ||
| client = trackerClients.get(targetHostUri); | ||
|
|
||
| if (targetHostUri != mostWantedURI && !excludedUris.contains(targetHostUri) && client != null) | ||
| { | ||
|
|
@@ -318,7 +295,7 @@ else if (excludedUris.contains(targetHostUri)) | |
| * @param shouldForceUpdate | ||
| */ | ||
| private void checkUpdatePartitionState(long clusterGenerationId, int partitionId, | ||
| List<DegraderTrackerClient> trackerClients, boolean shouldForceUpdate) | ||
| Map<URI, TrackerClient> trackerClients, boolean shouldForceUpdate) | ||
| { | ||
| DegraderLoadBalancerStrategyConfig config = getConfig(); | ||
| final Partition partition = _state.getPartition(partitionId); | ||
|
|
@@ -387,24 +364,20 @@ else if(shouldUpdatePartition(clusterGenerationId, partition.getState(), config, | |
| } | ||
| } | ||
|
|
||
| private DegraderTrackerClient searchClientFromUri(URI uri, List<DegraderTrackerClient> trackerClients) | ||
| private DegraderTrackerClient searchClientFromUri(URI uri, Map<URI, TrackerClient> trackerClients) | ||
|
Author
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. When it is a map, we do the search in O(1) time rather than O(N). |
||
| { | ||
| for (DegraderTrackerClient trackerClient : trackerClients) { | ||
| if (trackerClient.getUri().equals(uri)) { | ||
| return trackerClient; | ||
| } | ||
| } | ||
| return null; | ||
| return (DegraderTrackerClient) trackerClients.get(uri); | ||
| } | ||
|
|
||
| private void updatePartitionState(long clusterGenerationId, Partition partition, List<DegraderTrackerClient> trackerClients, DegraderLoadBalancerStrategyConfig config) | ||
| private void updatePartitionState(long clusterGenerationId, Partition partition, | ||
| Map<URI, TrackerClient> trackerClients, DegraderLoadBalancerStrategyConfig config) | ||
| { | ||
| PartitionDegraderLoadBalancerState partitionState = partition.getState(); | ||
|
|
||
| List<DegraderTrackerClientUpdater> clientUpdaters = new ArrayList<>(); | ||
| for (DegraderTrackerClient client: trackerClients) | ||
| for (TrackerClient client: trackerClients.values()) | ||
| { | ||
| clientUpdaters.add(new DegraderTrackerClientUpdater(client, partition.getId())); | ||
| clientUpdaters.add(new DegraderTrackerClientUpdater((DegraderTrackerClient) client, partition.getId())); | ||
| } | ||
|
|
||
| boolean quarantineEnabled = _state.isQuarantineEnabled(); | ||
|
|
@@ -1050,6 +1023,18 @@ public static void overrideMinCallCount(int partitionId, double newOverrideDropR | |
| */ | ||
| protected static boolean shouldUpdatePartition(long clusterGenerationId, PartitionDegraderLoadBalancerState partitionState, | ||
|
Contributor
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 create a new method and call the new one from the old? This is a protected method and we'd like backwards compatibility.
Author
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. Kept the old one and created the overloaded version of it. The old one does the conversion (inefficient but still accessible). |
||
| DegraderLoadBalancerStrategyConfig config, boolean updateEnabled, boolean shouldForceUpdate, List<DegraderTrackerClient> trackerClients) | ||
| { | ||
| Map<URI, TrackerClient> trackerClientMap = new HashMap<>(trackerClients.size()); | ||
|
|
||
| for (TrackerClient trackerClient : trackerClients) | ||
| { | ||
| trackerClientMap.put(trackerClient.getUri(), trackerClient); | ||
| } | ||
| return shouldUpdatePartition(clusterGenerationId, partitionState, config, updateEnabled, shouldForceUpdate, trackerClientMap); | ||
| } | ||
|
|
||
| protected static boolean shouldUpdatePartition(long clusterGenerationId, PartitionDegraderLoadBalancerState partitionState, | ||
| DegraderLoadBalancerStrategyConfig config, boolean updateEnabled, boolean shouldForceUpdate, Map<URI, TrackerClient> trackerClients) | ||
| { | ||
| boolean trackerClientInconsistency = trackerClients.size() != partitionState.getPointsMap().size(); | ||
| return updateEnabled | ||
|
|
@@ -1115,7 +1100,7 @@ public Ring<URI> getRing(long clusterGenerationId, int partitionId, Map<URI, Tra | |
| return new DelegatingRingFactory<URI>(_config).createRing(Collections.emptyMap(), Collections.emptyMap()); | ||
| } | ||
|
|
||
| checkUpdatePartitionState(clusterGenerationId, partitionId, castToDegraderTrackerClients(trackerClients), shouldForceUpdate); | ||
| checkUpdatePartitionState(clusterGenerationId, partitionId, trackerClients, shouldForceUpdate); | ||
| return _state.getRing(partitionId); | ||
| } | ||
|
|
||
|
|
||
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.
Memory allocation followed by O(N) operations.