use of com.ibm.etcd.client.kv.KvClient.FluentWatchRequest in project etcd-java by IBM.
the class RangeCache method setupWatch.
// called only from listenerExecutor context
private void setupWatch(List<RangeResponse> rrs, boolean firstTime, SettableFuture<Boolean> promise) {
if (closed) {
throw new CancellationException();
}
Set<ByteString> snapshot = firstTime && entries.isEmpty() ? null : new HashSet<>();
RangeResponse toUpdate = rrs.get(0);
if (toUpdate.getKvsCount() > 0) {
for (KeyValue kv : toUpdate.getKvsList()) {
if (snapshot != null) {
snapshot.add(kv.getKey());
}
offerUpdate(kv, true);
}
}
long snapshotRev = toUpdate.getHeader().getRevision();
if (firstTime) {
notifyListeners(EventType.INITIALIZED, null, true);
}
if (snapshot != null) {
if (rrs.size() > 1) {
for (KeyValue kv : rrs.get(1).getKvsList()) {
snapshot.add(kv.getKey());
}
}
// prune deleted entries
KeyValue.Builder kvBld = null;
for (ByteString key : entries.keySet()) {
if (!snapshot.contains(key)) {
if (kvBld == null) {
kvBld = KeyValue.newBuilder().setVersion(0L).setModRevision(snapshotRev);
}
offerUpdate(kvBld.setKey(key).build(), true);
}
}
}
revisionUpdate(snapshotRev);
StreamObserver<WatchUpdate> watchObserver = new StreamObserver<WatchUpdate>() {
@Override
public void onNext(WatchUpdate update) {
List<Event> events = update.getEvents();
int eventCount = events != null ? events.size() : 0;
if (eventCount > 0) {
for (Event event : events) {
KeyValue kv = event.getKv();
// event.getPrevKv(); //TBD
switch(event.getType()) {
case DELETE:
if (kv.getVersion() != 0L) {
kv = KeyValue.newBuilder(kv).setVersion(0L).clearValue().build();
}
// fall-thru
case PUT:
offerUpdate(kv, true);
break;
case UNRECOGNIZED:
default:
logger.warn("Unrecognized event for key " + kv.getKey().toStringUtf8());
break;
}
}
}
revisionUpdate(eventCount == 0 ? update.getHeader().getRevision() - 1L : events.get(eventCount - 1).getKv().getModRevision());
}
@Override
public void onCompleted() {
// should only happen after external close()
if (!closed) {
if (!client.isClosed()) {
logger.warn("Watch completed unexpectedly (not closed) (fromKey = " + fromKey.toStringUtf8() + ")");
}
close();
}
}
@Override
public void onError(Throwable t) {
if (closed) {
promise.setException(new CancellationException());
return;
}
boolean isDone = promise.isDone();
if (isDone && promise.isCancelled()) {
return;
}
if (!(t instanceof RevisionCompactedException)) {
logger.error("Watch failed with exception (fromKey = " + fromKey.toStringUtf8() + ")", t);
promise.setException(t);
return;
}
// Refresh the cache, which will renew the watch
ListenableFuture<Boolean> refresh;
if (isDone) {
refresh = fullRefreshCache();
} else {
// If this is a watch creation failure, delay the attempt for 1 second
refresh = Futures.scheduleAsync(RangeCache.this::fullRefreshCache, 1L, TimeUnit.SECONDS, client.internalScheduledExecutor());
if (promise.setFuture(refresh)) {
refresh = null;
}
}
synchronized (RangeCache.this) {
if (!closed) {
if (refresh != null) {
startFuture = refresh;
refresh = null;
}
watch = null;
}
}
if (refresh == null) {
logger.warn("Performing full refresh (fromKey = " + fromKey.toStringUtf8() + ") following watch compaction error: " + t);
} else {
assert closed;
refresh.cancel(false);
}
}
};
FluentWatchRequest watchRequest = // .prevKv() //TODO TBD
kvClient.watch(fromKey).rangeEnd(toKey).progressNotify().startRevision(snapshotRev + 1).executor(listenerExecutor);
Watch newWatch;
synchronized (RangeCache.this) {
if (closed) {
throw new CancellationException();
}
if (promise.isCancelled()) {
return;
}
watch = newWatch = watchRequest.start(watchObserver);
}
Futures.addCallback(newWatch, (FutureListener<Boolean>) (v, t) -> {
if (t != null && !newWatch.isCancelled()) {
// Error cases are handled by onError above
return;
}
if (!Boolean.TRUE.equals(v) && closed) {
promise.setException(new CancellationException());
} else {
promise.set(v);
}
}, directExecutor());
}
Aggregations