Search in sources :

Example 1 with TxnResponse

use of com.ibm.etcd.api.TxnResponse 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 TxnResponse

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

the class PersistentLeaseKey method putKey.

// called only from our serialized executor context
protected void putKey(long leaseId) {
    if (leaseId == 0L || closeFuture != null)
        return;
    if (updateFuture != null && !updateFuture.isDone()) {
        // if the cancellation wins then putKey will be immediately retried
        updateFuture.cancel(false);
        return;
    }
    // execute a transaction which either sets the lease on an existing key
    // or creates the key with the lease if it doesn't exist
    PutRequest.Builder putBld = PutRequest.newBuilder().setKey(key).setLease(leaseId);
    KvClient.FluentTxnRequest req = client.getKvClient().txnIf().exists(key).backoffRetry(() -> closeFuture == null && isActive());
    ListenableFuture<? extends Object> fut;
    ListenableFuture<TxnResponse> txnFut;
    if (rangeCache == null) {
        fut = txnFut = req.then().put(putBld.setIgnoreValue(true)).elseDo().put(putBld.setIgnoreValue(false).setValue(defaultValue)).async();
    } else {
        RangeRequest getOp = RangeRequest.newBuilder().setKey(key).build();
        txnFut = req.then().put(putBld.setIgnoreValue(true)).get(getOp).elseDo().put(putBld.setIgnoreValue(false).setValue(defaultValue)).get(getOp).async();
        fut = Futures.transform(txnFut, (Function<TxnResponse, Object>) tr -> rangeCache.offerUpdate(tr.getResponses(1).getResponseRange().getKvs(0), false));
    }
    if (!isDone())
        fut = Futures.transform(fut, (Function<Object, Object>) r -> set(key));
    // this callback is to trigger an immediate retry in case the attempt was cancelled by a more
    // recent lease state change to active
    Futures.addCallback(fut, (FutureListener<Object>) (v, t) -> {
        if (t instanceof CancellationException && isActive())
            putKey(leaseId);
    }, executor);
    updateFuture = fut;
}
Also used : LeaseState(com.ibm.etcd.client.lease.PersistentLease.LeaseState) MoreExecutors(com.google.common.util.concurrent.MoreExecutors) Function(com.google.common.base.Function) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) CancellationException(java.util.concurrent.CancellationException) Executor(java.util.concurrent.Executor) EtcdClient(com.ibm.etcd.client.EtcdClient) FutureListener(com.ibm.etcd.client.FutureListener) KvClient(com.ibm.etcd.client.kv.KvClient) SettableFuture(com.google.common.util.concurrent.SettableFuture) TxnResponse(com.ibm.etcd.api.TxnResponse) SerializingExecutor(com.ibm.etcd.client.SerializingExecutor) ByteString(com.google.protobuf.ByteString) ListenerObserver(com.ibm.etcd.client.ListenerObserver) Futures(com.google.common.util.concurrent.Futures) PersistentLease(com.ibm.etcd.client.lease.PersistentLease) RangeRequest(com.ibm.etcd.api.RangeRequest) PutRequest(com.ibm.etcd.api.PutRequest) AbstractFuture(com.google.common.util.concurrent.AbstractFuture) Function(com.google.common.base.Function) CancellationException(java.util.concurrent.CancellationException) RangeRequest(com.ibm.etcd.api.RangeRequest) PutRequest(com.ibm.etcd.api.PutRequest) KvClient(com.ibm.etcd.client.kv.KvClient) TxnResponse(com.ibm.etcd.api.TxnResponse)

Example 3 with TxnResponse

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

the class RangeCache method put.

/**
 * Multi-purpose put or delete, returns updated or existing KeyValue
 *
 * @param key
 * @param value new value to put, or null to delete
 * @param lease leaseId to associate value with if successful, or 0L for no lease
 * @param conditions conditions which must all match to proceed, null or empty for unconditional
 * @return PutResult
 */
