use of com.ibm.etcd.api.TxnResponse in project etcd-java by IBM.
the class RangeCache method strongIterator.
/**
* Iterator whose contents is guaranteed to be sequentially consistent
* with remote updates to the cached range.
*
* @return an {@link Iterator} over the {@link KeyValues} of this cache
*/
public Iterator<KeyValue> strongIterator() {
// memory barrier prior to reading seenUpToRev
entries.get(fromKey);
long seenUpTo = seenUpToRev;
if (seenUpTo == 0L) {
ListenableFuture<Boolean> startFut;
synchronized (this) {
startFut = startFuture;
}
if (startFut == null) {
// cache has not yet been started
return kvClient.get(fromKey).rangeEnd(toKey).timeout(120_000L).sync().getKvsList().iterator();
} else
try {
startFut.get(2L, TimeUnit.MINUTES);
// now started
seenUpTo = seenUpToRev;
} catch (TimeoutException te) {
throw Status.DEADLINE_EXCEEDED.asRuntimeException();
} catch (ExecutionException e) {
throw Status.UNKNOWN.withCause(e).asRuntimeException();
} catch (InterruptedException | CancellationException e) {
throw Status.CANCELLED.withCause(e).asRuntimeException();
}
}
/*
* This logic is similar to that in fullRefreshCache(), but
* it includes an optimistic initial comparison of counts
* to identify cases where no deletions have been missed and
* thus a retrieval of all the keys isn't required.
*/
RangeRequest.Builder rangeReqBld = RangeRequest.newBuilder().setKey(fromKey).setRangeEnd(toKey);
RangeRequest curCountReq = rangeReqBld.setCountOnly(true).setMaxCreateRevision(seenUpTo).build();
RangeRequest seenCountReq = rangeReqBld.clearMaxCreateRevision().setRevision(seenUpTo).build();
RangeRequest newModsReq = rangeReqBld.clearRevision().clearCountOnly().setMinModRevision(seenUpTo + 1).build();
// first, attempt to get:
// 0- kvs modified since seenUpTo
// 1- current count excluding those created since seenUpTo
// 2- count at revision seenUpTo (this could potentially
// fail with compaction error, see below)
ListenableFuture<TxnResponse> txn = kvClient.batch().get(newModsReq).get(curCountReq).get(seenCountReq).async();
TxnResponse txnResp;
try {
txnResp = waitFor(txn, 8000L);
} catch (RuntimeException e) {
Code code = Status.fromThrowable(e).getCode();
if (code != Code.OUT_OF_RANGE)
throw e;
// if (2) above fails due to compaction, also retrieve all current keys
RangeRequest otherKeysReq = rangeReqBld.clearMinModRevision().setMaxModRevision(seenUpTo).setKeysOnly(true).build();
txnResp = waitFor(kvClient.batch().get(newModsReq).get(otherKeysReq).async(), // longer timeout
60_000L);
}
long revNow = txnResp.getHeader().getRevision();
if (revNow > seenUpToRev) {
RangeResponse newModKvs = txnResp.getResponses(0).getResponseRange();
List<KeyValue> otherKeys;
if (txnResp.getResponsesCount() == 2) {
// this means we must have taken the compacted exception path above
otherKeys = txnResp.getResponses(1).getResponseRange().getKvsList();
} else if (// <- latest count
txnResp.getResponses(1).getResponseRange().getCount() < txnResp.getResponses(2).getResponseRange().getCount()) {
// <- count at seenUpTo
// if counts don't match, there must have been deletions since seenUpTo,
// so additionally retrieve all current keys
RangeRequest otherKeysReq = rangeReqBld.clearMinModRevision().setMaxModRevision(seenUpTo).setKeysOnly(true).build();
// longer timeout
otherKeys = waitFor(kvClient.get(otherKeysReq), 60_000L).getKvsList();
} else
otherKeys = null;
boolean newKvs = newModKvs.getKvsCount() > 0;
if (otherKeys != null) {
// if this is true, there *might* be deletions to process
if (otherKeys.isEmpty() && !newKvs)
return Collections.emptyIterator();
// bring cache up to date with recently deleted kvs
Set<ByteString> keys = Stream.concat(otherKeys.stream(), newModKvs.getKvsList().stream()).map(kv -> kv.getKey()).collect(Collectors.toSet());
entries.values().stream().filter(kv -> kv.getModRevision() < revNow && !keys.contains(kv.getKey())).forEach(kv -> offerDelete(kv.getKey(), revNow));
}
// bring cache up to date with recently modified kvs
if (newKvs)
newModKvs.getKvsList().forEach(kv -> offerUpdate(kv, false));
if (revNow > seenUpToRev)
listenerExecutor.execute(() -> revisionUpdate(revNow));
}
return iterator();
}
Aggregations