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);
}
}
}
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;
}
}
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());
}
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();
}
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;
}
}
}
}
}
Aggregations