mirror of
https://github.com/LBRYFoundation/lbry-database-java.git
synced 2025-08-23 17:27:26 +00:00
Implement start-stop iterator test
This commit is contained in:
parent
047df3a149
commit
dd15a01285
3 changed files with 116 additions and 49 deletions
|
@ -67,7 +67,7 @@ public abstract class BasePrefixDB{
|
||||||
this.applyStash();
|
this.applyStash();
|
||||||
WriteOptions writeOptions = new WriteOptions().setSync(true);
|
WriteOptions writeOptions = new WriteOptions().setSync(true);
|
||||||
try{
|
try{
|
||||||
if(this.operationStack.length()!=0){
|
if(this.operationStack.length()==0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WriteBatch batch = new WriteBatch();
|
WriteBatch batch = new WriteBatch();
|
||||||
|
|
|
@ -5,7 +5,6 @@ import com.lbry.database.util.Tuple2;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -54,6 +53,7 @@ public class RevertibleOperationStack{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateAndApplyStashedOperations(){
|
public void validateAndApplyStashedOperations(){
|
||||||
|
// System.err.println("STASH = "+this.stash);
|
||||||
if(this.stash.isEmpty()){
|
if(this.stash.isEmpty()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -63,14 +63,10 @@ public class RevertibleOperationStack{
|
||||||
|
|
||||||
while(!this.stash.isEmpty()){
|
while(!this.stash.isEmpty()){
|
||||||
RevertibleOperation operation = this.stash.pollFirst();
|
RevertibleOperation operation = this.stash.pollFirst();
|
||||||
RevertibleOperation[] operationArr = null;
|
|
||||||
for(Map.Entry<byte[],RevertibleOperation[]> e : this.items.entrySet()){
|
RevertibleOperation[] operationArr = MapHelper.getValue(this.items,operation.key);
|
||||||
if(Arrays.equals(e.getKey(),operation.getKey())){
|
|
||||||
operationArr = e.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(operationArr!=null && operationArr.length>=1 && operation.invert().equals(operationArr[operationArr.length-1])){
|
if(operationArr!=null && operationArr.length>=1 && operation.invert().equals(operationArr[operationArr.length-1])){
|
||||||
this.items.put(operationArr[0].getKey(),Arrays.copyOfRange(operationArr,0,operationArr.length-1));
|
this.items.replace(operationArr[0].getKey(),Arrays.copyOfRange(operationArr,0,operationArr.length-1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(operationArr!=null && operationArr.length>=1 && operation.equals(operationArr[operationArr.length-1])){
|
if(operationArr!=null && operationArr.length>=1 && operation.equals(operationArr[operationArr.length-1])){
|
||||||
|
@ -82,50 +78,39 @@ public class RevertibleOperationStack{
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<byte[],byte[]> existing = new HashMap<>();
|
Map<byte[],byte[]> existing = new HashMap<>();
|
||||||
if(this.enforceIntegrity && !uniqueKeys.isEmpty()){
|
// if(this.enforceIntegrity && !uniqueKeys.isEmpty()){
|
||||||
List<byte[]> uniqueKeysList = new ArrayList<>(uniqueKeys);
|
// List<byte[]> uniqueKeysList = new ArrayList<>(uniqueKeys);
|
||||||
for(int idx=0;idx<uniqueKeys.size();idx+=10000){
|
// for(int idx=0;idx<uniqueKeys.size();idx+=10000){
|
||||||
List<byte[]> batch = uniqueKeysList.subList(idx,Math.min(uniqueKeysList.size(),idx+10000));
|
// List<byte[]> batch = uniqueKeysList.subList(idx,Math.min(uniqueKeysList.size(),idx+10000));
|
||||||
Iterator<Optional<byte[]>> iterator = this.multiGet.apply(batch).iterator();
|
// Iterator<Optional<byte[]>> iterator = this.multiGet.apply(batch).iterator();
|
||||||
for(byte[] k : batch){
|
// for(byte[] k : batch){
|
||||||
byte[] v = iterator.next().get();
|
// byte[] v = iterator.next().get();
|
||||||
existing.put(k,v);
|
// System.err.println(new RevertiblePut(k,v));
|
||||||
}
|
// existing.put(k,v);
|
||||||
|
// }
|
||||||
}
|
//
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
for(RevertibleOperation operation : needAppend){
|
for(RevertibleOperation operation : needAppend){
|
||||||
RevertibleOperation[] operationArr = null;
|
RevertibleOperation[] operationArr = MapHelper.getValue(this.items,operation.key);
|
||||||
for(Map.Entry<byte[],RevertibleOperation[]> e : this.items.entrySet()){
|
// System.err.println("@ "+operation+ " vs "+Arrays.toString(operationArr));
|
||||||
if(Arrays.equals(e.getKey(),operation.getKey())){
|
|
||||||
operationArr = e.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(operationArr!=null && operationArr.length>=1 && operationArr[operationArr.length-1].equals(operation)){
|
if(operationArr!=null && operationArr.length>=1 && operationArr[operationArr.length-1].equals(operation)){
|
||||||
this.items.put(operationArr[0].getKey(),Arrays.copyOfRange(operationArr,0,operationArr.length-1));
|
RevertibleOperation[] operationArr2 = MapHelper.getValue(this.items,operation.getKey());
|
||||||
RevertibleOperation[] operationArrX = null;
|
List<RevertibleOperation> operationList = Arrays.asList(operationArr2!=null?operationArr2:new RevertibleOperation[0]);
|
||||||
for(Map.Entry<byte[],RevertibleOperation[]> e : this.items.entrySet()){
|
if(!operationList.isEmpty()){
|
||||||
if(Arrays.equals(e.getKey(),operation.getKey())){
|
operationList.remove(operationList.size()-1);
|
||||||
operationArrX = e.getValue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(operationArrX==null || operationArrX.length==0){
|
if(operationList.isEmpty()){
|
||||||
this.items.remove(operation.getKey());
|
MapHelper.remove(this.items,operation.getKey());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!this.enforceIntegrity){
|
if(!this.enforceIntegrity){
|
||||||
RevertibleOperation[] operationArrX = null;
|
RevertibleOperation[] operationArr2 = MapHelper.getValue(this.items,operation.getKey());
|
||||||
for(Map.Entry<byte[],RevertibleOperation[]> e : this.items.entrySet()){
|
List<RevertibleOperation> operationList = Arrays.asList(operationArr2!=null?operationArr2:new RevertibleOperation[0]);
|
||||||
if(Arrays.equals(e.getKey(),operation.getKey())){
|
operationList.add(operation);
|
||||||
operationArrX = e.getValue();
|
this.items.put(operationList.get(0).getKey(),operationList.toArray(new RevertibleOperation[0]));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RevertibleOperation[] newArr = new RevertibleOperation[operationArrX==null?1:operationArrX.length+1];
|
|
||||||
newArr[newArr.length-1] = operation;
|
|
||||||
this.items.put(newArr[0].getKey(),newArr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RevertibleOperation[] operationArrX = null;
|
RevertibleOperation[] operationArrX = null;
|
||||||
|
@ -135,7 +120,8 @@ public class RevertibleOperationStack{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] storedValue = existing.get(operation.getKey());
|
byte[] storedValue = MapHelper.getValue(existing,operation.getKey());
|
||||||
|
// System.err.println(operation);
|
||||||
boolean hasStoredValue = storedValue!=null;
|
boolean hasStoredValue = storedValue!=null;
|
||||||
RevertibleOperation deleteStoredOperation = hasStoredValue?new RevertibleDelete(operation.getKey(),storedValue):null;
|
RevertibleOperation deleteStoredOperation = hasStoredValue?new RevertibleDelete(operation.getKey(),storedValue):null;
|
||||||
boolean deleteStoredOperationInOperationList = false;
|
boolean deleteStoredOperationInOperationList = false;
|
||||||
|
|
|
@ -1,20 +1,31 @@
|
||||||
package com.lbry.database.tests;
|
package com.lbry.database.tests;
|
||||||
|
|
||||||
import com.lbry.database.PrefixDB;
|
import com.lbry.database.PrefixDB;
|
||||||
|
import com.lbry.database.keys.ActiveAmountKey;
|
||||||
import com.lbry.database.keys.ClaimTakeoverKey;
|
import com.lbry.database.keys.ClaimTakeoverKey;
|
||||||
|
import com.lbry.database.revert.RevertibleOperation;
|
||||||
|
import com.lbry.database.revert.RevertiblePut;
|
||||||
import com.lbry.database.util.ArrayHelper;
|
import com.lbry.database.util.ArrayHelper;
|
||||||
|
import com.lbry.database.values.ActiveAmountValue;
|
||||||
import com.lbry.database.values.ClaimTakeoverValue;
|
import com.lbry.database.values.ClaimTakeoverValue;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestInstance;
|
import org.junit.jupiter.api.TestInstance;
|
||||||
|
import org.rocksdb.ReadOptions;
|
||||||
import org.rocksdb.RocksDBException;
|
import org.rocksdb.RocksDBException;
|
||||||
|
import org.rocksdb.RocksIterator;
|
||||||
|
import org.rocksdb.Slice;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
@ -28,7 +39,6 @@ public class RevertablePrefixDBTest{
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public void setUp() throws IOException,RocksDBException{
|
public void setUp() throws IOException,RocksDBException{
|
||||||
this.tmpDir = Files.createTempDirectory("tmp").toFile();
|
this.tmpDir = Files.createTempDirectory("tmp").toFile();
|
||||||
System.err.println(this.tmpDir);
|
|
||||||
this.database = new PrefixDB(this.tmpDir.getAbsolutePath(),32);
|
this.database = new PrefixDB(this.tmpDir.getAbsolutePath(),32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +158,77 @@ public class RevertablePrefixDBTest{
|
||||||
public void testHubDatabaseIterator(){}
|
public void testHubDatabaseIterator(){}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHubDatabaseIteratorStartStop(){}
|
public void testHubDatabaseIteratorStartStop() throws RocksDBException{
|
||||||
|
int txNum = 101;
|
||||||
|
|
||||||
|
for(int x=0;x<255;x++){
|
||||||
|
byte[] claimHash = ArrayHelper.fill(new byte[20],(byte) x);
|
||||||
|
final int txNumInner = txNum;
|
||||||
|
this.database.active_amount.stashPut(new ActiveAmountKey(){{
|
||||||
|
this.claim_hash = claimHash;
|
||||||
|
this.txo_type = 1;
|
||||||
|
this.activation_height = 200;
|
||||||
|
this.tx_num = txNumInner;
|
||||||
|
this.position = 1;
|
||||||
|
}},new ActiveAmountValue(){{
|
||||||
|
this.amount = 100000;
|
||||||
|
}});
|
||||||
|
this.database.active_amount.stashPut(new ActiveAmountKey(){{
|
||||||
|
this.claim_hash = claimHash;
|
||||||
|
this.txo_type = 1;
|
||||||
|
this.activation_height = 201;
|
||||||
|
this.tx_num = txNumInner+1;
|
||||||
|
this.position = 1;
|
||||||
|
}},new ActiveAmountValue(){{
|
||||||
|
this.amount = 200000;
|
||||||
|
}});
|
||||||
|
this.database.active_amount.stashPut(new ActiveAmountKey(){{
|
||||||
|
this.claim_hash = claimHash;
|
||||||
|
this.txo_type = 1;
|
||||||
|
this.activation_height = 202;
|
||||||
|
this.tx_num = txNumInner+2;
|
||||||
|
this.position = 1;
|
||||||
|
}},new ActiveAmountValue(){{
|
||||||
|
this.amount = 300000;
|
||||||
|
}});
|
||||||
|
txNum += 3;
|
||||||
|
}
|
||||||
|
this.database.unsafeCommit();
|
||||||
|
|
||||||
|
BiFunction<byte[],Integer,Long> getActiveAmountAsOfHeight = (claimHash,height) -> {
|
||||||
|
try{
|
||||||
|
ReadOptions readOptions = new ReadOptions().setPrefixSameAsStart(true).setTotalOrderSeek(true);
|
||||||
|
RocksIterator iterator = this.database.active_amount.iterate(readOptions);
|
||||||
|
iterator.seek(ByteBuffer.allocate(1+20+1+4).order(ByteOrder.BIG_ENDIAN).put(this.database.active_amount.prefix().getValue()).put(claimHash).put((byte) 1).putInt(0));
|
||||||
|
byte[] stop = ByteBuffer.allocate(1+20+1+4).order(ByteOrder.BIG_ENDIAN).put(this.database.active_amount.prefix().getValue()).put(claimHash).put((byte) 1).putInt(height).array();
|
||||||
|
byte[] latestValue = null;
|
||||||
|
while(iterator.isValid()){
|
||||||
|
if(ByteBuffer.wrap(iterator.key(),0,1+20+1).equals(ByteBuffer.wrap(stop,0,1+20+1))){
|
||||||
|
int compareStop = ByteBuffer.wrap(iterator.key(),0,1+20+1+4).compareTo(ByteBuffer.wrap(stop));
|
||||||
|
if(compareStop>0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
latestValue = iterator.value();
|
||||||
|
}
|
||||||
|
iterator.next();
|
||||||
|
}
|
||||||
|
return latestValue!=null?this.database.active_amount.unpackValue(latestValue).amount:0;
|
||||||
|
}catch(RocksDBException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
};
|
||||||
|
|
||||||
|
for(int x=0;x<255;x++){
|
||||||
|
byte[] claimHash = ArrayHelper.fill(new byte[20],(byte) x);
|
||||||
|
|
||||||
|
assertEquals(300000,getActiveAmountAsOfHeight.apply(claimHash,300));
|
||||||
|
assertEquals(300000,getActiveAmountAsOfHeight.apply(claimHash,203));
|
||||||
|
assertEquals(300000,getActiveAmountAsOfHeight.apply(claimHash,202));
|
||||||
|
assertEquals(200000,getActiveAmountAsOfHeight.apply(claimHash,201));
|
||||||
|
assertEquals(100000,getActiveAmountAsOfHeight.apply(claimHash,200));
|
||||||
|
assertEquals(0,getActiveAmountAsOfHeight.apply(claimHash,199));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue