Search in sources :

Example 1 with FluentWatchRequest

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());
}
Also used : StreamObserver(io.grpc.stub.StreamObserver) FluentWatchRequest(com.ibm.etcd.client.kv.KvClient.FluentWatchRequest) EtcdClient(com.ibm.etcd.client.EtcdClient) CompareResult(com.ibm.etcd.api.Compare.CompareResult) LoggerFactory(org.slf4j.LoggerFactory) TimeoutException(java.util.concurrent.TimeoutException) RevisionCompactedException(com.ibm.etcd.client.watch.RevisionCompactedException) ResponseOp(com.ibm.etcd.api.ResponseOp) StreamObserver(io.grpc.stub.StreamObserver) Compare(com.ibm.etcd.api.Compare) Map(java.util.Map) Status(io.grpc.Status) RangeResponse(com.ibm.etcd.api.RangeResponse) CancellationException(java.util.concurrent.CancellationException) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) EventType(com.ibm.etcd.client.utils.RangeCache.Listener.EventType) Set(java.util.Set) NavigableSet(java.util.NavigableSet) NavigableMap(java.util.NavigableMap) GuardedBy(javax.annotation.concurrent.GuardedBy) Collectors(java.util.stream.Collectors) RequestOp(com.ibm.etcd.api.RequestOp) MoreExecutors.directExecutor(com.google.common.util.concurrent.MoreExecutors.directExecutor) ByteString(com.google.protobuf.ByteString) CompareOrBuilder(com.ibm.etcd.api.CompareOrBuilder) List(java.util.List) Stream(java.util.stream.Stream) GrpcClient.waitFor(com.ibm.etcd.client.GrpcClient.waitFor) DeleteRangeResponse(com.ibm.etcd.api.DeleteRangeResponse) RangeRequest(com.ibm.etcd.api.RangeRequest) Event(com.ibm.etcd.api.Event) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) GrpcClient(com.ibm.etcd.client.GrpcClient) WatchUpdate(com.ibm.etcd.client.kv.WatchUpdate) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) Watch(com.ibm.etcd.client.kv.KvClient.Watch) TxnResponse(com.ibm.etcd.api.TxnResponse) Iterators(com.google.common.collect.Iterators) ConcurrentMap(java.util.concurrent.ConcurrentMap) HashSet(java.util.HashSet) DeleteRangeRequest(com.ibm.etcd.api.DeleteRangeRequest) Code(io.grpc.Status.Code) KeyUtils(com.ibm.etcd.client.KeyUtils) PutRequest(com.ibm.etcd.api.PutRequest) Logger(org.slf4j.Logger) Iterator(java.util.Iterator) Executor(java.util.concurrent.Executor) FutureListener(com.ibm.etcd.client.FutureListener) KvClient(com.ibm.etcd.client.kv.KvClient) KeyValue(com.ibm.etcd.api.KeyValue) TxnRequest(com.ibm.etcd.api.TxnRequest) ExecutionException(java.util.concurrent.ExecutionException) TimeUnit(java.util.concurrent.TimeUnit) Consumer(java.util.function.Consumer) AtomicLong(java.util.concurrent.atomic.AtomicLong) ConcurrentSkipListMap(java.util.concurrent.ConcurrentSkipListMap) Futures(com.google.common.util.concurrent.Futures) ConcurrentSkipListSet(java.util.concurrent.ConcurrentSkipListSet) Collections(java.util.Collections) AbstractFuture(com.google.common.util.concurrent.AbstractFuture) CompareTarget(com.ibm.etcd.api.Compare.CompareTarget) KeyValue(com.ibm.etcd.api.KeyValue) ByteString(com.google.protobuf.ByteString) FluentWatchRequest(com.ibm.etcd.client.kv.KvClient.FluentWatchRequest) WatchUpdate(com.ibm.etcd.client.kv.WatchUpdate) RangeResponse(com.ibm.etcd.api.RangeResponse) DeleteRangeResponse(com.ibm.etcd.api.DeleteRangeResponse) RevisionCompactedException(com.ibm.etcd.client.watch.RevisionCompactedException) CancellationException(java.util.concurrent.CancellationException) Watch(com.ibm.etcd.client.kv.KvClient.Watch) Event(com.ibm.etcd.api.Event)

Aggregations

Iterators (com.google.common.collect.Iterators)1 AbstractFuture (com.google.common.util.concurrent.AbstractFuture)1 Futures (com.google.common.util.concurrent.Futures)1 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)1 MoreExecutors.directExecutor (com.google.common.util.concurrent.MoreExecutors.directExecutor)1 ByteString (com.google.protobuf.ByteString)1 Compare (com.ibm.etcd.api.Compare)1 CompareResult (com.ibm.etcd.api.Compare.CompareResult)1 CompareTarget (com.ibm.etcd.api.Compare.CompareTarget)1 CompareOrBuilder (com.ibm.etcd.api.CompareOrBuilder)1 DeleteRangeRequest (com.ibm.etcd.api.DeleteRangeRequest)1 DeleteRangeResponse (com.ibm.etcd.api.DeleteRangeResponse)1 Event (com.ibm.etcd.api.Event)1 KeyValue (com.ibm.etcd.api.KeyValue)1 PutRequest (com.ibm.etcd.api.PutRequest)1 RangeRequest (com.ibm.etcd.api.RangeRequest)1 RangeResponse (com.ibm.etcd.api.RangeResponse)1 RequestOp (com.ibm.etcd.api.RequestOp)1 ResponseOp (com.ibm.etcd.api.ResponseOp)1 TxnRequest (com.ibm.etcd.api.TxnRequest)1