com.yahoo.container.jdisc.messagebus.SessionCache Maven / Gradle / Ivy
The newest version!
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.jdisc.messagebus;
import com.yahoo.component.AbstractComponent;
import com.yahoo.component.annotation.Inject;
import com.yahoo.container.jdisc.ContainerMbusConfig;
import com.yahoo.document.DocumentTypeManager;
import com.yahoo.documentapi.messagebus.protocol.DocumentProtocol;
import com.yahoo.jdisc.ReferencedResource;
import com.yahoo.jdisc.References;
import com.yahoo.jdisc.ResourceReference;
import com.yahoo.jdisc.SharedResource;
import com.yahoo.messagebus.ConfigAgent;
import com.yahoo.messagebus.DynamicThrottlePolicy;
import com.yahoo.messagebus.IntermediateSessionParams;
import com.yahoo.messagebus.MessageBus;
import com.yahoo.messagebus.MessageBusParams;
import com.yahoo.messagebus.MessagebusConfig;
import com.yahoo.messagebus.Protocol;
import com.yahoo.messagebus.SourceSessionParams;
import com.yahoo.messagebus.StaticThrottlePolicy;
import com.yahoo.messagebus.ThrottlePolicy;
import com.yahoo.messagebus.network.NetworkMultiplexer;
import com.yahoo.messagebus.shared.SharedIntermediateSession;
import com.yahoo.messagebus.shared.SharedMessageBus;
import com.yahoo.messagebus.shared.SharedSourceSession;
import com.yahoo.yolean.concurrent.Memoized;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Class to encapsulate access to slobrok sessions.
*
* @author Steinar Knutsen
* @author Einar Rosenvinge
*/
// TODO jonmv: Remove this: only used with more than one entry by FeedHandlerV3, where only timeout varies.
// rant: This whole construct is because DI at one point didn't exist, so getting hold of a shared resource
// or session was hard(?), and one resorted to routing through the Container, using URIs, to the correct
// MbusClient, with or without throttling. This introduced the problem of ownership during shutdown,
// which was solved with manual reference counting. This is all much better solved with DI, which (now)
// owns everything, and does component shutdown in reverse construction order, which is always right.
// So for the sake of everyone's mental health, this should all just be removed now! I suspect this is
// even the case for Request; we can track in handlers, and warn when requests have been misplaced.
public final class SessionCache extends AbstractComponent {
private static final Logger log = Logger.getLogger(SessionCache.class.getName());
private final Memoized messageBus;
private final Object intermediateLock = new Object();
private final Map intermediates = new HashMap<>();
private final IntermediateSessionCreator intermediatesCreator = new IntermediateSessionCreator();
private final Object sourceLock = new Object();
private final Map sources = new HashMap<>();
private final SourceSessionCreator sourcesCreator = new SourceSessionCreator();
@Inject
public SessionCache(NetworkMultiplexerProvider nets, ContainerMbusConfig containerMbusConfig,
DocumentTypeManager documentTypeManager,
MessagebusConfig messagebusConfig) {
this(nets::net, containerMbusConfig, documentTypeManager, messagebusConfig);
}
public SessionCache(Supplier net, ContainerMbusConfig containerMbusConfig,
DocumentTypeManager documentTypeManager,
MessagebusConfig messagebusConfig) {
this(net,
containerMbusConfig,
messagebusConfig,
new DocumentProtocol(documentTypeManager));
}
public SessionCache(Supplier net, ContainerMbusConfig containerMbusConfig,
MessagebusConfig messagebusConfig, Protocol protocol) {
this.messageBus = new Memoized<>(() -> createSharedMessageBus(net.get(), containerMbusConfig, messagebusConfig, protocol),
SharedMessageBus::release);
}
@Override
public void deconstruct() {
messageBus.close();
}
// Lazily create shared message bus.
private SharedMessageBus bus() {
return messageBus.get();
}
private static SharedMessageBus createSharedMessageBus(NetworkMultiplexer net,
ContainerMbusConfig mbusConfig,
MessagebusConfig messagebusConfig,
Protocol protocol) {
MessageBusParams mbusParams = new MessageBusParams().addProtocol(protocol);
mbusParams.setMaxPendingCount(mbusConfig.maxpendingcount());
MessageBus bus = new MessageBus(net, mbusParams);
new ConfigAgent(messagebusConfig, bus); // Configure the wrapped MessageBus with a routing table.
return new SharedMessageBus(bus);
}
ReferencedResource retainIntermediate(IntermediateSessionParams p) {
return intermediatesCreator.retain(intermediateLock, intermediates, p);
}
public ReferencedResource retainSource(SourceSessionParams p) {
return sourcesCreator.retain(sourceLock, sources, p);
}
private abstract static class SessionCreator {
abstract SESSION create(PARAMS p);
abstract KEY buildKey(PARAMS p);
abstract void logReuse(SESSION session);
ReferencedResource retain(Object lock, Map registry, PARAMS p) {
SESSION session;
ResourceReference sessionReference;
KEY key = buildKey(p);
// this lock is held for a horribly long time, but I see no way of
// making it slimmer
synchronized (lock) {
session = registry.get(key);
if (session == null) {
session = createAndStore(registry, p, key);
sessionReference = References.fromResource(session);
} else {
try {
sessionReference = session.refer(this);
logReuse(session);
} catch (IllegalStateException e) {
session = createAndStore(registry, p, key);
sessionReference = References.fromResource(session);
}
}
}
return new ReferencedResource<>(session, sessionReference);
}
SESSION createAndStore(Map registry, PARAMS p, KEY key) {
SESSION session = create(p);
registry.put(key, session);
return session;
}
}
private class SourceSessionCreator
extends SessionCreator {
@Override
SharedSourceSession create(SourceSessionParams p) {
log.log(Level.FINE, "Creating new source session.");
return bus().newSourceSession(p);
}
@Override
SourceSessionKey buildKey(SourceSessionParams p) {
return new SourceSessionKey(p);
}
@Override
void logReuse(SharedSourceSession session) {
log.log(Level.FINE, "Reusing source session.");
}
}
private class IntermediateSessionCreator
extends SessionCreator {
@Override
SharedIntermediateSession create(IntermediateSessionParams p) {
log.log(Level.FINE, "Creating new intermediate session " + p.getName());
return bus().newIntermediateSession(p);
}
@Override
String buildKey(IntermediateSessionParams p) {
return p.getName();
}
@Override
void logReuse(SharedIntermediateSession session) {
log.log(Level.FINE, "Reusing intermediate session " + session.name());
}
}
static class ThrottlePolicySignature {
@Override
public int hashCode() {
return getClass().hashCode();
}
}
static final class StaticThrottlePolicySignature extends ThrottlePolicySignature {
private final int maxPendingCount;
private final long maxPendingSize;
StaticThrottlePolicySignature(StaticThrottlePolicy policy) {
maxPendingCount = policy.getMaxPendingCount();
maxPendingSize = policy.getMaxPendingSize();
}
@Override
public int hashCode() {
int prime = 31;
int result = super.hashCode();
result = prime * result + maxPendingCount;
result = prime * result + Long.hashCode(maxPendingSize);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (! (obj instanceof StaticThrottlePolicySignature other)) return false;
return (maxPendingCount == other.maxPendingCount) && (maxPendingSize == other.maxPendingSize);
}
}
static final class DynamicThrottlePolicySignature extends ThrottlePolicySignature {
private final int maxPending;
private final double maxWindowSize;
private final double minWindowSize;
private final double windowSizeBackoff;
private final double windowSizeIncrement;
DynamicThrottlePolicySignature(DynamicThrottlePolicy policy) {
maxPending = policy.getMaxPendingCount();
maxWindowSize = policy.getMaxWindowSize();
minWindowSize = policy.getMinWindowSize();
windowSizeBackoff = policy.getWindowSizeBackOff();
windowSizeIncrement = policy.getWindowSizeIncrement();
}
@Override
public int hashCode() {
int prime = 31;
int result = super.hashCode();
result = prime * result + maxPending;
result = prime * result + Double.hashCode(maxWindowSize);
result = prime * result + Double.hashCode(minWindowSize);
result = prime * result + Double.hashCode(windowSizeBackoff);
result = prime * result + Double.hashCode(windowSizeIncrement);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (! (obj instanceof DynamicThrottlePolicySignature other)) return false;
if (maxPending != other.maxPending) return false;
if (Double.doubleToLongBits(maxWindowSize) != Double.doubleToLongBits(other.maxWindowSize)) return false;
if (Double.doubleToLongBits(minWindowSize) != Double.doubleToLongBits(other.minWindowSize)) return false;
if (Double.doubleToLongBits(windowSizeBackoff) != Double.doubleToLongBits(other.windowSizeBackoff)) return false;
if (Double.doubleToLongBits(windowSizeIncrement) != Double.doubleToLongBits(other.windowSizeIncrement)) return false;
return true;
}
}
static class UnknownThrottlePolicySignature extends ThrottlePolicySignature {
private final ThrottlePolicy policy;
UnknownThrottlePolicySignature(final ThrottlePolicy policy) {
this.policy = policy;
}
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (other.getClass() != getClass()) {
return false;
}
return ((UnknownThrottlePolicySignature) other).policy == policy;
}
}
static class SourceSessionKey {
private final double timeout;
private final ThrottlePolicySignature policy;
SourceSessionKey(SourceSessionParams p) {
timeout = p.getTimeout();
policy = createSignature(p.getThrottlePolicy());
}
private static ThrottlePolicySignature createSignature(ThrottlePolicy policy) {
Class> policyClass = policy.getClass();
if (policyClass == DynamicThrottlePolicy.class) {
return new DynamicThrottlePolicySignature((DynamicThrottlePolicy) policy);
} else if (policyClass == StaticThrottlePolicy.class) {
return new StaticThrottlePolicySignature((StaticThrottlePolicy) policy);
} else {
return new UnknownThrottlePolicySignature(policy);
}
}
@Override
public String toString() {
return "SourceSessionKey{" + "timeout=" + timeout + ", policy=" + policy + '}';
}
@Override
public int hashCode() {
int prime = 31;
int result = 1;
result = prime * result + ((policy == null) ? 0 : policy.hashCode());
result = prime * result + Double.hashCode(timeout);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
SourceSessionKey other = (SourceSessionKey) obj;
if (policy == null) {
if (other.policy != null) return false;
} else if (!policy.equals(other.policy)) {
return false;
}
if (Double.doubleToLongBits(timeout) != Double.doubleToLongBits(other.timeout)) return false;
return true;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy