Search in sources :

Example 1 with Watch

use of com.ibm.etcd.client.kv.KvClient.Watch in project etcd-java by IBM.

the class WatchTest method testWatch.

@Test
public void testWatch() throws Exception {
    proxy.start();
    try (KvStoreClient directClient = EtcdClient.forEndpoint("localhost", 2379).withPlainText().build();
        KvStoreClient client = EtcdClient.forEndpoint("localhost", 2390).withPlainText().build()) {
        KvClient kvc = client.getKvClient();
        long start = System.currentTimeMillis();
        final BlockingQueue<Object> watchEvents = new LinkedBlockingQueue<>();
        final Object COMPLETED = new Object();
        final StreamObserver<WatchUpdate> observer = new StreamObserver<WatchUpdate>() {

            @Override
            public void onNext(WatchUpdate value) {
                System.out.println(t(start) + "watch event: " + value);
                watchEvents.add(value);
            }

            @Override
            public void onError(Throwable t) {
                System.out.println(t(start) + "watch error: " + t);
                watchEvents.add(t);
            }

            @Override
            public void onCompleted() {
                System.out.println(t(start) + "watch completed");
                watchEvents.add(COMPLETED);
            }
        };
        Watch watch = kvc.watch(bs("/watchtest")).asPrefix().start(observer);
        // assertFalse(watch.isDone());
        // test blocking watch at the same time
        WatchIterator watchIterator = kvc.watch(bs("/watchtest")).asPrefix().start();
        assertTrue(watch.get(1, TimeUnit.SECONDS));
        kvc.put(bs("/watchtest/a"), bs("a value")).sync();
        directClient.getKvClient().put(bs("/watchtest/b"), bs("b value")).sync();
        WatchUpdate wu = getNextSkipEmpty(watchEvents);
        assertNotNull(wu);
        assertEquals("event: " + wu, 1, wu.getEvents().size());
        assertEquals(EventType.PUT, wu.getEvents().get(0).getType());
        assertEquals(bs("a value"), wu.getEvents().get(0).getKv().getValue());
        assertEquals(bs("/watchtest/a"), wu.getEvents().get(0).getKv().getKey());
        WatchUpdate wu2 = getNextSkipEmpty(watchIterator);
        assertEquals(EventType.PUT, wu2.getEvents().get(0).getType());
        assertEquals(bs("a value"), wu2.getEvents().get(0).getKv().getValue());
        assertEquals(bs("/watchtest/a"), wu2.getEvents().get(0).getKv().getKey());
        watchEvents.poll(500, TimeUnit.MILLISECONDS);
        watch.close();
        assertEquals(COMPLETED, watchEvents.poll(500, TimeUnit.MILLISECONDS));
        kvc.put(bs("/watchtest/c"), bs("c value")).sync();
        assertNull(watchEvents.poll(500, TimeUnit.MILLISECONDS));
        assertTrue(watchIterator.hasNext());
        assertTrue(watchIterator.hasNext());
        assertEquals(bs("b value"), watchIterator.next().getEvents().get(0).getKv().getValue());
        assertEquals(EventType.PUT, watchIterator.next().getEvents().get(0).getType());
        // fresh new watch
        watch = kvc.watch(ByteString.copyFromUtf8("/watchtest")).asPrefix().start(observer);
        assertTrue(watch.get(1, TimeUnit.SECONDS));
        kvc.batch().put(kvc.put(bs("/watchtest/d"), bs("d value")).asRequest()).delete(kvc.delete(bs("/watchtest/a")).asRequest()).sync();
        wu = getNextSkipEmpty(watchEvents);
        assertEquals(2, wu.getEvents().size());
        assertTrue(wu.getEvents().stream().anyMatch(e -> e.getType() == EventType.DELETE));
        assertTrue(wu.getEvents().stream().anyMatch(e -> e.getType() == EventType.PUT));
        // kill path to server
        proxy.kill();
        Thread.sleep(1000L);
        // while disconnected, put a new key
        directClient.getKvClient().put(bs("/watchtest/e"), bs("e value")).sync();
        Thread.sleep(1000L);
        // reinstate path to server
        proxy.start();
        // watch should be unaffected - next event seen should be the missed one
        wu = (WatchUpdate) watchEvents.poll(3000L, TimeUnit.MILLISECONDS);
        assertEquals(bs("/watchtest/e"), wu.getEvents().get(0).getKv().getKey());
        watch.close();
        // (earlier batch update)
        assertEquals(2, watchIterator.next().getEvents().size());
        assertEquals(bs("/watchtest/e"), watchIterator.next().getEvents().get(0).getKv().getKey());
        watchIterator.close();
        assertNull(watchIterator.next().getHeader());
        assertFalse(watchIterator.hasNext());
        try {
            watchIterator.next();
            fail("should throw NSEE here");
        } catch (NoSuchElementException nsee) {
        }
    } finally {
        proxy.kill();
    }
}
Also used : StreamObserver(io.grpc.stub.StreamObserver) AfterClass(org.junit.AfterClass) WatchUpdate(com.ibm.etcd.client.kv.WatchUpdate) BeforeClass(org.junit.BeforeClass) Watch(com.ibm.etcd.client.kv.KvClient.Watch) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) KvClient(com.ibm.etcd.client.kv.KvClient) BlockingQueue(java.util.concurrent.BlockingQueue) Test(org.junit.Test) WatchIterator(com.ibm.etcd.client.kv.KvClient.WatchIterator) LinkedBlockingQueue(java.util.concurrent.LinkedBlockingQueue) AtomicReference(java.util.concurrent.atomic.AtomicReference) WatchCreateException(com.ibm.etcd.client.watch.WatchCreateException) ExecutionException(java.util.concurrent.ExecutionException) TimeUnit(java.util.concurrent.TimeUnit) ByteString(com.google.protobuf.ByteString) StreamObserver(io.grpc.stub.StreamObserver) Map(java.util.Map) Phaser(java.util.concurrent.Phaser) EventType(com.ibm.etcd.api.Event.EventType) KvTest.bs(com.ibm.etcd.client.KvTest.bs) Assert(org.junit.Assert) NoSuchElementException(java.util.NoSuchElementException) PutResponse(com.ibm.etcd.api.PutResponse) KvTest.t(com.ibm.etcd.client.KvTest.t) WatchUpdate(com.ibm.etcd.client.kv.WatchUpdate) LinkedBlockingQueue(java.util.concurrent.LinkedBlockingQueue) Watch(com.ibm.etcd.client.kv.KvClient.Watch) KvClient(com.ibm.etcd.client.kv.KvClient) NoSuchElementException(java.util.NoSuchElementException) WatchIterator(com.ibm.etcd.client.kv.KvClient.WatchIterator) Test(org.junit.Test)

