package org.rakam.kume.service.crdt.register; import org.rakam.kume.service.DistributedObjectServiceAdapter; import org.rakam.kume.ServiceContext; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; public class LWWRegister<T> extends DistributedObjectServiceAdapter<LWWRegister<T>, LWWRegister.ObjectTimestampHolder<T>> { public LWWRegister(ServiceContext clusterContext, T value, int replicationFactor) { super(clusterContext, new LWWRegister.ObjectTimestampHolder<>(value), replicationFactor); } public void set(T entry) { ObjectTimestampHolder<T> holder = new ObjectTimestampHolder<>(entry); sendToReplicas((service, ctx) -> { if(service.value.timestamp < holder.timestamp) { service.value = holder; } }); } public T get() { Stream<CompletableFuture<ObjectTimestampHolder<T>>> stream = askReplicas((service, ctx) -> service.get()); return stream.max((o1, o2) -> Long.compare(o1.join().timestamp, o2.join().timestamp)) .get().join().value; } @Override protected boolean mergeIn(ObjectTimestampHolder<T> val) { if(val.timestamp > value.timestamp) { value = val; return true; } return false; } // we do not use vector clocks because they're expensive. public static class ObjectTimestampHolder<T> { T value; long timestamp; public ObjectTimestampHolder(T value) { this.value = value; // TODO: clock synchronization using master node or a public time server? timestamp = System.currentTimeMillis(); } } }