package lsr.paxos.storage;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import lsr.common.ClientRequest;
import lsr.common.CrashModel;
import lsr.common.ProcessDescriptor;
import lsr.paxos.messages.ForwardClientBatch;
import lsr.paxos.replica.ClientBatchID;
import lsr.paxos.replica.ClientBatchManager;
/**
* Singleton class. Why? As it would be utterly wrong to have two such objects.
*/
public class ClientBatchStore {
public static ClientBatchStore instance = __init();
private static ClientBatchStore __init() {
if (CrashModel.FullSS.equals(ProcessDescriptor.processDescriptor.crashModel))
return new SynchronousClientBatchStore();
else
return new ClientBatchStore();
}
protected ClientBatchStore() {
assert ProcessDescriptor.processDescriptor.indirectConsensus;
// nop is rare, better put it on the map than check at each search
batches.put(ClientBatchID.NOP, new ClientRequest[0]);
}
protected final HashMap<ClientBatchID, ClientRequest[]> batches = new HashMap<ClientBatchID, ClientRequest[]>();
protected final HashSet<ClientBatchID> batchesWaitedFor = new HashSet<ClientBatchID>();
// TODO: garbage collect not instanced batches on followers
protected final HashSet<ClientBatchID> instancelessBatches = new HashSet<ClientBatchID>();
protected ClientBatchManager clientBatchManager = null;
public synchronized ClientRequest[] getBatch(ClientBatchID batchId) {
return batches.get(batchId);
}
/**
* Maps batch ID -> instance and returns if the value is already available
*/
public synchronized void associateWithInstance(ClientBatchID batchId) {
if (batches.containsKey(batchId)) {
instancelessBatches.remove(batchId);
return;
}
batchesWaitedFor.add(batchId);
}
public synchronized void setBatch(final ClientBatchID batchId, ClientRequest[] value) {
batches.put(batchId, value);
if (!batchesWaitedFor.remove(batchId))
instancelessBatches.add(batchId);
}
public synchronized void setBatch(ForwardClientBatch fReq) {
setBatch(fReq.rid, fReq.requests);
}
public synchronized boolean isAnyInstanceWaiting(ClientBatchID batchId) {
return batchesWaitedFor.contains(batchId);
}
public ClientBatchManager getClientBatchManager() {
return clientBatchManager;
}
/**
* Delivers batch manager for easy access to it
*/
public void setClientBatchManager(ClientBatchManager clientBatchManager) {
assert clientBatchManager != null;
assert this.clientBatchManager == null;
this.clientBatchManager = clientBatchManager;
}
/**
* Get batch ID's of available batches that are not yet associated with any
* instance
*
* Returns cloned object.
*/
@SuppressWarnings("unchecked")
public synchronized HashSet<ClientBatchID> getInstancelessBatches() {
return (HashSet<ClientBatchID>) instancelessBatches.clone();
}
public synchronized boolean hasAllBatches(Collection<ClientBatchID> cbids) {
for (ClientBatchID cbid : cbids) {
if (!batches.containsKey(cbid))
return false;
}
return true;
}
public synchronized void removeBatches(Collection<ClientBatchID> cbids) {
for (ClientBatchID cbid : cbids)
batches.remove(cbid);
// after updating to a snapshot it may happen
batchesWaitedFor.removeAll(cbids);
instancelessBatches.removeAll(cbids);
if (clientBatchManager != null) {
clientBatchManager.removeBatches(cbids);
}
}
}