Search in sources :

Example 6 with RangeResponse

use of com.ibm.etcd.api.RangeResponse 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 KeyValue}s of this cache
 */
public Iterator<KeyValue> strongIterator() {
    long seenUpTo = seenUpToRev.get();
    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.get();
            } 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)
    TxnResponse txnResp;
    try {
        txnResp = kvClient.batch().get(newModsReq).get(curCountReq).get(seenCountReq).timeout(8000L).sync();
    } 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 = kvClient.batch().get(newModsReq).get(otherKeysReq).timeout(60_000L).sync();
    }
    long revNow = txnResp.getHeader().getRevision();
    if (revNow > seenUpToRev.get()) {
        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.get()) {
            listenerExecutor.execute(() -> revisionUpdate(revNow));
        }
    }
    return iterator();
}
Also used : 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) MoreExecutors(com.google.common.util.concurrent.MoreExecutors) 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) Nullable(org.checkerframework.checker.nullness.qual.Nullable) 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) Code(io.grpc.Status.Code) RangeResponse(com.ibm.etcd.api.RangeResponse) DeleteRangeResponse(com.ibm.etcd.api.DeleteRangeResponse) RangeRequest(com.ibm.etcd.api.RangeRequest) DeleteRangeRequest(com.ibm.etcd.api.DeleteRangeRequest) TxnResponse(com.ibm.etcd.api.TxnResponse) ExecutionException(java.util.concurrent.ExecutionException) TimeoutException(java.util.concurrent.TimeoutException)

Example 7 with RangeResponse

use of com.ibm.etcd.api.RangeResponse 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;
            }
            assert startFuture == promise;
            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) MoreExecutors(com.google.common.util.concurrent.MoreExecutors) 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) Nullable(org.checkerframework.checker.nullness.qual.Nullable) 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)

Example 8 with RangeResponse

use of com.ibm.etcd.api.RangeResponse in project etcd-java by IBM.

the class KvTest method testKvOps.

@Test
public void testKvOps() throws Exception {
    proxy.start();
    try (KvStoreClient directClient = EtcdClient.forEndpoint("localhost", 2379).withPlainText().build();
        KvStoreClient client = EtcdClient.forEndpoint("localhost", 2391).withPlainText().build()) {
        KvClient kvc = client.getKvClient();
        ByteString a = bs("a"), b = bs("b"), v1 = bs("v1"), v2 = bs("v2");
        // clean up first
        kvc.batch().delete(kvc.delete(a).asRequest()).delete(kvc.delete(b).asRequest()).sync();
        // basic put
        assertEquals(KeyValue.getDefaultInstance(), kvc.put(a, v2).prevKv().sync().getPrevKv());
        assertTrue(kvc.put(a, v2).sync().getHeader().getRevision() > 0);
        assertEquals(v2, kvc.put(a, v1).prevKv().sync().getPrevKv().getValue());
        // put with non-existent leaseId
        try {
            kvc.put(b, v1, 12345L).sync();
            fail("lease should not exist");
        } catch (StatusRuntimeException sre) {
            assertEquals(Code.NOT_FOUND, sre.getStatus().getCode());
        }
        // basic get
        RangeResponse rr = kvc.get(bs("a")).sync();
        assertEquals(1L, rr.getCount());
        assertEquals(v1, rr.getKvs(0).getValue());
        // basic delete
        assertEquals(0L, kvc.delete(bs("notthere")).sync().getDeleted());
        assertEquals(v1, kvc.delete(a).prevKv().sync().getPrevKvs(0).getValue());
        assertEquals(0, kvc.get(bs("a")).sync().getCount());
        PutRequest pr1 = kvc.put(a, v1).asRequest(), pr2 = kvc.put(b, v2).asRequest();
        // batch put
        assertEquals(2, kvc.batch().put(pr1).put(pr2).sync().getResponsesCount());
        assertEquals(v1, kvc.get(a).sync().getKvs(0).getValue());
        assertEquals(v2, kvc.get(b).sync().getKvs(0).getValue());
        // basic transaction
        ListenableFuture<TxnResponse> tresp = kvc.txnIf().cmpEqual(a).value(v1).and().cmpNotEqual(b).version(10).then().put(kvc.put(bs("new"), bs("newval")).asRequest()).async();
        assertNotNull(tresp.get().getResponses(0).getResponsePut().getHeader());
        // test disconnected behaviour
        proxy.kill();
        Thread.sleep(200L);
        // should fail
        ListenableFuture<RangeResponse> rrFut1 = kvc.get(bs("new")).async();
        ListenableFuture<RangeResponse> rrFut2 = kvc.get(bs("new")).deadline(Deadline.after(20, TimeUnit.SECONDS)).backoffRetry().async();
        try {
            rrFut1.get(1000, TimeUnit.SECONDS);
            fail("expected get to fail while disconnected");
        } catch (Exception e) {
            // TODO
            System.out.println("failed with: " + e);
        }
        // this one should still be retrying
        assertFalse(rrFut2.isDone());
        // reconnect
        proxy.start();
        // should succeed once network path is there again
        long before = System.nanoTime();
        RangeResponse rr2 = rrFut2.get(2000, TimeUnit.SECONDS);
        long took = (System.nanoTime() - before) / 1000_000L;
        assertEquals(bs("newval"), rr2.getKvs(0).getValue());
        System.out.println("took " + took + "ms after network was reestablished");
    } finally {
        proxy.close();
    }
}
Also used : RangeResponse(com.ibm.etcd.api.RangeResponse) ByteString(com.google.protobuf.ByteString) StatusRuntimeException(io.grpc.StatusRuntimeException) KvClient(com.ibm.etcd.client.kv.KvClient) PutRequest(com.ibm.etcd.api.PutRequest) TxnResponse(com.ibm.etcd.api.TxnResponse) StatusRuntimeException(io.grpc.StatusRuntimeException) Test(org.junit.Test)

