package com.jivesoftware.os.amza.service.storage.binary;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.jivesoftware.os.amza.api.AmzaInterner;
import com.jivesoftware.os.amza.api.TimestampedValue;
import com.jivesoftware.os.amza.api.filer.UIO;
import com.jivesoftware.os.amza.api.partition.PartitionName;
import com.jivesoftware.os.amza.api.partition.VersionedPartitionName;
import com.jivesoftware.os.amza.api.stream.RowType;
import com.jivesoftware.os.amza.api.wal.MemoryWALIndex;
import com.jivesoftware.os.amza.api.wal.MemoryWALIndexProvider;
import com.jivesoftware.os.amza.api.wal.MemoryWALUpdates;
import com.jivesoftware.os.amza.api.wal.WALIndexProvider;
import com.jivesoftware.os.amza.api.wal.WALKey;
import com.jivesoftware.os.amza.api.wal.WALRow;
import com.jivesoftware.os.amza.service.SickPartitions;
import com.jivesoftware.os.amza.service.filer.HeapByteBufferFactory;
import com.jivesoftware.os.amza.service.stats.AmzaStats;
import com.jivesoftware.os.amza.service.stats.AmzaStats.CompactionStats;
import com.jivesoftware.os.amza.api.IoStats;
import com.jivesoftware.os.amza.service.storage.WALStorage;
import com.jivesoftware.os.jive.utils.ordered.id.ConstantWriterIdProvider;
import com.jivesoftware.os.jive.utils.ordered.id.OrderIdProviderImpl;
import java.io.File;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* @author jonathan.colt
*/
public class RowPartitionNGTest {
final BinaryPrimaryRowMarshaller primaryRowMarshaller = new BinaryPrimaryRowMarshaller();
final BinaryHighwaterRowMarshaller highwaterRowMarshaller = new BinaryHighwaterRowMarshaller(new AmzaInterner());
@Test(enabled = false)
public void concurrencyTest() throws Exception {
AmzaStats amzaStats = new AmzaStats();
File walDir = Files.createTempDir();
IoStats ioStats = new IoStats();
RowIOProvider binaryRowIOProvider = new BinaryRowIOProvider(4096, 64, false);
final WALIndexProvider<MemoryWALIndex> indexProvider = new MemoryWALIndexProvider("memory");
VersionedPartitionName partitionName = new VersionedPartitionName(new PartitionName(false, "ring".getBytes(), "booya".getBytes()),
VersionedPartitionName.STATIC_VERSION);
BinaryWALTx binaryWALTx = new BinaryWALTx("booya", binaryRowIOProvider, primaryRowMarshaller, 4096, 64);
OrderIdProviderImpl idProvider = new OrderIdProviderImpl(new ConstantWriterIdProvider(1));
WALStorage<MemoryWALIndex> indexedWAL = new WALStorage<>(
new AmzaStats(),
partitionName,
idProvider,
primaryRowMarshaller,
highwaterRowMarshaller,
binaryWALTx,
indexProvider,
new SickPartitions(),
false,
2);
indexedWAL.load(ioStats, walDir, -1, -1, false, false, -1, 0);
final Random r = new Random();
ScheduledExecutorService compact = Executors.newScheduledThreadPool(1);
compact.scheduleAtFixedRate(() -> {
CompactionStats compactionStats = amzaStats.beginCompaction(AmzaStats.CompactionFamily.tombstone, walDir.getName());
try {
indexedWAL.compactTombstone(ioStats, compactionStats, walDir, walDir, RowType.primary, 0, 0, Long.MAX_VALUE, Long.MAX_VALUE, -1, -1, 0, false,
(transitionToCompacted) -> transitionToCompacted.tx(() -> {
return null;
}));
} catch (Exception x) {
x.printStackTrace();
} finally {
compactionStats.finished();
}
}, 1, 1, TimeUnit.SECONDS);
int numThreads = 1;
ExecutorService writers = Executors.newFixedThreadPool(numThreads);
for (int i = 0; i < numThreads; i++) {
writers.submit(() -> {
for (int i1 = 1; i1 < 1_000; i1++) {
try {
addBatch(ioStats, r, idProvider, indexedWAL, i1, 0, 10);
if (i1 % 1000 == 0) {
System.out.println(Thread.currentThread() + " batch:" + i1);
}
} catch (Throwable x) {
x.printStackTrace();
}
}
});
}
writers.shutdown();
writers.awaitTermination(1, TimeUnit.DAYS);
compact.shutdownNow();
// addBatch(r, idProvider, table, 10, 0, 10);
// table.compactTombstone(0);
// addBatch(r, idProvider, table, 10, 10, 20);
// table.compactTombstone(0);
//
// table.rowScan(new RowScan<Exception>() {
//
// @Override
// public boolean row(long transactionId, RowIndexKey key, RowIndexValue value) throws Exception {
// System.out.println(UIO.bytesInt(key.getKey()));
// return true;
// }
// });
}
private void addBatch(IoStats ioStats, Random r, OrderIdProviderImpl idProvider, WALStorage indexedWAL, int range, int start, int length) throws Exception {
List<WALRow> updates = Lists.newArrayList();
byte[] prefix = UIO.intBytes(-1);
for (int i = start; i < start + length; i++) {
byte[] key = UIO.intBytes(r.nextInt(range));
byte[] value = UIO.intBytes(i);
long timestampAndVersion = idProvider.nextId();
updates.add(new WALRow(RowType.primary, prefix, key, value, timestampAndVersion, false, timestampAndVersion));
}
indexedWAL.update(ioStats, true, RowType.primary, -1, false, prefix, new MemoryWALUpdates(updates, null));
}
@Test
public void diskBackedEventualConsistencyTest() throws Exception {
File walDir = Files.createTempDir();
IoStats ioStats = new IoStats();
RowIOProvider binaryRowIOProvider = new BinaryRowIOProvider(4096, 64, false);
WALIndexProvider<MemoryWALIndex> indexProvider = new MemoryWALIndexProvider("memory");
VersionedPartitionName versionedPartitionName = new VersionedPartitionName(new PartitionName(false, "ring".getBytes(), "booya".getBytes()),
VersionedPartitionName.STATIC_VERSION);
BinaryWALTx binaryWALTx = new BinaryWALTx("booya", binaryRowIOProvider, primaryRowMarshaller, 4096, 64);
OrderIdProviderImpl idProvider = new OrderIdProviderImpl(new ConstantWriterIdProvider(1));
testEventualConsistency(ioStats, walDir, versionedPartitionName, idProvider, binaryWALTx, indexProvider);
}
@Test
public void memoryBackedEventualConsistencyTest() throws Exception {
IoStats ioStats = new IoStats();
RowIOProvider binaryRowIOProvider = new MemoryBackedRowIOProvider(4_096, 4_096, 4_096, 64, new HeapByteBufferFactory());
WALIndexProvider<MemoryWALIndex> indexProvider = new MemoryWALIndexProvider("memory");
VersionedPartitionName versionedPartitionName = new VersionedPartitionName(new PartitionName(false, "ring".getBytes(), "booya".getBytes()),
VersionedPartitionName.STATIC_VERSION);
BinaryWALTx binaryWALTx = new BinaryWALTx("booya", binaryRowIOProvider, primaryRowMarshaller, 4096, 64);
OrderIdProviderImpl idProvider = new OrderIdProviderImpl(new ConstantWriterIdProvider(1));
testEventualConsistency(ioStats, null, versionedPartitionName, idProvider, binaryWALTx, indexProvider);
}
private void testEventualConsistency(IoStats ioStats, File baseKey, VersionedPartitionName versionedPartitionName,
OrderIdProviderImpl idProvider,
BinaryWALTx binaryWALTx,
WALIndexProvider<MemoryWALIndex> walIndexProvider)
throws Exception {
WALStorage<MemoryWALIndex> indexedWAL = new WALStorage<>(
new AmzaStats(),
versionedPartitionName,
idProvider,
primaryRowMarshaller,
highwaterRowMarshaller,
binaryWALTx,
walIndexProvider,
new SickPartitions(),
false,
2);
indexedWAL.load(ioStats, baseKey, -1, -1, false, false, -1, 0);
WALKey walKey = k(1);
TimestampedValue value = indexedWAL.getTimestampedValue(walKey.prefix, walKey.key);
Assert.assertNull(value);
int t = 10;
update(ioStats, indexedWAL, walKey.prefix, walKey.key, v("hello"), t, false);
value = indexedWAL.getTimestampedValue(walKey.prefix, walKey.key);
Assert.assertEquals(value.getTimestampId(), t);
Assert.assertEquals(new String(value.getValue()), "hello");
t++;
update(ioStats, indexedWAL, walKey.prefix, walKey.key, v("hello2"), t, false);
value = indexedWAL.getTimestampedValue(walKey.prefix, walKey.key);
Assert.assertEquals(value.getTimestampId(), t);
Assert.assertEquals(new String(value.getValue()), "hello2");
update(ioStats, indexedWAL, walKey.prefix, walKey.key, v("hello3"), t, false);
value = indexedWAL.getTimestampedValue(walKey.prefix, walKey.key);
Assert.assertEquals(value.getTimestampId(), t);
Assert.assertEquals(new String(value.getValue()), "hello2");
update(ioStats, indexedWAL, walKey.prefix, walKey.key, v("fail"), t - 1, false);
value = indexedWAL.getTimestampedValue(walKey.prefix, walKey.key);
Assert.assertEquals(value.getTimestampId(), t);
Assert.assertEquals(new String(value.getValue()), "hello2");
t++;
update(ioStats, indexedWAL, walKey.prefix, walKey.key, v("deleted"), t, false);
value = indexedWAL.getTimestampedValue(walKey.prefix, walKey.key);
Assert.assertEquals(value.getTimestampId(), t);
Assert.assertEquals(new String(value.getValue()), "deleted");
update(ioStats, indexedWAL, walKey.prefix, walKey.key, v("fail"), t - 1, false);
value = indexedWAL.getTimestampedValue(walKey.prefix, walKey.key);
Assert.assertEquals(value.getTimestampId(), t);
Assert.assertEquals(new String(value.getValue()), "deleted");
t++;
update(ioStats, indexedWAL, walKey.prefix, walKey.key, v("hello4"), t, false);
value = indexedWAL.getTimestampedValue(walKey.prefix, walKey.key);
Assert.assertEquals(value.getTimestampId(), t);
Assert.assertEquals(new String(value.getValue()), "hello4");
}
public WALKey k(int key) {
return new WALKey(UIO.intBytes(-key), UIO.intBytes(key));
}
public byte[] v(String value) {
return value.getBytes();
}
private void update(IoStats ioStats, WALStorage indexedWAL, byte[] prefix, byte[] key, byte[] value, long timestamp, boolean remove) throws Exception {
List<WALRow> updates = Lists.newArrayList();
updates.add(new WALRow(RowType.primary, prefix, key, value, timestamp, remove, timestamp));
indexedWAL.update(ioStats, true, RowType.primary, -1, false, prefix, new MemoryWALUpdates(updates, null));
}
}