Search in sources :

Example 6 with KeyValue

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

the class EtcdLeaderElection method updateLeader.

private void updateLeader() {
    synchronized (candidates) {
        if (candidates.isClosed())
            return;
        KeyValue chosen = null;
        for (KeyValue kv : candidates) if (chosen == null || kv.getCreateRevision() < chosen.getCreateRevision())
            chosen = kv;
        String chosenId = chosen == null ? null : candId(chosen);
        String priorLeaderId = leaderId;
        leaderId = chosenId;
        // observer only
        if (id == null)
            return;
        boolean wasUs = id.equals(priorLeaderId), isUs = id.equals(chosenId);
        if (wasUs ^ isUs) {
            leader = isUs;
            notifyListeners(isUs);
        }
    }
}
Also used : KeyValue(com.ibm.etcd.api.KeyValue) ByteString(com.google.protobuf.ByteString)

Example 7 with KeyValue

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

Example 8 with KeyValue

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

the class RangeCache method getRemote.

protected KeyValue getRemote(ByteString key, boolean weak) {
    if (key == null)
        return null;
    ListenableFuture<RangeResponse> rrf = kvClient.get(key).serializable(weak).async();
    // TODO -- async option
    RangeResponse rr = waitFor(rrf, TIMEOUT_MS);
    KeyValue kv = rr.getCount() > 0 ? rr.getKvs(0) : null;
    return kv != null ? offerUpdate(kv, false) : offerDelete(key, rr.getHeader().getRevision());
}
Also used : KeyValue(com.ibm.etcd.api.KeyValue) RangeResponse(com.ibm.etcd.api.RangeResponse) DeleteRangeResponse(com.ibm.etcd.api.DeleteRangeResponse)

Example 9 with KeyValue

use of com.ibm.etcd.api.KeyValue 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 KeyValues} of this cache
 */
public Iterator<KeyValue> strongIterator() {
    // memory barrier prior to reading seenUpToRev
    entries.get(fromKey);
    long seenUpTo = seenUpToRev;
    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;
            } 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)
    ListenableFuture<TxnResponse> txn = kvClient.batch().get(newModsReq).get(curCountReq).get(seenCountReq).async();
    TxnResponse txnResp;
    try {
        txnResp = waitFor(txn, 8000L);
    } 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 = waitFor(kvClient.batch().get(newModsReq).get(otherKeysReq).async(), // longer timeout
        60_000L);
    }
    long revNow = txnResp.getHeader().getRevision();
    if (revNow > seenUpToRev) {
        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)
            listenerExecutor.execute(() -> revisionUpdate(revNow));
    }
    return iterator();
}
Also used : 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) 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) Function(com.google.common.base.Function) 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) 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) 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) Logger(org.slf4j.Logger) Iterator(java.util.Iterator) Executor(java.util.concurrent.Executor) KvClient(com.ibm.etcd.client.kv.KvClient) SerializingExecutor(com.ibm.etcd.client.SerializingExecutor) KeyValue(com.ibm.etcd.api.KeyValue) TxnRequest(com.ibm.etcd.api.TxnRequest) ExecutionException(java.util.concurrent.ExecutionException) TimeUnit(java.util.concurrent.TimeUnit) ConcurrentSkipListMap(java.util.concurrent.ConcurrentSkipListMap) Futures(com.google.common.util.concurrent.Futures) ConcurrentSkipListSet(java.util.concurrent.ConcurrentSkipListSet) Collections(java.util.Collections) 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 10 with KeyValue

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

KeyValue (com.ibm.etcd.api.KeyValue)10 ByteString (com.google.protobuf.ByteString)5 DeleteRangeResponse (com.ibm.etcd.api.DeleteRangeResponse)4 RangeResponse (com.ibm.etcd.api.RangeResponse)4 TxnResponse (com.ibm.etcd.api.TxnResponse)4 Event (com.ibm.etcd.api.Event)3 KvClient (com.ibm.etcd.client.kv.KvClient)3 Function (com.google.common.base.Function)2 DeleteRangeRequest (com.ibm.etcd.api.DeleteRangeRequest)2 RangeRequest (com.ibm.etcd.api.RangeRequest)2 EtcdClient (com.ibm.etcd.client.EtcdClient)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 Iterators (com.google.common.collect.Iterators)1 Futures (com.google.common.util.concurrent.Futures)1 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)1 MoreExecutors (com.google.common.util.concurrent.MoreExecutors)1 Compare (com.ibm.etcd.api.Compare)1