I have the following problem: In this simple implementation of a MVR-CRDT the merge() method should add all values that are not null to the merged replica. For that to happen i used a for loop which iterates through both ArrayLists. At every index where a value is present it compares the vector clocks and overwrites the value of the highest clock at said index. It seems to not be working as intended. I would be glad if somebody can point out the problem.
import java.util.ArrayList;
import java.util.Collections;
public class MVR_CRDT{
//variables
static int size;
private ArrayList<Integer> vectorClock;
private ArrayList<Object> values;
//constructor
public MVR_CRDT(int size) {
this.vectorClock = new ArrayList<Integer>(Collections.nCopies(size, 0));
this.values = new ArrayList<Object>(Collections.nCopies(size, null));
}
//sets a value and increments its corresponding vector clock
public void setValue(int index, Object value) {
//check if value is different
if (value != this.values.get(index)){
this.vectorClock.set(index, this.vectorClock.get(index) + 1);
this.values.set(index, value);
//goes over the other indexes and empties their value
for (int i = 0; i < size; i++) {
while (i != index){
this.values.set(i, null);
}
}
}
}
//the second replica merges into the first
public static void merge(MVR_CRDT first, MVR_CRDT second){
//loops through the ArrayList
for (int i = 0; i < size; i++) {
//if there is a value to be merged
if (second.values.get(i) != null){
first.vectorClock.set(i, max(first.vectorClock.get(i), second.vectorClock.get(i)));
first.values.set(i, second.values.get(i));
}
}
}
private static Integer max(Integer integer, Integer integer2) {
return null;
}
//gets the value at index position of a replica
public Object getValue(int index) {
return this.values.get(index);
}
public Integer getVector(int index) {
return this.vectorClock.get(index);
}
//prints the replica
public void query() {
System.out.println("Vector Clock: " + this.vectorClock.toString());
System.out.println("Values: " + this.values.toString() + "\n");
}
public static void main(String[] args) {
MVR_CRDT Alice = new MVR_CRDT(3);
MVR_CRDT Bob = new MVR_CRDT(3);
MVR_CRDT Charlie = new MVR_CRDT(3);
Alice.setValue(0, "Hello");
Alice.setValue(0, "Hello"); //to show that Alices counter does not increase
System.out.println("Alice's replica: ");
Alice.query();
Bob.setValue(1, 12312);
System.out.println("Bob's replica: ");
Bob.query();
// Charlie.setValue(2, false);
merge(Alice, Bob);
System.out.println("Alice's replica after merging: ");
Alice.query();
// System.out.println(Bob.getValue(1) + " , " + Bob.getVector(1));
}
}
Output after merge expected:
Vector Clock: [1, 1, 0] Values: [Hello, 12312, null]
Main method output:
Alice's replica:
Vector Clock: [1, 0, 0]
Values: [Hello, null, null]
Bob's replica:
Vector Clock: [0, 1, 0]
Values: [null, 12312, null]
Alice's replica after merging:
Vector Clock: [1, 0, 0]
Values: [Hello, null, null]
Looking at your code, several improvements can be done. I start with the one you asked for, so you can stop after that:
Your method
mergeuses the static variablesizeas stop criterion in theforloop, that is never explicitly initialized, so it is implicitly initialized to0- the loop inmergenever does a single pass, so your output shows an unmodifiedAlice.But even if it would use a "better" size, like e.g.
first.vectorClock.size()it would not output the correct result - but a different one:This is because your
maxmethod always returnsnull. I would recommend that you use a built inmaxmethod likeInteger.max(int, int). Then your expected result will be output.And now for some other findings (you could stop reading, your original problem is solved): Please use an established naming convention when you post code here, for Java this is in the Java Language Specification, chapter 6. Names, section 6.1. Declarations (it starts after the long nested list there, written in italic).
So for your class name that should be
MvrCrdtor maybe betterMultiValueRegisterCRDT- or even longer? Your instancesAliceandBobshould start with a lowercase letter and bealiceandbob.Your code leftovers and commented code should be removed, before posting here.
Declarations: your member fields do not need to know the implementation of the list, it's sufficient to use
List<Integer>andList<Object>, useArrayList<>in the constructor only with thenewoperator!As you never reassign the member fields
vectorClockandvalues, you should make themfinal.Constructor: You don't need to specify the type that the list stores on the assignment, it's sufficient to have
this.vectorClock = new ArrayList<>(Collections.nCopies(size, 0));Method
setValue: You have the same issue withsizein yoursetValuemethod, replace it withthis.values.size().In the
whileloop inside yourforloop the values ofiandindexnever change inside thewhileloop, so you should replace thewhilewith anifor you change the loop-body. Otherwise you will have an endless loop, if the loop is ever entered! For now you didn't face this problem, since the outerforloop never was entered.Method
merge: That method should neither bestaticnor take two parameters. It always modifies the first input, so it could as well just take the object to merge into the current instance and manipulatethisinstead offirst:With that modification you would have to use it so:
alice.merge(bob);Method
query: you could replace this with an override oftoStringlike:Then you can use it like
System.out.println("Alice's replica: \n" + alice);