package edu.brown.hstore.handlers;
import org.apache.log4j.Logger;
import org.voltdb.CatalogContext;
import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.RpcCallback;
import com.google.protobuf.RpcController;
import edu.brown.hstore.HStoreCoordinator;
import edu.brown.hstore.HStoreSite;
import edu.brown.hstore.HStoreThreadManager;
import edu.brown.hstore.Hstoreservice.HStoreService;
import edu.brown.hstore.conf.HStoreConf;
import edu.brown.hstore.txns.LocalTransaction;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.protorpc.ProtoRpcController;
import edu.brown.utils.PartitionSet;
/**
* AbstractTransactionHandler is a wrapper around the invocation methods for some action
* There is a local and remote method to send a message to the necessary HStoreSites
* for a transaction. The sendMessages() method is the main entry point that the
* HStoreCoordinator's convenience methods will use.
* @param <T> The message that we will send out on the network
* @param <U> The expected message that we will need to get back as a response
*/
public abstract class AbstractTransactionHandler<T extends GeneratedMessage, U extends GeneratedMessage> {
private static final Logger LOG = Logger.getLogger(AbstractTransactionHandler.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
protected final HStoreSite hstore_site;
protected final HStoreConf hstore_conf;
protected final HStoreCoordinator coordinator;
protected final HStoreService handler;
protected final CatalogContext catalogContext;
/** The total number of sites in the cluster */
protected final int num_sites;
/** The site id where this handler is running at */
protected final int local_site_id;
public AbstractTransactionHandler(HStoreSite hstore_site, HStoreCoordinator hstore_coord) {
this.hstore_site = hstore_site;
this.hstore_conf = hstore_site.getHStoreConf();
this.catalogContext = hstore_site.getCatalogContext();
this.coordinator = hstore_coord;
this.handler = this.coordinator.getHandler();
this.num_sites = this.catalogContext.numberOfSites;
this.local_site_id = hstore_site.getSiteId();
}
/**
* Send a copy of a single message request to the partitions given as input
* If a partition is managed by the local HStoreSite, then we will invoke
* the sendLocal() method. If it is on a remote HStoreSite, then we will
* invoke sendRemote().
* @param ts
* @param request
* @param callback
* @param partitions
*/
public void sendMessages(LocalTransaction ts, T request, RpcCallback<U> callback, PartitionSet partitions) {
// If this flag is true, then we'll invoke the local method
// We want to do this *after* we send out all the messages to the remote sites
// so that we don't have to wait as long for the responses to come back over the network
boolean send_local = false;
boolean site_sent[] = new boolean[this.num_sites];
if (debug.val)
LOG.debug(String.format("Sending %s to %d partitions for %s",
request.getClass().getSimpleName(), partitions.size(), ts));
for (int partition : partitions.values()) {
int dest_site_id = this.catalogContext.getSiteIdForPartitionId(partition);
// Skip this HStoreSite if we're already sent it a message
if (site_sent[dest_site_id]) continue;
if (trace.val)
LOG.trace(String.format("Sending %s message to %s for %s",
request.getClass().getSimpleName(),
HStoreThreadManager.formatSiteName(dest_site_id), ts));
// Local Partition
if (this.local_site_id == dest_site_id) {
send_local = true;
}
// Remote Partition
else {
HStoreService channel = this.coordinator.getChannel(dest_site_id);
assert(channel != null) : "Invalid partition id '" + partition + "'";
ProtoRpcController controller = this.getProtoRpcController(ts, dest_site_id);
assert(controller != null) : "Invalid " + request.getClass().getSimpleName() + " ProtoRpcController for site #" + dest_site_id;
this.sendRemote(channel, controller, request, callback);
}
site_sent[dest_site_id] = true;
} // FOR
// Optimization: We'll invoke sendLocal() after we have sent out
// all of the messages to remote sites
if (send_local) this.sendLocal(ts.getTransactionId(), request, partitions, callback);
}
/**
* The processing method that is invoked if the outgoing message needs
* to be sent to a partition that is on the same machine as where this
* handler is executing.
* @param txn_id
* @param request
* @param partitions
* @param callback
*/
public abstract void sendLocal(Long txn_id, T request, PartitionSet partitions, RpcCallback<U> callback);
/**
* The processing method that is invoked if the outgoing message needs
* to be sent to a partition that is *not* managed by the same HStoreSite
* as where this handler is executing. This is non-blocking and does not
* wait for the callback to be executed in response to the remote side.
* @param channel
* @param controller
* @param request
* @param callback
*/
public abstract void sendRemote(HStoreService channel, ProtoRpcController controller, T request, RpcCallback<U> callback);
/**
* This is the method that is invoked on the remote HStoreSite for each incoming
* message request. This will then determine whether the message should be queued up
* for execution in a different thread, or whether it should invoke remoteHandler()
* right away.
* @param controller
* @param request
* @param callback
*/
public abstract void remoteQueue(RpcController controller, T request, RpcCallback<U> callback);
/**
* The remoteHandler is the code that executes on the remote node to process
* the request for a transaction that is executing on a different node.
* @param controller
* @param request
* @param callback
*/
public abstract void remoteHandler(RpcController controller, T request, RpcCallback<U> callback);
/**
* Return a cached ProtoRpcController handler from the LocalTransaction object
* @param ts
* @param site_id
* @return
*/
protected abstract ProtoRpcController getProtoRpcController(LocalTransaction ts, int site_id);
}