Example 2 with Watch

use of com.ibm.etcd.client.kv.KvClient.Watch in project etcd-java by IBM.

the class RangeCache method fullRefreshCache.

// internal method - should not be called while watch is active
protected ListenableFuture<Boolean> fullRefreshCache() {
    ListenableFuture<List<RangeResponse>> rrfut;
    long seenUpTo = seenUpToRev;
    boolean firstTime = (seenUpTo == 0L);
    if (firstTime || entries.size() <= 20) {
        // TODO *maybe* chunking (for large caches)
        ListenableFuture<RangeResponse> rrf = kvClient.get(fromKey).rangeEnd(toKey).backoffRetry(() -> !closed).timeout(300_000L).async();
        rrfut = Futures.transform(rrf, (Function<RangeResponse, List<RangeResponse>>) rr -> Collections.singletonList(rr));
    } else {
        // in case the local cache is large, reduce data transfer by requesting
        // just keys, and full key+value only for those modified since seenUpToRev
        RangeRequest.Builder rangeReqBld = RangeRequest.newBuilder().setKey(fromKey).setRangeEnd(toKey);
        RangeRequest newModsReq = rangeReqBld.setMinModRevision(seenUpTo + 1).build();
        RangeRequest otherKeysReq = rangeReqBld.clearMinModRevision().setMaxModRevision(seenUpTo).setKeysOnly(true).build();
        ListenableFuture<TxnResponse> trf = kvClient.batch().get(newModsReq).get(otherKeysReq).backoffRetry(() -> !closed).timeout(300_000L).async();
        rrfut = Futures.transform(trf, (Function<TxnResponse, List<RangeResponse>>) tr -> tr.getResponsesList().stream().map(r -> r.getResponseRange()).collect(Collectors.toList()));
    }
    return Futures.transformAsync(rrfut, rrs -> {
        if (closed)
            throw new CancellationException();
        Set<ByteString> snapshot = firstTime ? null : new HashSet<>();
        RangeResponse toUpdate = rrs.get(0);
        if (toUpdate.getKvsCount() > 0)
            for (KeyValue kv : toUpdate.getKvsList()) {
                if (!firstTime)
                    snapshot.add(kv.getKey());
                offerUpdate(kv, true);
            }
        long snapshotRev = toUpdate.getHeader().getRevision();
        if (firstTime)
            notifyListeners(EventType.INITIALIZED, null, true);
        else {
            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);
        Watch newWatch = // .prevKv() //TODO TBD
        kvClient.watch(fromKey).rangeEnd(toKey).progressNotify().startRevision(snapshotRev + 1).executor(listenerExecutor).start(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.error("Watch completed unexpectedly (not closed)");
                    }
                    close();
                }
            }

            @Override
            public void onError(Throwable t) {
                logger.error("Watch failed with exception ", t);
                if (t instanceof RevisionCompactedException)
                    synchronized (RangeCache.this) {
                        // fail if happens during start, otherwise refresh
                        if (!closed && startFuture != null && startFuture.isDone()) {
                            // will renew watch
                            startFuture = fullRefreshCache();
                        }
                    }
            }
        });
        synchronized (this) {
            if (closed)
                throw new CancellationException();
            return watch = newWatch;
        }
    }, listenerExecutor);
}
Also used : KeyValue(com.ibm.etcd.api.KeyValue) ByteString(com.google.protobuf.ByteString) WatchUpdate(com.ibm.etcd.client.kv.WatchUpdate) Function(com.google.common.base.Function) RangeResponse(com.ibm.etcd.api.RangeResponse) DeleteRangeResponse(com.ibm.etcd.api.DeleteRangeResponse) RevisionCompactedException(com.ibm.etcd.client.watch.RevisionCompactedException) CancellationException(java.util.concurrent.CancellationException) RangeRequest(com.ibm.etcd.api.RangeRequest) DeleteRangeRequest(com.ibm.etcd.api.DeleteRangeRequest) Watch(com.ibm.etcd.client.kv.KvClient.Watch) Event(com.ibm.etcd.api.Event) List(java.util.List) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) TxnResponse(com.ibm.etcd.api.TxnResponse)

