{
final long startTime = System.currentTimeMillis();
// First things first: find Server nodes to talk to:
NodesForKey nodes = _clusterView.getNodesFor(key);
PutOperationResult result = new PutOperationResult(config.getOperationConfig());
// One sanity check: if not enough server nodes to talk to, can't succeed...
int nodeCount = nodes.size();
// should this actually result in an exception?
if (nodeCount < config.getOperationConfig().getMinimalOksToSucceed()) {
return result;
}
// Then figure out how long we have for the whole operation
final long endOfTime = startTime + config.getOperationConfig().getGetOperationTimeoutMsecs();
final long lastValidTime = endOfTime - config.getCallConfig().getMinimumTimeoutMsecs();
/* Ok: first round; try PUT into every enabled store, up to optimal number
* of successes we expect.
*/
final boolean noRetries = !allowRetries();
List<NodeFailure> retries = null;
for (int i = 0; i < nodeCount; ++i) {
ClusterServerNode server = nodes.node(i);
if (server.isDisabled() && !noRetries) { // can skip disabled, iff retries allowed
break;
}
CallFailure fail = server.entryPutter().tryPut(config.getCallConfig(), endOfTime, key, content);
if (fail != null) { // only add to retry-list if something retry may help with
if (fail.isRetriable()) {
retries = _add(retries, new NodeFailure(server, fail));
} else {
result.addFailed(new NodeFailure(server, fail));
}
continue;
}
result.addSucceeded(server);
// Very first round: go up to max if it's smooth sailing!
if (result.succeededMaximally()) {
return result.addFailed(retries);
}
}
if (noRetries) { // if we can't retry, don't:
return result.addFailed(retries);
}
// If we got this far, let's accept sub-optimal outcomes as well; or, if we timed out
final long secondRoundStart = System.currentTimeMillis();
if (result.succeededMinimally() || secondRoundStart >= lastValidTime) {
return result.addFailed(retries);
}
// Do we need any delay in between?
_doDelay(startTime, secondRoundStart, endOfTime);
// Otherwise: go over retry list first, and if that's not enough, try disabled
if (retries == null) {
retries = new LinkedList<NodeFailure>();
} else {
Iterator<NodeFailure> it = retries.iterator();
while (it.hasNext()) {
NodeFailure retry = it.next();
ClusterServerNode server = (ClusterServerNode) retry.getServer();
CallFailure fail = server.entryPutter().tryPut(config.getCallConfig(), endOfTime, key, content);
if (fail != null) {
retry.addFailure(fail);
if (!fail.isRetriable()) { // not worth retrying?
result.addFailed(retry);
it.remove();
}
} else {
it.remove(); // remove now from retry list
result.addSucceeded(server);
if (result.succeededOptimally()) {
return result.addFailed(retries);
}
}
}
}
// if no success, add disabled nodes in the mix; but only if we don't have minimal success:
for (int i = 0; i < nodeCount; ++i) {
if (result.succeededMinimally() || System.currentTimeMillis() >= lastValidTime) {
return result.addFailed(retries);
}
ClusterServerNode server = nodes.node(i);
if (server.isDisabled()) {
CallFailure fail = server.entryPutter().tryPut(config.getCallConfig(), endOfTime, key, content);
if (fail != null) {
if (fail.isRetriable()) {
retries.add(new NodeFailure(server, fail));
} else {
result.addFailed(new NodeFailure(server, fail));
}
} else {
result.addSucceeded(server);
}
}
}
// But from now on, keep on retrying, up to... N times (start with 1, as we did first retry)
long prevStartTime = secondRoundStart;
for (int i = 1; (i <= MAX_RETRIES_FOR_PUT) && !retries.isEmpty(); ++i) {
final long currStartTime = System.currentTimeMillis();
_doDelay(prevStartTime, currStartTime, endOfTime);
// and off we go again...
Iterator<NodeFailure> it = retries.iterator();
while (it.hasNext()) {
if (result.succeededMinimally() || System.currentTimeMillis() >= lastValidTime) {
return result.addFailed(retries);
}
NodeFailure retry = it.next();
ClusterServerNode server = (ClusterServerNode) retry.getServer();
CallFailure fail = server.entryPutter().tryPut(config.getCallConfig(), endOfTime, key, content);
if (fail != null) {
retry.addFailure(fail);
if (!fail.isRetriable()) {
result.addFailed(retry);
it.remove();
}
} else {
result.addSucceeded(server);
}
}
prevStartTime = currStartTime;
}
// we are all done, failed:
return result.addFailed(retries);
}