oracle.kv.impl.api.rgstate.UpdateThread Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of oracle-nosql-server Show documentation
Show all versions of oracle-nosql-server Show documentation
NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.
/*-
* Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle NoSQL
* Database made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle NoSQL Database for a copy of the license and
* additional information.
*/
package oracle.kv.impl.api.rgstate;
import java.rmi.ConnectIOException;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import oracle.kv.ResultHandler;
import oracle.kv.FaultException;
import oracle.kv.impl.api.RequestDispatcher;
import oracle.kv.impl.rep.admin.IllegalRepNodeServiceStateException;
import oracle.kv.impl.rep.admin.RepNodeAdminFaultException;
import oracle.kv.impl.topo.RepGroupId;
import oracle.kv.impl.topo.RepNodeId;
import oracle.kv.impl.topo.ResourceId;
import oracle.kv.impl.topo.ResourceId.ResourceType;
import oracle.kv.impl.util.KVThreadFactory;
import oracle.kv.impl.util.RateLimitingLogger;
import oracle.kv.impl.util.registry.RegistryUtils;
/**
* Base class for update threads.
*
* TODO: pass stats out to Monitor when in an RN. Log when in a client? TODO:
* Limit the rate of log messages on error?
*/
public abstract class UpdateThread extends Thread {
/**
* The max number of RNs to track for the purposes of log rate limiting.
*/
private static final int LIMIT_RNS = 100;
/**
* 1 min in millis
*/
private static final int ONE_MINUTE_MS = 60 * 1000;
/**
* The dispatcher associated with this updater thread.
*/
protected final RequestDispatcher requestDispatcher;
/**
* The thread pool used to resolve request handles in parallel.
*/
protected final UpdateThreadPoolExecutor threadPool;
/**
* Shutdown can only be executed once. The shutdown field protects against
* multiple invocations.
*/
protected final AtomicBoolean shutdown = new AtomicBoolean(false);
/**
* The time interval between executions of a concurrent update pass.
*/
protected final int periodMs;
/**
* The amount of time to wait when resolving a handle.
*/
private final int resolutionTimeoutMs = 10000;
private volatile int resolveCount;
private volatile int resolveFailCount;
private volatile int resolveExceptionCount;
protected final Logger logger;
/*
* Encapsulates the above logger to limit the rate of log messages
* associated with a specific RN.
*/
private final RateLimitingLogger rateLimitingLogger;
/**
* Creates the RN state update thread.
*
* @param requestDispatcher the request dispatcher associated with this
* thread.
*
* @param periodMs the time period between passes over the RNs to see if
* they need updating.
*
* @param logger the logger used by the update threads.
*/
protected UpdateThread(RequestDispatcher requestDispatcher,
int periodMs,
UncaughtExceptionHandler handler,
final Logger logger) {
super();
this.requestDispatcher = requestDispatcher;
this.periodMs = periodMs;
this.logger = logger;
this.rateLimitingLogger = new RateLimitingLogger
(ONE_MINUTE_MS, LIMIT_RNS, logger);
final String name = " " + requestDispatcher.getDispatcherId() +
" " + getClass().getSimpleName();
this.setName(name);
this.setDaemon(true);
this.setUncaughtExceptionHandler(handler);
/* Reclaim threads if they have not been used over 5 time periods. */
final int keepAliveTimeMs = periodMs * 5;
threadPool =
new UpdateThreadPoolExecutor
(logger, keepAliveTimeMs,
new UpdateThreadFactory(logger, name, handler));
}
/**
* For test use only.
*/
UpdateThreadPoolExecutor getThreadPool() {
return threadPool;
}
public int getResolveCount() {
return resolveCount;
}
public int getResolveFailCount() {
return resolveFailCount;
}
public int getResolveExceptionCount() {
return resolveExceptionCount;
}
@Override
public void run() {
logger.log(Level.INFO, "{0} started", this);
try {
while (!shutdown.get()) {
doUpdate();
if (shutdown.get()) {
return;
}
try {
Thread.sleep(periodMs);
} catch (InterruptedException e) {
logger.log(Level.WARNING, "{0} interrupted", this);
throw new IllegalStateException(e);
}
}
} catch (Throwable t) {
requestDispatcher.shutdown(t);
} finally {
shutdown();
}
}
/**
* Called periodically to perform update.
*/
protected abstract void doUpdate();
/**
* Returns the list of RNs whose state must be maintained. If the
* request dispatcher belongs to the client the state associated with all
* RNs is maintained. If it's associated with an RN, both states associated
* with its RG and states in other RGs whose requestHandler needs repair
* are maintained. This behavior will need to be updated with support for
* partition migration, or if support light weight clients which don't
* themselves maintain RN state.
*
* @return the list of RNs whose state is to be maintained.
*/
protected Collection getRNs() {
final ResourceId dispatcherId = requestDispatcher.getDispatcherId();
final RepGroupStateTable rgst =
requestDispatcher.getRepGroupStateTable();
if (dispatcherId.getType() == ResourceType.CLIENT) {
return rgst.getRepNodeStates();
} else if (dispatcherId.getType() == ResourceType.REP_NODE) {
final Collection rns =
rgst.getRNStatesNeedingRepair();
final RepNodeId rnId = (RepNodeId)dispatcherId;
rns.addAll(getRNs(new RepGroupId(rnId.getGroupId())));
return rns;
} else {
throw new IllegalStateException
("Unexpected dispatcher: " + dispatcherId);
}
}
/**
* Returns the rep group state of the specified group.
* @param groupId a group Id
* @return a rep group state
*/
protected RepGroupState getRepGroupState(RepGroupId groupId) {
final RepGroupStateTable rgst =
requestDispatcher.getRepGroupStateTable();
return rgst.getGroupState(groupId);
}
/**
* Returns the list of RNs in the specified group.
*
* @param groupId a group Id
* @return the list of RNs in the group
*/
protected Collection getRNs(RepGroupId groupId) {
return getRepGroupState(groupId).getRepNodeStates();
}
/**
* Returns true if the handle of the specified state needs resolution.
* If true a thread is started to resolve asynchronously, so that a single
* network problem does not tie up resolution of all handles.
*
* @param rnState target node
* @return true if the handle needs resolution
*/
protected boolean needsResolution(RepNodeState rnState) {
if (!rnState.reqHandlerNeedsResolution()) {
return false;
}
threadPool.execute(new ResolveHandler(rnState));
return true;
}
public void shutdown() {
if (!shutdown.compareAndSet(false, true)) {
/* Already shutdown. */
return;
}
logger.log(Level.INFO, "{0} shutdown", this);
threadPool.shutdownNow();
}
/**
* The task that's actually executed by the threads in the thread pool to
* resolve handles.
*/
private class ResolveHandler implements UpdateThreadPoolExecutor.UpdateTask {
final RepNodeState rns;
ResolveHandler(RepNodeState rns) {
super();
this.rns = rns;
}
@Override
public RepNodeId getResourceId() {
return rns.getRepNodeId();
}
@Override
public void run() {
final RegistryUtils regUtils = requestDispatcher.getRegUtils();
if (regUtils == null) {
/*
* The request dispatcher has not initialized itself as
* yet. Retry later.
*/
return;
}
class ResolveResultHandler implements ResultHandler {
@Override
public void onResult(Boolean ref, Throwable e) {
if (e != null) {
logger.log(Level.WARNING,
"Exception in ResolveHandler thread " +
"when contacting:" + rns.getRepNodeId(),
e);
resolveExceptionCount++;
} else if (!ref) {
logger.log(Level.FINEST, "Resolve RN failed: {0}",
rns);
resolveFailCount++;
} else {
logger.log(Level.FINEST, "Resolve RN succeeded: {0}",
rns);
resolveCount++;
}
}
}
logger.log(Level.FINEST, "Probing RN state: {0}", rns);
rns.resolveReqHandlerRef(regUtils, resolutionTimeoutMs,
new ResolveResultHandler());
}
}
/**
* Produces a brief message (whenever possible) describing the exception.
* In particular, it avoids use of FaultException.toString which always
* logs the complete stack trace.
*
* @param level the logging level to be used
* @param msgPrefix the message prefix text
* @param exception the exception
*/
protected void logBrief(RepNodeId rnId,
Level level,
String msgPrefix,
Exception exception) {
if (exception instanceof FaultException) {
final FaultException fe = (FaultException) exception;
final String fcName = fe.getFaultClassName();
if ((fcName != null) && (fe.getMessage() != null)) {
/* Avoid use of FaultException.toString and stack trace */
rateLimitingLogger.log(rnId, level,
msgPrefix +
" Fault class:" + fcName +
" Problem:" + fe.getMessage());
return;
}
}
if (exception instanceof java.rmi.ConnectException ||
exception instanceof ConnectIOException) {
rateLimitingLogger.log(rnId, level,
msgPrefix +
" Problem:" + exception.toString());
return;
}
/* Default to use of toString */
logger.log(level, "{0} Problem:{1}",
new Object[]{msgPrefix, exception.toString()});
}
/**
* Logs a suitable message on failure.
*/
protected void logOnFailure(RepNodeId rnId,
Exception exception,
final String changesMsg) {
if (exception == null) {
logger.info("Error. " + changesMsg);
return;
}
final String irnsseName =
IllegalRepNodeServiceStateException.class.getName();
if ((exception instanceof RepNodeAdminFaultException) &&
((RepNodeAdminFaultException) exception).
getFaultClassName().equals(irnsseName)) {
/* log an abbreviated message. */
rateLimitingLogger.log(rnId, Level.INFO,
changesMsg + "Exception message:" +
exception.getMessage());
} else {
logBrief(rnId, Level.INFO, changesMsg, exception);
}
}
/**
* Name the update thread pool threads, and pass any uncaught exceptions
* to the logger.
*/
private class UpdateThreadFactory extends KVThreadFactory {
private final String name;
private final UncaughtExceptionHandler handler;
UpdateThreadFactory(Logger logger,
String name,
UncaughtExceptionHandler handler) {
super(null, logger);
this.name = name + "_Updater";
this.handler = handler;
}
@Override
public String getName() {
return name;
}
@Override
public UncaughtExceptionHandler makeUncaughtExceptionHandler() {
return handler;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy