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