Example 3 with Watch

use of com.ibm.etcd.client.kv.KvClient.Watch in project etcd-java by IBM.

the class EtcdWatchClient method watch.

/**
 * Blocking watch
 */
public WatchIterator watch(WatchCreateRequest createReq) {
    EtcdWatchIterator watchIt = new EtcdWatchIterator();
    Watch handle = watch(createReq, watchIt, MoreExecutors.directExecutor());
    return watchIt.setWatch(handle);
}
Also used : Watch(com.ibm.etcd.client.kv.KvClient.Watch)

Example 4 with Watch

use of com.ibm.etcd.client.kv.KvClient.Watch in project etcd-java by IBM.

the class WatchTest method testResiliency.

@Test
public void testResiliency() throws Exception {
    try (EtcdClient directClient = EtcdClient.forEndpoint("localhost", 2379).withPlainText().build();
        EtcdClient client = EtcdClient.forEndpoint("localhost", 2396).withPlainText().build()) {
        directClient.getKvClient().delete(bs("watch-tr-test/")).asPrefix().sync();
        try (final LocalNettyProxy prox = new LocalNettyProxy(2396)) {
            Thread proxyThread = new Thread() {

                {
                    setDaemon(true);
                }

                @Override
                public void run() {
                    try {
                        int N = 4;
                        for (int i = 1; i <= N; i++) {
                            prox.start();
                            Thread.sleep(1000L + (long) (Math.random() * 5000));
                            System.out.println("killing proxy " + i);
                            // finish in running state
                            if (i < N)
                                prox.kill();
                            Thread.sleep((long) (Math.random() * 4000));
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            proxyThread.start();
            KvClient directKv = directClient.getKvClient();
            Phaser p = new Phaser(1);
            Map<ByteString, Object> watchedKeys = new ConcurrentHashMap<>();
            int i = 0;
            // the proxy is stopped/started
            while (proxyThread.isAlive()) {
                // put a key
                ByteString key = bs("watch-tr-test/" + Math.random());
                ByteString value = bs("value " + (i++));
                PutResponse pr = directKv.put(key, value).sync();
                watchedKeys.put(key, "pending");
                p.register();
                final int ii = i;
                AtomicReference<Watch> w = new AtomicReference<>();
                w.set(client.getKvClient().watch(key).startRevision(pr.getHeader().getRevision()).start((ListenerObserver<WatchUpdate>) (complete, wu, err) -> {
                    if (complete) {
                        if (err != null) {
                            watchedKeys.replace(key, err);
                            err.printStackTrace();
                        } else
                            watchedKeys.remove(key);
                        if (w.get() == null)
                            p.arrive();
                    } else if (!wu.getEvents().isEmpty()) {
                        if (value.equals(wu.getEvents().get(0).getKv().getValue())) {
                            if (ii % 2 == 0) {
                                // cancel every other watch
                                w.get().close();
                                w.set(null);
                            } else {
                                watchedKeys.remove(key);
                                p.arrive();
                            }
                        } else {
                            watchedKeys.replace(key, "unexpected watch event value");
                            p.arrive();
                        }
                    }
                }));
                Thread.sleep((long) (Math.random() * 500));
            }
            p.arrive();
            p.awaitAdvanceInterruptibly(0, 8, TimeUnit.SECONDS);
            System.out.println("created " + i + " watches; left incomplete: " + watchedKeys.size());
            watchedKeys.entrySet().forEach(e -> System.out.println("** " + e.getKey().toStringUtf8() + "=" + e.getValue()));
            assertTrue(watchedKeys.isEmpty());
        }
    }
}
Also used : ByteString(com.google.protobuf.ByteString) AtomicReference(java.util.concurrent.atomic.AtomicReference) PutResponse(com.ibm.etcd.api.PutResponse) WatchCreateException(com.ibm.etcd.client.watch.WatchCreateException) ExecutionException(java.util.concurrent.ExecutionException) NoSuchElementException(java.util.NoSuchElementException) Watch(com.ibm.etcd.client.kv.KvClient.Watch) KvClient(com.ibm.etcd.client.kv.KvClient) Phaser(java.util.concurrent.Phaser) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Test(org.junit.Test)

Example 5 with Watch

use of com.ibm.etcd.client.kv.KvClient.Watch in project etcd-java by IBM.

the class WatchTest method testCreateFail.

@Test
public void testCreateFail() throws Exception {
    KvStoreClient client = EtcdClient.forEndpoint("localhost", 2379).withPlainText().build();
    try {
        KvClient kvc = client.getKvClient();
        // range end before start => should fail
        Watch watch2 = kvc.watch(ByteString.copyFromUtf8("/watchtest2")).rangeEnd(ByteString.copyFromUtf8("/watchtest1")).startRevision(-5000L).start((ListenerObserver<WatchUpdate>) (c, v, t) -> {
        });
        try {
            watch2.get(1000, TimeUnit.SECONDS);
            fail("watch future should fail");
        } catch (ExecutionException e) {
            System.out.println("watch creation failed with exception: " + e);
            assertTrue(e.getCause() instanceof WatchCreateException);
        }
    } finally {
        client.close();
    }
}
Also used : AfterClass(org.junit.AfterClass) WatchUpdate(com.ibm.etcd.client.kv.WatchUpdate) BeforeClass(org.junit.BeforeClass) Watch(com.ibm.etcd.client.kv.KvClient.Watch) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) KvClient(com.ibm.etcd.client.kv.KvClient) BlockingQueue(java.util.concurrent.BlockingQueue) Test(org.junit.Test) WatchIterator(com.ibm.etcd.client.kv.KvClient.WatchIterator) LinkedBlockingQueue(java.util.concurrent.LinkedBlockingQueue) AtomicReference(java.util.concurrent.atomic.AtomicReference) WatchCreateException(com.ibm.etcd.client.watch.WatchCreateException) ExecutionException(java.util.concurrent.ExecutionException) TimeUnit(java.util.concurrent.TimeUnit) ByteString(com.google.protobuf.ByteString) StreamObserver(io.grpc.stub.StreamObserver) Map(java.util.Map) Phaser(java.util.concurrent.Phaser) EventType(com.ibm.etcd.api.Event.EventType) KvTest.bs(com.ibm.etcd.client.KvTest.bs) Assert(org.junit.Assert) NoSuchElementException(java.util.NoSuchElementException) PutResponse(com.ibm.etcd.api.PutResponse) KvTest.t(com.ibm.etcd.client.KvTest.t) Watch(com.ibm.etcd.client.kv.KvClient.Watch) WatchUpdate(com.ibm.etcd.client.kv.WatchUpdate) KvClient(com.ibm.etcd.client.kv.KvClient) ExecutionException(java.util.concurrent.ExecutionException) WatchCreateException(com.ibm.etcd.client.watch.WatchCreateException) Test(org.junit.Test)

Aggregations

Watch (com.ibm.etcd.client.kv.KvClient.Watch)5 ByteString (com.google.protobuf.ByteString)4 PutResponse (com.ibm.etcd.api.PutResponse)3 KvClient (com.ibm.etcd.client.kv.KvClient)3 WatchUpdate (com.ibm.etcd.client.kv.WatchUpdate)3 WatchCreateException (com.ibm.etcd.client.watch.WatchCreateException)3 NoSuchElementException (java.util.NoSuchElementException)3 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)3 ExecutionException (java.util.concurrent.ExecutionException)3 Phaser (java.util.concurrent.Phaser)3 AtomicReference (java.util.concurrent.atomic.AtomicReference)3 Test (org.junit.Test)3 EventType (com.ibm.etcd.api.Event.EventType)2 KvTest.bs (com.ibm.etcd.client.KvTest.bs)2 KvTest.t (com.ibm.etcd.client.KvTest.t)2 WatchIterator (com.ibm.etcd.client.kv.KvClient.WatchIterator)2 StreamObserver (io.grpc.stub.StreamObserver)2 Map (java.util.Map)2 BlockingQueue (java.util.concurrent.BlockingQueue)2 LinkedBlockingQueue (java.util.concurrent.LinkedBlockingQueue)2