package water;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import water.H2O.H2OCallback;
import water.H2O.H2OCountedCompleter;
import water.util.IcedInt;
import water.util.IcedInt.AtomicIncrementAndGet;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Random;
/**
* Created by tomasnykodym on 10/5/15.
*
* Test that our DKV follows java mm.
*/
public class DKVTest extends TestUtil {
@BeforeClass()
public static void setup() {
stall_till_cloudsize(1);
}
private static class TestMM extends MRTask<TestMM> {
final Key [] _keys;
public TestMM(Key[] keys) {_keys = keys;}
@Override public void setupLocal() {
final H2OCountedCompleter barrier = (new H2OCountedCompleter() {
@Override
public void compute2() {
}
});
barrier.addToPendingCount(_keys.length-1);
for(Key k:_keys)DKV.get(k);
for (int i = 0; i < _keys.length; ++i) {
final Key fk = _keys[i];
new AtomicIncrementAndGet(new H2OCallback<AtomicIncrementAndGet>(barrier) {
@Override
public void callback(AtomicIncrementAndGet f) {
final int lb = f._val;
for(H2ONode node:H2O.CLOUD._memary) {
if(node != H2O.SELF) {
barrier.addToPendingCount(1);
new RPC(node,new DTask() {
@Override
public void compute2() {
IcedInt val = DKV.getGet(fk);
if (val._val < lb)
throw new IllegalArgumentException("DKV seems to be in inconsistent state after TAtomic, value lower than expected, expected at least " + lb + ", got " + val._val);
tryComplete();
}
}).addCompleter(barrier).call();
}
}
}
}).fork(_keys[i]);
}
barrier.join();
}
}
/**
* Test for recently discovered bug in DKV where updates sometimes failed to wait for all invalidates to the given key.
*
* Test that DKV puts (Tatomic updates) are globally visible after the tatomic task gets back.
*
* Makes a Key per node, caches it on all other nodes and then performs atomic updates followed by global visibility check.
* Fails if the update is not globally visible.
*
*/
@Test
public void testTatomic(){
try {
final Key[] keys = new Key[H2O.CLOUD.size()];
for (int r = 0; r < 20; ++r) {
System.out.println("iteration " + r);
try {
for (int i = 0; i < keys.length; ++i) // byte rf, byte systemType, boolean hint, H2ONode... replicas
DKV.put(keys[i] = Key.make((byte) 1, Key.HIDDEN_USER_KEY, true, H2O.CLOUD._memary[i]), new IcedInt(0));
new TestMM(keys).doAllNodes();
} finally {
for (Key k : keys)
if (k != null) DKV.remove(k);
}
}
} finally {
// H2O.orderlyShutdown();
}
}
class Bytes extends Iced<Bytes> {
public byte[] _b;
Bytes(byte[] b) { _b = b; }
}
@Ignore // This currently fails if the driver node owns the Key, as the live POJO version in the DKV is shared with the object that was inserted...
@Test
public void testConsistency(){
// make a random key
Key k1 = Key.make();
try {
// create random bytes
byte[] b1 = new byte[100];
new Random(1234).nextBytes(b1);
Bytes bytes1 = new Bytes(b1);
// put them into the DKV
DKV.put(k1, bytes1);
// overwrite the state of the live object
new Random(4321).nextBytes(bytes1._b);
// check that the DKV version isn't modified (i.e., that the DKV has the "original" version that was put into the store)
Assert.assertFalse(Arrays.equals(((Bytes) DKV.getGet(k1))._b, bytes1._b));
} finally {
DKV.remove(k1);
}
}
}