package com.zillabyte.motherbrain.flow.rpc;
import java.util.concurrent.TimeoutException;
import org.apache.log4j.Logger;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.javatuples.Pair;
import com.zillabyte.motherbrain.coordination.CoordinationException;
import com.zillabyte.motherbrain.flow.MapTuple;
import com.zillabyte.motherbrain.flow.StateMachineException;
import com.zillabyte.motherbrain.flow.collectors.OutputCollector;
import com.zillabyte.motherbrain.flow.collectors.coordinated.CoordinatedOutputCollector;
import com.zillabyte.motherbrain.flow.operations.FunctionState;
import com.zillabyte.motherbrain.flow.operations.OperationException;
import com.zillabyte.motherbrain.flow.operations.Source;
import com.zillabyte.motherbrain.flow.operations.SourceState;
import com.zillabyte.motherbrain.flow.operations.multilang.MultiLangException;
import com.zillabyte.motherbrain.flow.rpc.queues.InputQueue;
import com.zillabyte.motherbrain.top.MotherbrainException;
import com.zillabyte.motherbrain.universe.Config;
import com.zillabyte.motherbrain.universe.Universe;
import com.zillabyte.motherbrain.utils.Utils;
@NonNullByDefault
public final class RPCSource extends Source {
private static final long serialVersionUID = -8496379143900675797L;
private static Logger log = Utils.getLogger(RPCSource.class);
private InputQueue _inputQueue;
private final long IDLE_INTERVAL_MS = Config.getOrDefault("rpc.source.idle.interval", 1000L * 60 * 5);
private long _lastEmit = System.currentTimeMillis();
/***
*
* @param node
*/
public RPCSource(String name) {
super(name);
}
/***
*
*/
@Override
public void prepare() throws OperationException {
_inputQueue = Universe.instance().rpcQueueFactory().getInputQueue(this);
_inputQueue.init();
}
/***
*
*/
@Override
protected boolean nextTuple(OutputCollector rawCollector) throws OperationException, InterruptedException {
// Get the next request (if any) from the queue...
RPCRequest request = _inputQueue.getNextRequest();
if(request != null) {
CoordinatedOutputCollector collector = (CoordinatedOutputCollector) rawCollector;
log.info("processing rpc batch: " + request.id);
collector.setCurrentBatch(request.id); // RPC sources should not have sub-batches (always 0)
try {
// Emit the tuple the stream...
for(Pair<String, MapTuple> p : request.getTuples()) {
MapTuple tuple = p.getValue1();
markEmit();
collector.emit(tuple);
}
} catch(MotherbrainException e) {
this.logger().logError(e);
} finally {
// Finalize the batch, even if there was an error
collector.explicitlyCompleteBatch(request.id);
log.info("rpc batch source-done: " + request.id);
}
}
// Only idle (return false) if we haven't seen anything for a while..
boolean idle = isReadyForIdle();
return !idle;
}
/***
*
*/
private void markEmit() {
_lastEmit = System.currentTimeMillis();
}
/***
*
* @return
*/
private boolean isReadyForIdle() {
if (_lastEmit + IDLE_INTERVAL_MS < System.currentTimeMillis()) {
return true;
} else {
return false;
}
}
@Override
public void handleIdleDetected() throws InterruptedException, OperationException {
// Transition the RPC source to idle state from STARTED. The onEndCycle below will take care of this transition from EMITTING to IDLE.
try {
if (isReadyForIdle() && _state == SourceState.STARTED ) {
transitionToState(FunctionState.IDLE.toString(), true);
}
} catch (StateMachineException | TimeoutException | CoordinationException e) {
throw new OperationException(this, e);
}
}
/****
*
* RPC Source PARALLELISM MUST BE 1! Change only with great caution.
*
*/
@Override
public int getMaxParallelism() {
return 1;
}
@Override
public void onBeginCycle(@NonNull OutputCollector output) throws InterruptedException, OperationException, CoordinationException, StateMachineException, TimeoutException {
transitionToState(SourceState.EMITTING.toString(), true);
}
@Override
public void onEndCycle(OutputCollector output) throws InterruptedException, OperationException, CoordinationException, StateMachineException, TimeoutException {
transitionToState(SourceState.IDLE.toString(), true);
try {
handleStats_ThreadUnsafe();
} catch (CoordinationException e) {
throw new OperationException(this, e);
}
}
@Override
public boolean nextTupleDetected() {
return _inputQueue.nextRequestAvailable();
}
@Override
public void cleanup() throws MultiLangException, OperationException, InterruptedException {
super.cleanup();
_inputQueue.shutdown();
}
}