Example 9 with RangeResponse

use of com.ibm.etcd.api.RangeResponse in project SORMAS-Project by hzi-braunschweig.

the class EtcdCentralClient method get.

public <T> T get(String key, Class<T> clazz) throws IOException {
    // use resource auto-close
    try (KvStoreClient etcdClient = createEtcdClient()) {
        if (etcdClient == null) {
            LOGGER.error("Etcd could not be accessed.");
            return null;
        }
        KvClient etcd = etcdClient.getKvClient();
        if (etcd == null) {
            LOGGER.error("Could not create an etcd KV client.");
            return null;
        }
        RangeResponse range = etcd.get(ByteString.copyFromUtf8(key)).sync();
        if (range.getCount() == 0) {
            LOGGER.error("There is no value available for key {}", key);
            return null;
        }
        return deserialize(range.getKvs(0), clazz);
    }
}
Also used : KvStoreClient(com.ibm.etcd.client.KvStoreClient) RangeResponse(com.ibm.etcd.api.RangeResponse) KvClient(com.ibm.etcd.client.kv.KvClient)

Aggregations

RangeResponse (com.ibm.etcd.api.RangeResponse)9 DeleteRangeResponse (com.ibm.etcd.api.DeleteRangeResponse)6 KvClient (com.ibm.etcd.client.kv.KvClient)6 ByteString (com.google.protobuf.ByteString)5 KeyValue (com.ibm.etcd.api.KeyValue)5 TxnResponse (com.ibm.etcd.api.TxnResponse)5 PutRequest (com.ibm.etcd.api.PutRequest)4 TxnRequest (com.ibm.etcd.api.TxnRequest)4 Iterators (com.google.common.collect.Iterators)3 AbstractFuture (com.google.common.util.concurrent.AbstractFuture)3 Futures (com.google.common.util.concurrent.Futures)3 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)3 MoreExecutors (com.google.common.util.concurrent.MoreExecutors)3 MoreExecutors.directExecutor (com.google.common.util.concurrent.MoreExecutors.directExecutor)3 Compare (com.ibm.etcd.api.Compare)3 CompareResult (com.ibm.etcd.api.Compare.CompareResult)3 CompareTarget (com.ibm.etcd.api.Compare.CompareTarget)3 CompareOrBuilder (com.ibm.etcd.api.CompareOrBuilder)3 DeleteRangeRequest (com.ibm.etcd.api.DeleteRangeRequest)3 Event (com.ibm.etcd.api.Event)3