public PutResult put(ByteString key, ByteString value, long lease, CompareOrBuilder... conditions) {
    ListenableFuture<TxnResponse> tf = doPut(key, value, true, lease, conditions);
    // TODO -- async option
    TxnResponse tr = waitFor(tf, TIMEOUT_MS);
    if (tr.getSucceeded()) {
        if (value != null) {
            KeyValue putValue = tr.getResponses(1).getResponseRange().getKvs(0);
            offerUpdate(putValue, false);
            return new PutResult(true, putValue);
        } else {
            offerDelete(key, tr.getHeader().getRevision());
            return new PutResult(true, null);
        }
    } else {
        // assert conditions != null && conditions.length > 0;
        RangeResponse rr = tr.getResponses(0).getResponseRange();
        KeyValue exist = rr.getKvsCount() > 0 ? offerUpdate(rr.getKvs(0), false) : offerDelete(key, tr.getHeader().getRevision());
        return new PutResult(false, exist);
    }
}
Also used : KeyValue(com.ibm.etcd.api.KeyValue) RangeResponse(com.ibm.etcd.api.RangeResponse) DeleteRangeResponse(com.ibm.etcd.api.DeleteRangeResponse) TxnResponse(com.ibm.etcd.api.TxnResponse)

Example 4 with TxnResponse

use of com.ibm.etcd.api.TxnResponse 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();
        assertEquals(0L, kvc.delete(bs("notthere")).sync().getDeleted());
        ByteString a = bs("a"), b = bs("b"), v1 = bs("v1"), v2 = bs("v2");
        // basic put
        assertTrue(kvc.put(a, v1).sync().getHeader().getRevision() > 0);
        // basic get
        RangeResponse rr = kvc.get(bs("a")).sync();
        assertEquals(1L, rr.getCount());
        assertEquals(v1, rr.getKvs(0).getValue());
        // basic delete
        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")).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 : KvStoreClient(com.ibm.etcd.client.KvStoreClient) RangeResponse(com.ibm.etcd.api.RangeResponse) ByteString(com.google.protobuf.ByteString) KvClient(com.ibm.etcd.client.kv.KvClient) PutRequest(com.ibm.etcd.api.PutRequest) TxnResponse(com.ibm.etcd.api.TxnResponse) Test(org.junit.Test)

Example 5 with TxnResponse

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

the class RangeCache method putNoGet.

/**
 * Multi-purpose put or delete. If successful returns modRevision
 * of updated keyValue or 0 if deleted
 *
 * @param key
 * @param value new value to put, or null to delete
 * @param lease leaseId to associate value with if successful, or 0L for no lease
 * @param conditions conditions which must all match to proceed, null or empty for unconditional
 * @return -1 if condition failed, else modRevision of updated keyValue
 */
public long putNoGet(ByteString key, ByteString value, long lease, CompareOrBuilder... conditions) {
    ListenableFuture<TxnResponse> tf = doPut(key, value, false, lease, conditions);
    // TODO -- async option
    TxnResponse tr = waitFor(tf, TIMEOUT_MS);
    if (!tr.getSucceeded())
        return -1L;
    else if (value != null) {
        KeyValue kv = tr.getResponses(1).getResponseRange().getKvs(0);
        offerUpdate(kv, false);
        return kv.getModRevision();
    } else {
        offerDelete(key, tr.getHeader().getRevision());
        // TODO TBD return modRevision or 0 in this case?
        return 0L;
    }
}
Also used : KeyValue(com.ibm.etcd.api.KeyValue) TxnResponse(com.ibm.etcd.api.TxnResponse)

Aggregations

TxnResponse (com.ibm.etcd.api.TxnResponse)6 ByteString (com.google.protobuf.ByteString)4 KeyValue (com.ibm.etcd.api.KeyValue)4 RangeResponse (com.ibm.etcd.api.RangeResponse)4 Function (com.google.common.base.Function)3 DeleteRangeResponse (com.ibm.etcd.api.DeleteRangeResponse)3 PutRequest (com.ibm.etcd.api.PutRequest)3 RangeRequest (com.ibm.etcd.api.RangeRequest)3 KvClient (com.ibm.etcd.client.kv.KvClient)3 Futures (com.google.common.util.concurrent.Futures)2 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)2 MoreExecutors (com.google.common.util.concurrent.MoreExecutors)2 DeleteRangeRequest (com.ibm.etcd.api.DeleteRangeRequest)2 Event (com.ibm.etcd.api.Event)2 EtcdClient (com.ibm.etcd.client.EtcdClient)2 SerializingExecutor (com.ibm.etcd.client.SerializingExecutor)2 Watch (com.ibm.etcd.client.kv.KvClient.Watch)2 WatchUpdate (com.ibm.etcd.client.kv.WatchUpdate)2 RevisionCompactedException (com.ibm.etcd.client.watch.RevisionCompactedException)2 List (java.util.List)2