String getTargetSpec(RoutingContext context, BucketId bucketId) {
            String sendRandomReason = null;
            ClusterState cachedClusterState = safeCachedClusterState.get();

            if (cachedClusterState != null) { // If we have a cached cluster state (regular case), we use that to calculate correct node.
                try{
                    Integer target = distribution.getIdealDistributorNode(cachedClusterState, bucketId, owningBucketStates);
                    // If we have had too many failures towards existing node, reset failure count and send to random
                    if (persistentFailureChecker.tooManyFailures(target)) {
                        sendRandomReason = "Too many failures detected versus distributor " + target + ". Sending to random instead of using cached state.";
                        target = null;
                    }
                    // If we have found a target, and the target exists in slobrok, send to it.
                    if (target != null) {
                        context.setContext(new MessageContext(cachedClusterState, target));
                        String targetSpec = hostFetcher.getTargetSpec(target, context);
                        if (targetSpec != null) {
                            if (context.shouldTrace(1)) {
                                context.trace(1, "Using distributor " + target + " for " +
                                        bucketId + " as our state version is " + cachedClusterState.getVersion());
                            }
                            return targetSpec;
                        } else {
                            sendRandomReason = "Want to use distributor " + target + " but it is not in slobrok. Sending to random.";
                            log.log(Level.FINE, "Target distributor is not in slobrok");
                        }
                    } else {
                        context.setContext(new MessageContext(cachedClusterState));
                    }
                } catch (Distribution.TooFewBucketBitsInUseException e) {
                    Reply reply = new WrongDistributionReply(cachedClusterState.toString(true));
                    reply.addError(new Error(DocumentProtocol.ERROR_WRONG_DISTRIBUTION,
                                             "Too few distribution bits used for given cluster state"));
                    context.setReply(reply);
                    return null;
                } catch (Distribution.NoDistributorsAvailableException e) {
                    log.log(Level.FINE, "No distributors available; clearing cluster state");
                    safeCachedClusterState.set(null);
                    sendRandomReason = "No distributors available. Sending to random distributor.";
                    context.setContext(createRandomDistributorTargetContext());
                }
            } else {
                context.setContext(createRandomDistributorTargetContext());
                sendRandomReason = "No cluster state cached. Sending to random distributor.";
            }
            if (context.shouldTrace(1)) {
                context.trace(1, sendRandomReason != null ? sendRandomReason : "Sending to random distributor for unknown reason");
            }
            return hostFetcher.getRandomTargetSpec(context);
        }
Message context class. Contains data we want to inspect about a request at reply time.
documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/ContentPolicy.java