Search in sources :

Example 1 with Event

use of com.ibm.etcd.api.Event 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 2 with Event

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

the class PersistentLeaseKeyTest method testLeaseKey.

@Test
public void testLeaseKey() throws Exception {
    try (EtcdClient client = EtcdClient.forEndpoint("localhost", 2392).withPlainText().build();
        EtcdClient directClient = EtcdClient.forEndpoint("localhost", 2379).withPlainText().build()) {
        KvClient directKvClient = directClient.getKvClient();
        ByteString mykey = bs("mykeyy");
        PersistentLeaseKey plk = new PersistentLeaseKey(client, mykey, bs("defaultdata"), null);
        ListenableFuture<ByteString> startFuture = plk.startWithFuture();
        Thread.sleep(300L);
        // network conn to server not established yet
        assertFalse(startFuture.isDone());
        // key won't exist yet
        assertEquals(0, directKvClient.get(mykey).countOnly().sync().getCount());
        // reestablish network
        proxy.start();
        assertEquals(mykey, startFuture.get(3000, MILLISECONDS));
        // check key is present via other client
        assertEquals(bs("defaultdata"), directKvClient.get(mykey).sync().getKvs(0).getValue());
        plk.closeWithFuture().get(500, MILLISECONDS);
        // key should now be gone
        assertEquals(0, directKvClient.get(bs("mykeyy")).countOnly().sync().getCount());
        // -----------------
        PersistentLease pl = client.getLeaseClient().maintain().minTtl(2).start();
        System.out.println("PL state: " + pl.getState());
        plk = new PersistentLeaseKey(client, pl, mykey, bs("somedata"), null);
        plk.start();
        assertFalse(pl.isDone());
        Long leaseId = pl.get(1, TimeUnit.SECONDS);
        assertNotNull(leaseId);
        System.out.println("PL state: " + pl.getState());
        // will take a small amount of time after lease is created for PLK to be
        // created
        assertFalse(plk.isDone());
        plk.get(1, TimeUnit.SECONDS);
        KeyValue kv = directKvClient.get(mykey).sync().getKvs(0);
        assertEquals(bs("somedata"), kv.getValue());
        assertEquals((long) leaseId, kv.getLease());
        plk.setDefaultValue(bs("updateddata"));
        Thread.sleep(200L);
        // data doesn't change until key has to be recreated
        assertEquals(bs("somedata"), directKvClient.get(mykey).sync().getKvs(0).getValue());
        proxy.kill();
        long ttl = pl.getCurrentTtlSecs();
        System.out.println("TTL after kill is " + ttl);
        Thread.sleep(1000L);
        // key should still be there (lease not yet expired)
        kv = directKvClient.get(mykey).sync().getKvs(0);
        assertEquals(bs("somedata"), kv.getValue());
        assertEquals((long) leaseId, kv.getLease());
        Thread.sleep((pl.getCurrentTtlSecs() + 2) * 1000L);
        // lease should have now expired and key should be gone
        assertEquals(0, directKvClient.get(bs("mykeyy")).sync().getCount());
        proxy.start();
        long before = System.nanoTime();
        try (WatchIterator it = directKvClient.watch(bs("mykeyy")).start()) {
            for (int i = 0; i < 5; i++) {
                List<Event> events = it.next().getEvents();
                if (!events.isEmpty()) {
                    // key should be updated with new value once
                    // connection is reestablished
                    assertEquals(bs("updateddata"), events.get(0).getKv().getValue());
                    System.out.println("took " + (System.nanoTime() - before) / 1000_000L + "ms for key to re-appear");
                    break;
                }
            }
        }
    }
}
Also used : KeyValue(com.ibm.etcd.api.KeyValue) ByteString(com.google.protobuf.ByteString) EtcdClient(com.ibm.etcd.client.EtcdClient) KvClient(com.ibm.etcd.client.kv.KvClient) PersistentLease(com.ibm.etcd.client.lease.PersistentLease) Event(com.ibm.etcd.api.Event) WatchIterator(com.ibm.etcd.client.kv.KvClient.WatchIterator) Test(org.junit.Test)

Aggregations

ByteString (com.google.protobuf.ByteString)2 Event (com.ibm.etcd.api.Event)2 KeyValue (com.ibm.etcd.api.KeyValue)2 Function (com.google.common.base.Function)1 DeleteRangeRequest (com.ibm.etcd.api.DeleteRangeRequest)1 DeleteRangeResponse (com.ibm.etcd.api.DeleteRangeResponse)1 RangeRequest (com.ibm.etcd.api.RangeRequest)1 RangeResponse (com.ibm.etcd.api.RangeResponse)1 TxnResponse (com.ibm.etcd.api.TxnResponse)1 EtcdClient (com.ibm.etcd.client.EtcdClient)1 KvClient (com.ibm.etcd.client.kv.KvClient)1 Watch (com.ibm.etcd.client.kv.KvClient.Watch)1 WatchIterator (com.ibm.etcd.client.kv.KvClient.WatchIterator)1 WatchUpdate (com.ibm.etcd.client.kv.WatchUpdate)1 PersistentLease (com.ibm.etcd.client.lease.PersistentLease)1 RevisionCompactedException (com.ibm.etcd.client.watch.RevisionCompactedException)1 List (java.util.List)1 CancellationException (java.util.concurrent.CancellationException)1 CopyOnWriteArrayList (java.util.concurrent.CopyOnWriteArrayList)1 Test (org.junit.Test)1