oracle.kv.impl.util.StateTracker 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.util;
import java.util.NoSuchElementException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sleepycat.je.ThreadInterruptedException;
import com.sleepycat.je.rep.ReplicatedEnvironment;
import com.sleepycat.je.rep.StateChangeEvent;
import com.sleepycat.je.rep.impl.node.NameIdPair;
import com.sleepycat.je.utilint.StoppableThread;
import oracle.kv.impl.topo.ResourceId;
/**
* Base class for threads which manage replicated environment state changes.
*/
public abstract class StateTracker extends StoppableThread {
/**
* The queue used to hold state transitions. Note that only the latest
* transition is of interest. Consequently, as an optimization, earlier
* entries can be discarded whenever that's convenient.
*
* The queue must at least be of length 2 so that it can hold the last
* state transition and the EOQ marker
*/
private final BlockingQueue stateTransitions =
new ArrayBlockingQueue(2);
/**
* The End Of Queue marker associated with the
* stateTransitions
queue.
*/
static private StateChangeEvent EOQ_DETACHED_STATE_MARKER =
new StateChangeEvent(ReplicatedEnvironment.State.DETACHED,
NameIdPair.NULL);
/**
* The service associated with the state changes.
*/
protected final ResourceId serviceId;
protected final AtomicBoolean shutdown = new AtomicBoolean(false);
/* The amount of time to wait for a soft shutdown of this thread. */
static private final int SOFT_SHUTDOWN_MS = 10000;
protected final Logger logger;
/**
* Creates the StateTracker thread.
*
* @param name the name of the thread
* @param serviceId the service ID whose state is to be tracked
* @param logger the logger to be used
* @param exceptionHandler exception handler
*/
protected StateTracker(String name, ResourceId serviceId, Logger logger,
UncaughtExceptionHandler exceptionHandler) {
super(null, exceptionHandler, name);
this.serviceId = serviceId;
this.logger = logger;
}
/**
* Returns true if there are no more state change events in the queue.
*
* @return true if there are no more state change events in the queue
*/
protected boolean isEmpty() {
return stateTransitions.isEmpty();
}
/**
* Invoked by the RN whenever the HA listener notifies it of an HA state
* change. Since the listener thread must not be held up, the method merely
* queues up the event for async processing by the Manager's thread.
*
* Note that this method is invoked sequentially given the semantics of the
* HA state listener.
*
* @param stateChangeEvent the state change event
*/
public void noteStateChange(StateChangeEvent stateChangeEvent) {
/*
* Must not miss state change events. Only the latest state matters.
*/
while (true) {
/* retry until the element is in the queue. */
try {
stateTransitions.add(stateChangeEvent);
logger.log(Level.INFO, "{0} queue added:{1}", // TODO - FINE
new Object[]{getName(), stateChangeEvent.getState()});
return;
} catch (IllegalStateException e) {
/*
* No space in queue, remove an element and try again, only the
* latest state change matters.
*/
try {
final StateChangeEvent rem = stateTransitions.remove();
if (rem == EOQ_DETACHED_STATE_MARKER) {
/*
* Events that come after EOQ can be ignored. Stick the
* EOQ back in the queue.
*/
stateChangeEvent = EOQ_DETACHED_STATE_MARKER;
continue;
}
logger.log(Level.INFO, "{0} entry removed:{1}", // TODO - FINE
new Object[]{getName(), rem});
} catch (NoSuchElementException nsee) {
/* Consumed by the run() thread */
}
}
}
}
/**
* The loop which processes state change requests.
*/
@Override
public void run() {
boolean interrupted = false;
try {
runInternal();
} catch (ThreadInterruptedException tie) {
/* Expected as part of a hard shutdown. */
interrupted = true;
} catch (InterruptedException e) {
/* Expected as part of a hard shutdown. */
interrupted = true;
} finally {
logger.log(Level.INFO, "{0} thread exited: {1}{2}",
new Object[]{getName(), serviceId,
interrupted ? " Interrupted. " : ""});
}
}
private void runInternal()
throws InterruptedException {
logger.log(Level.INFO, "{0} thread start: {1}",
new Object[]{getName(), serviceId});
while (!shutdown.get()) {
final StateChangeEvent sce = stateTransitions.take();
if (!isEmpty()) {
/*
* Skip this state change event, if a new state transition
* supersedes this one.
*/
continue;
}
doNotify(sce);
if (sce == EOQ_DETACHED_STATE_MARKER) {
break;
}
}
}
protected abstract void doNotify(StateChangeEvent sce)
throws InterruptedException;
@Override
public int initiateSoftShutdown() {
assert shutdown.get();
final long limitMs = System.currentTimeMillis() + SOFT_SHUTDOWN_MS;
/* Place a special EOQ marker */
noteStateChange(EOQ_DETACHED_STATE_MARKER);
/* Return the amount of time left to wait, or -1 if none */
final long waitMs = limitMs - System.currentTimeMillis();
return (waitMs <= 0) ? -1 : (int)waitMs;
}
@Override
protected Logger getLogger() {
return logger;
}
/**
* Stops this tracker thread and waits for the thread to exit.
*/
public void shutdown() {
if (!shutdown.compareAndSet(false, true)) {
return;
}
shutdownThread(logger);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy