From f4d73de4e2563b615285ba1fae11b08e720aa447 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Mon, 7 Nov 2022 14:45:18 -0300 Subject: [PATCH 01/25] feat: wip stream apis Signed-off-by: Jeronimo Irazabal --- .../java/io/codenotary/immudb4j/Entry.java | 5 + .../io/codenotary/immudb4j/ImmuClient.java | 552 +++++++++++++++--- .../java/io/codenotary/immudb4j/Utils.java | 1 - .../java/io/codenotary/immudb4j/ZEntry.java | 8 + .../immudb4j/basics/LatchHolder.java | 38 ++ .../immudb4j/BasicImmuClientTest.java | 6 +- .../io/codenotary/immudb4j/HistoryTest.java | 9 +- .../immudb4j/ImmuClientIntegrationTest.java | 2 +- .../immudb4j/ListDatabasesTest.java | 2 +- .../io/codenotary/immudb4j/ListUsersTest.java | 2 +- .../LoginAndHealthCheckAndCleanIndexTest.java | 2 +- .../immudb4j/MultidatabaseTest.java | 2 +- .../codenotary/immudb4j/MultithreadTest.java | 4 +- .../io/codenotary/immudb4j/ReferenceTest.java | 2 +- .../java/io/codenotary/immudb4j/ScanTest.java | 5 +- .../immudb4j/SetAllAndGetAllTest.java | 9 +- .../io/codenotary/immudb4j/SetAndGetTest.java | 2 +- .../io/codenotary/immudb4j/ShutdownTest.java | 4 +- .../io/codenotary/immudb4j/StateTest.java | 6 +- .../java/io/codenotary/immudb4j/TxTest.java | 4 +- .../io/codenotary/immudb4j/UserMgmtTest.java | 2 +- .../immudb4j/VerifiedSetAndGetTest.java | 4 +- .../java/io/codenotary/immudb4j/ZAddTest.java | 2 +- 23 files changed, 552 insertions(+), 121 deletions(-) create mode 100644 src/main/java/io/codenotary/immudb4j/basics/LatchHolder.java diff --git a/src/main/java/io/codenotary/immudb4j/Entry.java b/src/main/java/io/codenotary/immudb4j/Entry.java index 87b6e37..15a3b6d 100644 --- a/src/main/java/io/codenotary/immudb4j/Entry.java +++ b/src/main/java/io/codenotary/immudb4j/Entry.java @@ -30,6 +30,11 @@ public class Entry { private Entry() {} + public Entry(byte[] key, byte[] value) { + this.key = key; + this.value = value; + } + public static Entry valueOf(ImmudbProto.Entry e) { final Entry entry = new Entry(); diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 99ad078..6ffcbf4 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -19,7 +19,9 @@ import com.google.protobuf.Empty; import io.codenotary.immudb.ImmuServiceGrpc; import io.codenotary.immudb.ImmudbProto; +import io.codenotary.immudb.ImmudbProto.Chunk; import io.codenotary.immudb.ImmudbProto.ScanRequest; +import io.codenotary.immudb4j.basics.LatchHolder; import io.codenotary.immudb4j.crypto.CryptoUtils; import io.codenotary.immudb4j.crypto.DualProof; import io.codenotary.immudb4j.crypto.InclusionProof; @@ -29,13 +31,17 @@ import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; import io.grpc.ConnectivityState; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; import java.util.Timer; import java.util.TimerTask; @@ -53,35 +59,37 @@ public class ImmuClient { private final PublicKey serverSigningKey; private final ImmuStateHolder stateHolder; private long keepAlivePeriod; + private int chunkSize; private ManagedChannel channel; - private final ImmuServiceGrpc.ImmuServiceBlockingStub stub; + private final ImmuServiceGrpc.ImmuServiceBlockingStub blockingStub; + private final ImmuServiceGrpc.ImmuServiceStub nonBlockingStub; private Session session; private Timer sessionHeartBeat; public ImmuClient(Builder builder) { - this.stateHolder = builder.getStateHolder(); - this.serverSigningKey = builder.getServerSigningKey(); - this.keepAlivePeriod = builder.getKeepAlivePeriod(); - this.stub = createStubFrom(builder); + stateHolder = builder.getStateHolder(); + serverSigningKey = builder.getServerSigningKey(); + keepAlivePeriod = builder.getKeepAlivePeriod(); + chunkSize = builder.getChunkSize(); + + channel = ManagedChannelBuilder + .forAddress(builder.getServerUrl(), builder.getServerPort()) + .usePlaintext() + .intercept(new ImmudbAuthRequestInterceptor(this)) + .build(); + + blockingStub = ImmuServiceGrpc.newBlockingStub(channel); + nonBlockingStub = ImmuServiceGrpc.newStub(channel); } public static Builder newBuilder() { return new Builder(); } - - private ImmuServiceGrpc.ImmuServiceBlockingStub createStubFrom(Builder builder) { - channel = ManagedChannelBuilder.forAddress(builder.getServerUrl(), builder.getServerPort()) - .usePlaintext() - .intercept(new ImmudbAuthRequestInterceptor(this)) - .build(); - - return ImmuServiceGrpc.newBlockingStub(channel); - } - - public synchronized void shutdown() { + + public synchronized void shutdown() throws InterruptedException { if (channel == null) { return; } @@ -91,46 +99,43 @@ public synchronized void shutdown() { } try { - channel.shutdown(); - if (!channel.isShutdown()) { - try { - channel.awaitTermination(1, TimeUnit.SECONDS); - } catch (InterruptedException e) { - channel.shutdownNow(); - } - } + channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); } finally { channel = null; } } - Session getSession() { - return this.session; + protected synchronized Session getSession() { + return session; } - public synchronized void openSession(String username, String password, String database) { - if (this.session != null) { + public synchronized void openSession(String database) { + openSession(database, "", ""); + } + + public synchronized void openSession(String database, String username, String password) { + if (session != null) { throw new IllegalStateException("session already opened"); } final ImmudbProto.OpenSessionRequest req = ImmudbProto.OpenSessionRequest .newBuilder() + .setDatabaseName(database) .setUsername(Utils.toByteString(username)) .setPassword(Utils.toByteString(password)) - .setDatabaseName(database) .build(); - final ImmudbProto.OpenSessionResponse resp = this.stub.openSession(req); + final ImmudbProto.OpenSessionResponse resp = this.blockingStub.openSession(req); - this.session = new Session(resp.getSessionID(), database); + session = new Session(resp.getSessionID(), database); - this.sessionHeartBeat = new Timer(); + sessionHeartBeat = new Timer(); - this.sessionHeartBeat.schedule(new TimerTask() { + sessionHeartBeat.schedule(new TimerTask() { @Override public void run() { try { - stub.keepAlive(Empty.getDefaultInstance()); + blockingStub.keepAlive(Empty.getDefaultInstance()); } catch (Exception e) { e.printStackTrace(); } @@ -139,16 +144,16 @@ public void run() { } public synchronized void closeSession() { - if (this.session == null) { + if (session == null) { throw new IllegalStateException("no open session"); } - this.sessionHeartBeat.cancel(); + sessionHeartBeat.cancel(); try { - this.stub.closeSession(Empty.getDefaultInstance()); + blockingStub.closeSession(Empty.getDefaultInstance()); } finally { - this.session = null; + session = null; } } @@ -157,14 +162,14 @@ public synchronized void closeSession() { * If nothing exists already, it is fetched from the server and save it locally. */ private ImmuState state() throws VerificationException { - if (this.session == null) { + if (session == null) { throw new IllegalStateException("no open session"); } - ImmuState state = this.stateHolder.getState(this.session.getDatabase()); + ImmuState state = stateHolder.getState(session.getDatabase()); if (state == null) { - state = this.currentState(); + state = currentState(); stateHolder.setState(state); } @@ -179,19 +184,19 @@ private ImmuState state() throws VerificationException { * Note: local state is not updated because this is not a verified operation */ public synchronized ImmuState currentState() throws VerificationException { - if (this.session == null) { + if (session == null) { throw new IllegalStateException("no open session"); } - final ImmudbProto.ImmutableState state = this.stub.currentState(Empty.getDefaultInstance()); + final ImmudbProto.ImmutableState state = blockingStub.currentState(Empty.getDefaultInstance()); final ImmuState immuState = ImmuState.valueOf(state); - if (!this.session.getDatabase().equals(immuState.getDatabase())) { + if (!session.getDatabase().equals(immuState.getDatabase())) { throw new VerificationException("database mismatch"); } - if (!immuState.checkSignature(this.serverSigningKey)) { + if (!immuState.checkSignature(serverSigningKey)) { throw new VerificationException("State signature verification failed"); } @@ -203,7 +208,7 @@ public synchronized ImmuState currentState() throws VerificationException { // public synchronized void createDatabase(String database) { - if (this.session == null) { + if (session == null) { throw new IllegalStateException("no open session"); } @@ -211,16 +216,65 @@ public synchronized void createDatabase(String database) { .setName(database) .build(); - this.stub.createDatabaseV2(req); + blockingStub.createDatabaseV2(req); + } + + // LoadDatabase loads database on the server. A database is not loaded + // if it has AutoLoad setting set to false or if it failed to load during + // immudb startup. + // + // This call requires SysAdmin permission level or admin permission to the database. + public synchronized void loadDatabase(String database) { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.LoadDatabaseRequest req = ImmudbProto.LoadDatabaseRequest.newBuilder() + .setDatabase(database) + .build(); + + blockingStub.loadDatabase(req); + } + + // UnloadDatabase unloads database on the server. Such database becomes inaccessible + // by the client and server frees internal resources allocated for that database. + // + // This call requires SysAdmin permission level or admin permission to the database. + public synchronized void unloadDatabase(String database) { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.UnloadDatabaseRequest req = ImmudbProto.UnloadDatabaseRequest.newBuilder() + .setDatabase(database) + .build(); + + blockingStub.unloadDatabase(req); + } + + // DeleteDatabase removes an unloaded database. + // This also removes locally stored files used by the database. + // + // This call requires SysAdmin permission level or admin permission to the database. + public synchronized void deleteDatabase(String database) { + if (session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.DeleteDatabaseRequest req = ImmudbProto.DeleteDatabaseRequest.newBuilder() + .setDatabase(database) + .build(); + + blockingStub.deleteDatabase(req); } public synchronized List databases() { - if (this.session == null) { + if (session == null) { throw new IllegalStateException("no open session"); } final ImmudbProto.DatabaseListRequestV2 req = ImmudbProto.DatabaseListRequestV2.newBuilder().build(); - final ImmudbProto.DatabaseListResponseV2 resp = this.stub.databaseListV2(req); + final ImmudbProto.DatabaseListResponseV2 resp = blockingStub.databaseListV2(req); final List list = new ArrayList<>(resp.getDatabasesCount()); @@ -254,7 +308,7 @@ public synchronized Entry getAtTx(byte[] key, long tx) throws KeyNotFoundExcepti .build(); try { - return Entry.valueOf(this.stub.get(req)); + return Entry.valueOf(blockingStub.get(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -275,7 +329,7 @@ public synchronized Entry getSinceTx(byte[] key, long tx) throws KeyNotFoundExce .build(); try { - return Entry.valueOf(this.stub.get(req)); + return Entry.valueOf(blockingStub.get(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -296,7 +350,7 @@ public synchronized Entry getAtRevision(byte[] key, long rev) throws KeyNotFound .build(); try { - return Entry.valueOf(this.stub.get(req)); + return Entry.valueOf(blockingStub.get(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -314,7 +368,7 @@ public synchronized List getAll(List keys) { } final ImmudbProto.KeyListRequest req = ImmudbProto.KeyListRequest.newBuilder().addAllKeys(keysBS).build(); - final ImmudbProto.Entries entries = this.stub.getAll(req); + final ImmudbProto.Entries entries = blockingStub.getAll(req); final List result = new ArrayList<>(entries.getEntriesCount()); @@ -338,7 +392,7 @@ public Entry verifiedGetAtTx(String key, long tx) throws KeyNotFoundException, V } public synchronized Entry verifiedGetAtTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { - final ImmuState state = this.state(); + final ImmuState state = state(); final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) @@ -355,7 +409,7 @@ public Entry verifiedGetSinceTx(String key, long tx) throws KeyNotFoundException public synchronized Entry verifiedGetSinceTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { - final ImmuState state = this.state(); + final ImmuState state = state(); final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) @@ -372,7 +426,7 @@ public Entry verifiedGetAtRevision(String key, long rev) throws KeyNotFoundExcep public synchronized Entry verifiedGetAtRevision(byte[] key, long rev) throws KeyNotFoundException, VerificationException { - final ImmuState state = this.state(); + final ImmuState state = state(); final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) @@ -392,7 +446,7 @@ private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) final ImmudbProto.VerifiableEntry vEntry; try { - vEntry = this.stub.verifiableGet(vGetReq); + vEntry = blockingStub.verifiableGet(vGetReq); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -464,7 +518,7 @@ private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) } final ImmuState newState = new ImmuState( - this.session.getDatabase(), + session.getDatabase(), targetId, targetAlh, vEntry.getVerifiableTx().getSignature().toByteArray()); @@ -473,7 +527,7 @@ private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throw new VerificationException("State signature verification failed"); } - this.stateHolder.setState(newState); + stateHolder.setState(newState); return Entry.valueOf(vEntry.getEntry()); } @@ -492,7 +546,7 @@ public synchronized TxHeader delete(byte[] key) throws KeyNotFoundException { .addKeys(Utils.toByteString(key)) .build(); - return TxHeader.valueOf(this.stub.delete(req)); + return TxHeader.valueOf(blockingStub.delete(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -513,7 +567,7 @@ public List history(String key, int limit, long offset, boolean desc) thr public synchronized List history(byte[] key, int limit, long offset, boolean desc) throws KeyNotFoundException { try { - ImmudbProto.Entries entries = this.stub.history(ImmudbProto.HistoryRequest.newBuilder() + ImmudbProto.Entries entries = blockingStub.history(ImmudbProto.HistoryRequest.newBuilder() .setKey(Utils.toByteString(key)) .setLimit(limit) .setOffset(offset) @@ -579,7 +633,7 @@ public synchronized List scan(byte[] prefix, byte[] seekKey, byte[] endKe .setDesc(desc) .build(); - final ImmudbProto.Entries entries = this.stub.scan(req); + final ImmudbProto.Entries entries = blockingStub.scan(req); return buildList(entries); } @@ -598,7 +652,7 @@ public synchronized TxHeader set(byte[] key, byte[] value) throws CorruptedDataE .build(); final ImmudbProto.SetRequest req = ImmudbProto.SetRequest.newBuilder().addKVs(kv).build(); - final ImmudbProto.TxHeader txHdr = this.stub.set(req); + final ImmudbProto.TxHeader txHdr = blockingStub.set(req); if (txHdr.getNentries() != 1) { throw new CorruptedDataException(); @@ -619,7 +673,7 @@ public synchronized TxHeader setAll(List kvList) throws CorruptedDataExc reqBuilder.addKVs(kvBuilder.build()); } - final ImmudbProto.TxHeader txHdr = this.stub.set(reqBuilder.build()); + final ImmudbProto.TxHeader txHdr = blockingStub.set(reqBuilder.build()); if (txHdr.getNentries() != kvList.size()) { throw new CorruptedDataException(); @@ -649,7 +703,7 @@ public synchronized TxHeader setReference(byte[] key, byte[] referencedKey, long .setBoundRef(atTx > 0) .build(); - final ImmudbProto.TxHeader txHdr = this.stub.setReference(req); + final ImmudbProto.TxHeader txHdr = blockingStub.setReference(req); if (txHdr.getNentries() != 1) { throw new CorruptedDataException(); @@ -663,7 +717,7 @@ public TxHeader verifiedSet(String key, byte[] value) throws VerificationExcepti } public synchronized TxHeader verifiedSet(byte[] key, byte[] value) throws VerificationException { - final ImmuState state = this.state(); + final ImmuState state = state(); final ImmudbProto.KeyValue kv = ImmudbProto.KeyValue.newBuilder() .setKey(Utils.toByteString(key)) @@ -675,7 +729,7 @@ public synchronized TxHeader verifiedSet(byte[] key, byte[] value) throws Verifi .setProveSinceTx(state.getTxId()) .build(); - final ImmudbProto.VerifiableTx vtx = this.stub.verifiableSet(vSetReq); + final ImmudbProto.VerifiableTx vtx = blockingStub.verifiableSet(vSetReq); final int ne = vtx.getTx().getHeader().getNentries(); @@ -711,7 +765,7 @@ public synchronized TxHeader verifiedSet(byte[] key, byte[] value) throws Verifi throw new VerificationException("State signature verification failed"); } - this.stateHolder.setState(newState); + stateHolder.setState(newState); return TxHeader.valueOf(vtx.getTx().getHeader()); } @@ -723,7 +777,7 @@ public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey) throws Ve public synchronized TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx) throws VerificationException { - final ImmuState state = this.state(); + final ImmuState state = state(); final ImmudbProto.ReferenceRequest refReq = ImmudbProto.ReferenceRequest.newBuilder() .setKey(Utils.toByteString(key)) @@ -737,7 +791,7 @@ public synchronized TxHeader verifiedSetReference(byte[] key, byte[] referencedK .setProveSinceTx(state.getTxId()) .build(); - final ImmudbProto.VerifiableTx vtx = this.stub.verifiableSetReference(vRefReq); + final ImmudbProto.VerifiableTx vtx = blockingStub.verifiableSetReference(vRefReq); final int vtxNentries = vtx.getTx().getHeader().getNentries(); if (vtxNentries != 1) { @@ -779,7 +833,7 @@ public synchronized TxHeader verifiedSetReference(byte[] key, byte[] referencedK throw new VerificationException("State signature verification failed"); } - this.stateHolder.setState(newState); + stateHolder.setState(newState); return TxHeader.valueOf(vtx.getTx().getHeader()); } @@ -819,7 +873,7 @@ public TxHeader zAdd(byte[] set, byte[] key, double score) throws CorruptedDataE } public synchronized TxHeader zAdd(byte[] set, byte[] key, long atTx, double score) throws CorruptedDataException { - final ImmudbProto.TxHeader txHdr = this.stub.zAdd( + final ImmudbProto.TxHeader txHdr = blockingStub.zAdd( ImmudbProto.ZAddRequest.newBuilder() .setSet(Utils.toByteString(set)) .setKey(Utils.toByteString(key)) @@ -850,7 +904,7 @@ public TxHeader verifiedZAdd(String set, String key, long atTx, double score) th public synchronized TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) throws VerificationException { - final ImmuState state = this.state(); + final ImmuState state = state(); final ImmudbProto.ZAddRequest zAddReq = ImmudbProto.ZAddRequest.newBuilder() .setSet(Utils.toByteString(set)) @@ -865,7 +919,7 @@ public synchronized TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, dou .setProveSinceTx(state.getTxId()) .build(); - final ImmudbProto.VerifiableTx vtx = this.stub.verifiableZAdd(vZAddReq); + final ImmudbProto.VerifiableTx vtx = blockingStub.verifiableZAdd(vZAddReq); if (vtx.getTx().getHeader().getNentries() != 1) { throw new VerificationException("Data is corrupted."); @@ -904,7 +958,7 @@ public synchronized TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, dou throw new VerificationException("State signature verification failed"); } - this.stateHolder.setState(newState); + stateHolder.setState(newState); return TxHeader.valueOf(vtx.getTx().getHeader()); } @@ -921,7 +975,7 @@ public synchronized List zScan(byte[] set, long limit, boolean reverse) .setDesc(reverse) .build(); - final ImmudbProto.ZEntries zEntries = this.stub.zScan(req); + final ImmudbProto.ZEntries zEntries = blockingStub.zScan(req); return buildList(zEntries); } @@ -932,7 +986,7 @@ public synchronized List zScan(byte[] set, long limit, boolean reverse) public synchronized Tx txById(long txId) throws TxNotFoundException, NoSuchAlgorithmException { try { - final ImmudbProto.Tx tx = this.stub.txById(ImmudbProto.TxRequest.newBuilder().setTx(txId).build()); + final ImmudbProto.Tx tx = blockingStub.txById(ImmudbProto.TxRequest.newBuilder().setTx(txId).build()); return Tx.valueOf(tx); } catch (StatusRuntimeException e) { if (e.getMessage().contains("tx not found")) { @@ -944,7 +998,7 @@ public synchronized Tx txById(long txId) throws TxNotFoundException, NoSuchAlgor } public synchronized Tx verifiedTxById(long txId) throws TxNotFoundException, VerificationException { - final ImmuState state = this.state(); + final ImmuState state = state(); final ImmudbProto.VerifiableTxRequest vTxReq = ImmudbProto.VerifiableTxRequest.newBuilder() .setTx(txId) @@ -954,7 +1008,7 @@ public synchronized Tx verifiedTxById(long txId) throws TxNotFoundException, Ver final ImmudbProto.VerifiableTx vtx; try { - vtx = this.stub.verifiableTxById(vTxReq); + vtx = blockingStub.verifiableTxById(vTxReq); } catch (StatusRuntimeException e) { if (e.getMessage().contains("tx not found")) { throw new TxNotFoundException(); @@ -1014,7 +1068,7 @@ public synchronized Tx verifiedTxById(long txId) throws TxNotFoundException, Ver public synchronized List txScan(long initialTxId) { final ImmudbProto.TxScanRequest req = ImmudbProto.TxScanRequest.newBuilder().setInitialTx(initialTxId).build(); - final ImmudbProto.TxList txList = this.stub.txScan(req); + final ImmudbProto.TxList txList = blockingStub.txScan(req); return buildList(txList); } @@ -1025,10 +1079,277 @@ public synchronized List txScan(long initialTxId, int limit, boolean desc) { .setLimit(limit) .setDesc(desc) .build(); - final ImmudbProto.TxList txList = this.stub.txScan(req); + final ImmudbProto.TxList txList = blockingStub.txScan(req); return buildList(txList); } + // + // ========== STREAMS ========== + // + + private StreamObserver txHeaderStreamObserver(LatchHolder latchHolder) { + return new StreamObserver() { + @Override + public void onCompleted() { + } + + @Override + public void onError(Throwable cause) { + throw new RuntimeException(cause); + } + + @Override + public void onNext(ImmudbProto.TxHeader hdr) { + latchHolder.doneWith(hdr); + } + }; + } + + private void chunkIt(byte[] bs, StreamObserver streamObserver) { + final ByteBuffer buf = ByteBuffer.allocate(Long.BYTES+bs.length).order(ByteOrder.BIG_ENDIAN); + + buf.putLong(bs.length); + buf.put(bs); + + final Chunk chunk = Chunk.newBuilder().setContent(Utils.toByteString(buf.array())).build(); + + streamObserver.onNext(chunk); + } + + private byte[] dechunkIt(Iterator chunks) { + final Chunk firstChunk = chunks.next(); + final byte[] firstChunkContent = firstChunk.getContent().toByteArray(); + + if (firstChunkContent.length < Long.BYTES) { + throw new RuntimeException("invalid chunk"); + } + + final ByteBuffer b = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.BIG_ENDIAN); + b.put(firstChunkContent, 0, Long.BYTES); + + int payloadSize = (int) b.getLong(0); + + final ByteBuffer buf = ByteBuffer.allocate(payloadSize); + buf.put(firstChunkContent, Long.BYTES, firstChunkContent.length-Long.BYTES); + + while (buf.position() < payloadSize) { + Chunk chunk = chunks.next(); + buf.put(chunk.getContent().toByteArray()); + } + + return buf.array(); + } + + private Iterator entryIterator(Iterator chunks) { + return new Iterator() { + + @Override + public boolean hasNext() { + return chunks.hasNext(); + } + + @Override + public Entry next() { + return new Entry(dechunkIt(chunks), dechunkIt(chunks)); + } + + }; + } + + private Iterator zentryIterator(Iterator chunks) { + return new Iterator() { + + @Override + public boolean hasNext() { + return chunks.hasNext(); + } + + @Override + public ZEntry next() { + final byte[] set = dechunkIt(chunks); + final byte[] key = dechunkIt(chunks); + + final ByteBuffer b = ByteBuffer.allocate(Double.BYTES + Long.BYTES).order(ByteOrder.BIG_ENDIAN); + b.put(dechunkIt(chunks)); // score + b.put(dechunkIt(chunks)); // atTx + + double score = b.getDouble(0); + long atTx = b.getLong(Double.BYTES); + + final byte[] value = dechunkIt(chunks); + + final Entry entry = new Entry(key, value); + + return new ZEntry(set, key, score, atTx, entry); + } + + }; + } + + // + // ========== STREAM SET ========== + // + + public TxHeader streamSet(String key, byte[] value) throws InterruptedException { + return streamSet(Utils.toByteArray(key), value); + } + + public synchronized TxHeader streamSet(byte[] key, byte[] value) throws InterruptedException { + final LatchHolder latchHolder = new LatchHolder<>(); + final StreamObserver streamObserver = nonBlockingStub.streamSet(txHeaderStreamObserver(latchHolder)); + + chunkIt(key, streamObserver); + chunkIt(value, streamObserver); + + streamObserver.onCompleted(); + + return TxHeader.valueOf(latchHolder.awaitValue()); + } + + public synchronized TxHeader streamSetAll(List kvList) throws InterruptedException { + final LatchHolder latchHolder = new LatchHolder<>(); + final StreamObserver streamObserver = nonBlockingStub.streamSet(txHeaderStreamObserver(latchHolder)); + + for (KVPair kv : kvList) { + chunkIt(kv.getKey(), streamObserver); + chunkIt(kv.getValue(), streamObserver); + } + + streamObserver.onCompleted(); + + return TxHeader.valueOf(latchHolder.awaitValue()); + } + + // + // ========== STREAM GET ========== + // + + public Entry streamGet(String key) throws KeyNotFoundException { + return streamGet(Utils.toByteArray(key)); + } + + public synchronized Entry streamGet(byte[] key) throws KeyNotFoundException { + final ImmudbProto.KeyRequest req = ImmudbProto.KeyRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .build(); + + try { + final Iterator chunks = blockingStub.streamGet(req); + return new Entry(dechunkIt(chunks), dechunkIt(chunks)); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + throw new KeyNotFoundException(); + } + + throw e; + } + } + + // + // ========== STREAM SCAN ========== + // + + public Iterator streamScan(String prefix) { + return streamScan(Utils.toByteArray(prefix)); + } + + public Iterator streamScan(byte[] prefix) { + return streamScan(prefix, 0, false); + } + + public Iterator streamScan(String prefix, long limit, boolean desc) { + return streamScan(Utils.toByteArray(prefix), limit, desc); + } + + public Iterator streamScan(byte[] prefix, long limit, boolean desc) { + return streamScan(prefix, null, limit, desc); + } + + public Iterator streamScan(String prefix, String seekKey, long limit, boolean desc) { + return streamScan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), limit, desc); + } + + public Iterator streamScan(String prefix, String seekKey, String endKey, long limit, boolean desc) { + return streamScan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), Utils.toByteArray(endKey), limit, desc); + } + + public Iterator streamScan(byte[] prefix, byte[] seekKey, long limit, boolean desc) { + return streamScan(prefix, seekKey, null, limit, desc); + } + + public Iterator streamScan(byte[] prefix, byte[] seekKey, byte[] endKey, long limit, boolean desc) { + return streamScan(prefix, seekKey, endKey, false, false, limit, desc); + } + + public synchronized Iterator streamScan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, + boolean inclusiveEnd, + long limit, boolean desc) { + final ImmudbProto.ScanRequest req = ScanRequest.newBuilder() + .setPrefix(Utils.toByteString(prefix)) + .setSeekKey(Utils.toByteString(seekKey)) + .setEndKey(Utils.toByteString(endKey)) + .setInclusiveSeek(inclusiveSeek) + .setInclusiveEnd(inclusiveEnd) + .setLimit(limit) + .setDesc(desc) + .build(); + + final Iterator chunks = blockingStub.streamScan(req); + + return entryIterator(chunks); + } + + // + // ========== STREAM ZSCAN ========== + // + + public Iterator streamZScan(String set, long limit, boolean reverse) { + return streamZScan(Utils.toByteArray(set), limit, reverse); + } + + public synchronized Iterator streamZScan(byte[] set, long limit, boolean reverse) { + final ImmudbProto.ZScanRequest req = ImmudbProto.ZScanRequest + .newBuilder() + .setSet(Utils.toByteString(set)) + .setLimit(limit) + .setDesc(reverse) + .build(); + + final Iterator chunks = blockingStub.streamZScan(req); + + return zentryIterator(chunks); + } + + // + // ========== STREAM HISTORY ========== + // + + public Iterator streamHistory(String key, int limit, long offset, boolean desc) throws KeyNotFoundException { + return streamHistory(Utils.toByteArray(key), limit, offset, desc); + } + + public synchronized Iterator streamHistory(byte[] key, int limit, long offset, boolean desc) + throws KeyNotFoundException { + try { + ImmudbProto.HistoryRequest req = ImmudbProto.HistoryRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .setLimit(limit) + .setOffset(offset) + .setDesc(desc) + .build(); + + final Iterator chunks = blockingStub.streamHistory(req); + + return entryIterator(chunks); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + throw new KeyNotFoundException(); + } + + throw e; + } + } + // // ========== HEALTH ========== // @@ -1042,7 +1363,7 @@ public boolean isShutdown() { } public synchronized boolean healthCheck() { - return this.stub.serverInfo(ImmudbProto.ServerInfoRequest.getDefaultInstance()) != null; + return blockingStub.serverInfo(ImmudbProto.ServerInfoRequest.getDefaultInstance()) != null; } // @@ -1050,7 +1371,7 @@ public synchronized boolean healthCheck() { // public synchronized List listUsers() { - final ImmudbProto.UserList userList = this.stub.listUsers(Empty.getDefaultInstance()); + final ImmudbProto.UserList userList = blockingStub.listUsers(Empty.getDefaultInstance()); return userList.getUsersList() .stream() @@ -1080,7 +1401,17 @@ public synchronized void createUser(String user, String password, Permission per .build(); // noinspection ResultOfMethodCallIgnored - this.stub.createUser(createUserRequest); + blockingStub.createUser(createUserRequest); + } + + public synchronized void activateUser(String user, boolean active) { + final ImmudbProto.SetActiveUserRequest req = ImmudbProto.SetActiveUserRequest.newBuilder() + .setUsername(user) + .setActive(active) + .build(); + + // noinspection ResultOfMethodCallIgnored + blockingStub.setActiveUser(req); } public synchronized void changePassword(String user, String oldPassword, String newPassword) { @@ -1091,7 +1422,31 @@ public synchronized void changePassword(String user, String oldPassword, String .build(); // noinspection ResultOfMethodCallIgnored - this.stub.changePassword(changePasswordRequest); + blockingStub.changePassword(changePasswordRequest); + } + + public synchronized void grantPermission(String user, String database, int permissions) { + final ImmudbProto.ChangePermissionRequest req = ImmudbProto.ChangePermissionRequest.newBuilder() + .setUsername(user) + .setAction(ImmudbProto.PermissionAction.GRANT) + .setDatabase(database) + .setPermission(permissions) + .build(); + + // noinspection ResultOfMethodCallIgnored + blockingStub.changePermission(req); + } + + public synchronized void revokePermission(String user, String database, int permissions) { + final ImmudbProto.ChangePermissionRequest req = ImmudbProto.ChangePermissionRequest.newBuilder() + .setUsername(user) + .setAction(ImmudbProto.PermissionAction.REVOKE) + .setDatabase(database) + .setPermission(permissions) + .build(); + + // noinspection ResultOfMethodCallIgnored + blockingStub.changePermission(req); } // @@ -1104,11 +1459,11 @@ public synchronized void flushIndex(float cleanupPercentage, boolean synced) { .setSynced(synced) .build(); - this.stub.flushIndex(req); + blockingStub.flushIndex(req); } public synchronized void compactIndex() { - this.stub.compactIndex(Empty.getDefaultInstance()); + blockingStub.compactIndex(Empty.getDefaultInstance()); } // @@ -1155,13 +1510,16 @@ public static class Builder { private long keepAlivePeriod; + private int chunkSize; + private ImmuStateHolder stateHolder; private Builder() { - this.serverUrl = "localhost"; - this.serverPort = 3322; - this.stateHolder = new SerializableImmuStateHolder(); - this.keepAlivePeriod = 60 * 1000; // 1 minute + serverUrl = "localhost"; + serverPort = 3322; + stateHolder = new SerializableImmuStateHolder(); + keepAlivePeriod = 60 * 1000; // 1 minute + chunkSize = 64 * 1024; // 64 * 1024 64 KiB } public ImmuClient build() { @@ -1169,7 +1527,7 @@ public ImmuClient build() { } public String getServerUrl() { - return this.serverUrl; + return serverUrl; } public Builder withServerUrl(String serverUrl) { @@ -1195,7 +1553,7 @@ public PublicKey getServerSigningKey() { * be used for verifying the state signature received from the server. */ public Builder withServerSigningKey(String publicKeyFilename) throws Exception { - this.serverSigningKey = CryptoUtils.getDERPublicKey(publicKeyFilename); + serverSigningKey = CryptoUtils.getDERPublicKey(publicKeyFilename); return this; } @@ -1217,6 +1575,14 @@ public Builder withStateHolder(ImmuStateHolder stateHolder) { return this; } + public Builder withChunkSize(int chunkSize) { + this.chunkSize = chunkSize; + return this; + } + + public int getChunkSize() { + return chunkSize; + } } } diff --git a/src/main/java/io/codenotary/immudb4j/Utils.java b/src/main/java/io/codenotary/immudb4j/Utils.java index 2366b9d..2a74d37 100644 --- a/src/main/java/io/codenotary/immudb4j/Utils.java +++ b/src/main/java/io/codenotary/immudb4j/Utils.java @@ -119,5 +119,4 @@ public static void copy(byte[] src, int srcPos, int length, byte[] dest) { public static void copy(byte[] src, int srcPos, int length, byte[] dest, int destPos) { System.arraycopy(src, srcPos, dest, destPos, length); } - } diff --git a/src/main/java/io/codenotary/immudb4j/ZEntry.java b/src/main/java/io/codenotary/immudb4j/ZEntry.java index c13303e..6d58767 100644 --- a/src/main/java/io/codenotary/immudb4j/ZEntry.java +++ b/src/main/java/io/codenotary/immudb4j/ZEntry.java @@ -39,6 +39,14 @@ public class ZEntry { private ZEntry() {} + public ZEntry(byte[] set, byte[] key, double score, long atTx, Entry entry) { + this.set = set; + this.key = key; + this.score = score; + this.atTx = atTx; + this.entry = entry; + } + public static ZEntry valueOf(ImmudbProto.ZEntry e) { final ZEntry entry = new ZEntry(); diff --git a/src/main/java/io/codenotary/immudb4j/basics/LatchHolder.java b/src/main/java/io/codenotary/immudb4j/basics/LatchHolder.java new file mode 100644 index 0000000..37ab10e --- /dev/null +++ b/src/main/java/io/codenotary/immudb4j/basics/LatchHolder.java @@ -0,0 +1,38 @@ +/* +Copyright 2022 CodeNotary, Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package io.codenotary.immudb4j.basics; + +import java.util.concurrent.CountDownLatch; + +public class LatchHolder { + + private T value; + private CountDownLatch doneLatch; + + public LatchHolder() { + doneLatch = new CountDownLatch(1); + } + + public T awaitValue() throws InterruptedException { + doneLatch.await(); + return value; + } + + public void doneWith(T value) { + this.value = value; + doneLatch.countDown(); + } +} \ No newline at end of file diff --git a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java index eaca0a5..030664d 100644 --- a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java +++ b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java @@ -27,8 +27,8 @@ public class BasicImmuClientTest extends ImmuClientIntegrationTest { @Test(testName = "set, get") - public void t1() throws VerificationException, CorruptedDataException { - immuClient.openSession("immudb", "immudb", "defaultdb"); + public void t1() throws VerificationException, CorruptedDataException, InterruptedException { + immuClient.openSession("defaultdb", "immudb", "immudb"); byte[] v0 = new byte[] { 0, 1, 2, 3 }; byte[] v1 = new byte[] { 3, 2, 1, 0 }; @@ -68,7 +68,7 @@ public void t1() throws VerificationException, CorruptedDataException { @Test(testName = "setAll, getAll") public void t2() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); List keys = new ArrayList<>(); keys.add("k0"); diff --git a/src/test/java/io/codenotary/immudb4j/HistoryTest.java b/src/test/java/io/codenotary/immudb4j/HistoryTest.java index f0b0cce..4278b13 100644 --- a/src/test/java/io/codenotary/immudb4j/HistoryTest.java +++ b/src/test/java/io/codenotary/immudb4j/HistoryTest.java @@ -21,13 +21,14 @@ import org.testng.annotations.Test; import java.nio.charset.StandardCharsets; +import java.util.Iterator; import java.util.List; public class HistoryTest extends ImmuClientIntegrationTest { @Test(testName = "set, history", priority = 2) public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); byte[] value1 = {0, 1, 2, 3}; byte[] value2 = {4, 5, 6, 7}; @@ -70,6 +71,12 @@ public void t1() { Assert.assertNotNull(historyResponse2); Assert.assertEquals(historyResponse2.size(), 1); + Iterator entriesIt = immuClient.streamHistory("history2", 10, 2, false); + Assert.assertTrue(entriesIt.hasNext()); + + Entry entry = entriesIt.next(); + Assert.assertNotNull(entry); + try { immuClient.history("nonExisting", 10, 0, false); Assert.fail("key not found exception expected"); diff --git a/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java b/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java index 19e42bf..07bdc0d 100644 --- a/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java +++ b/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java @@ -38,7 +38,7 @@ public static void beforeClass() throws IOException { } @AfterClass - public static void afterClass() { + public static void afterClass() throws InterruptedException { immuClient.shutdown(); } diff --git a/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java b/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java index fc19652..d45e578 100644 --- a/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java +++ b/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java @@ -23,7 +23,7 @@ public class ListDatabasesTest extends ImmuClientIntegrationTest { @Test(testName = "databases") public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); List databases = immuClient.databases(); if (databases.size() > 0) { diff --git a/src/test/java/io/codenotary/immudb4j/ListUsersTest.java b/src/test/java/io/codenotary/immudb4j/ListUsersTest.java index 6e8f762..afe30c0 100644 --- a/src/test/java/io/codenotary/immudb4j/ListUsersTest.java +++ b/src/test/java/io/codenotary/immudb4j/ListUsersTest.java @@ -21,7 +21,7 @@ public class ListUsersTest extends ImmuClientIntegrationTest { @Test(testName = "listUsers") public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); immuClient.listUsers().forEach(user -> System.out.printf(">>> Got user %s", user)); diff --git a/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java b/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java index 4b46e30..fdb66b9 100644 --- a/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java +++ b/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java @@ -23,7 +23,7 @@ public class LoginAndHealthCheckAndCleanIndexTest extends ImmuClientIntegrationT @Test(testName = "openSession (with default credentials), healthCheck, logout") public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); boolean isHealthy = immuClient.healthCheck(); Assert.assertTrue(isHealthy); diff --git a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java index 777e19e..aae39d9 100644 --- a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java @@ -26,7 +26,7 @@ public class MultidatabaseTest extends ImmuClientIntegrationTest { @Test(testName = "Interacting with multiple databases (creating them, setting, and getting, listing)") public void t1() throws VerificationException { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); immuClient.createDatabase("db1"); immuClient.createDatabase("db2"); diff --git a/src/test/java/io/codenotary/immudb4j/MultithreadTest.java b/src/test/java/io/codenotary/immudb4j/MultithreadTest.java index 95fbe19..73c381a 100644 --- a/src/test/java/io/codenotary/immudb4j/MultithreadTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultithreadTest.java @@ -29,7 +29,7 @@ public class MultithreadTest extends ImmuClientIntegrationTest { @Test(testName = "Multithread without key overlap") public void t1() throws InterruptedException, VerificationException { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); final int threadCount = 10; final int keyCount = 100; @@ -75,7 +75,7 @@ public void t1() throws InterruptedException, VerificationException { @Test(testName = "Multithread with key overlap") public void t2() throws InterruptedException, VerificationException { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); final int threadCount = 10; final int keyCount = 100; diff --git a/src/test/java/io/codenotary/immudb4j/ReferenceTest.java b/src/test/java/io/codenotary/immudb4j/ReferenceTest.java index 7bc4c9c..cd9b0b7 100644 --- a/src/test/java/io/codenotary/immudb4j/ReferenceTest.java +++ b/src/test/java/io/codenotary/immudb4j/ReferenceTest.java @@ -25,7 +25,7 @@ public class ReferenceTest extends ImmuClientIntegrationTest { @Test(testName = "set, setReference, setReferenceAt") public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); byte[] key = "testRef".getBytes(StandardCharsets.UTF_8); byte[] val = "abc".getBytes(StandardCharsets.UTF_8); diff --git a/src/test/java/io/codenotary/immudb4j/ScanTest.java b/src/test/java/io/codenotary/immudb4j/ScanTest.java index 3a66d8c..9d9d328 100644 --- a/src/test/java/io/codenotary/immudb4j/ScanTest.java +++ b/src/test/java/io/codenotary/immudb4j/ScanTest.java @@ -20,13 +20,14 @@ import org.testng.annotations.Test; import java.nio.charset.StandardCharsets; +import java.util.Iterator; import java.util.List; public class ScanTest extends ImmuClientIntegrationTest { @Test(testName = "scan", priority = 2) public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); byte[] value1 = {0, 1, 2, 3}; byte[] value2 = {4, 5, 6, 7}; @@ -56,7 +57,7 @@ public void t1() { @Test(testName = "set, zAdd, zScan", priority = 3) public void t2() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); byte[] value1 = {0, 1, 2, 3}; byte[] value2 = {4, 5, 6, 7}; diff --git a/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java b/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java index 71d91aa..ec06d36 100644 --- a/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java +++ b/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java @@ -26,7 +26,7 @@ public class SetAllAndGetAllTest extends ImmuClientIntegrationTest { @Test(testName = "setAll & getAll") public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String key1 = "sga-key1"; byte[] val1 = new byte[] { 1 }; @@ -48,6 +48,13 @@ public void t1() { Assert.fail("Failed at SetAll.", e); } + try { + TxHeader txMd = immuClient.streamSetAll(kvs); + Assert.assertNotNull(txMd); + } catch (InterruptedException e) { + Assert.fail("Failed at SetAll.", e); + } + List keys = Arrays.asList(key1, key2, key3); List got = immuClient.getAll(keys); diff --git a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java index 6f39d81..6797358 100644 --- a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java @@ -26,7 +26,7 @@ public class SetAndGetTest extends ImmuClientIntegrationTest { @Test(testName = "set, get") public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "key1"; byte[] val = new byte[]{1, 2, 3, 4, 5}; diff --git a/src/test/java/io/codenotary/immudb4j/ShutdownTest.java b/src/test/java/io/codenotary/immudb4j/ShutdownTest.java index 6df992c..14652c9 100644 --- a/src/test/java/io/codenotary/immudb4j/ShutdownTest.java +++ b/src/test/java/io/codenotary/immudb4j/ShutdownTest.java @@ -22,13 +22,13 @@ public class ShutdownTest extends ImmuClientIntegrationTest { @Test(testName = "Login attempt after shutdown", expectedExceptions = StatusRuntimeException.class) - public void t1() { + public void t1() throws InterruptedException { Assert.assertFalse(immuClient.isShutdown()); immuClient.shutdown(); - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); } } diff --git a/src/test/java/io/codenotary/immudb4j/StateTest.java b/src/test/java/io/codenotary/immudb4j/StateTest.java index 2e5ab96..0201149 100644 --- a/src/test/java/io/codenotary/immudb4j/StateTest.java +++ b/src/test/java/io/codenotary/immudb4j/StateTest.java @@ -30,7 +30,7 @@ public class StateTest extends ImmuClientIntegrationTest { @Test(testName = "currentState") public void t2() throws VerificationException { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); ImmuState currState = immuClient.currentState(); @@ -82,7 +82,7 @@ public void t3() { return; } - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); try { immuClient.currentState(); @@ -124,7 +124,7 @@ public void t4() { return; } - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); try { ImmuState state = immuClient.currentState(); diff --git a/src/test/java/io/codenotary/immudb4j/TxTest.java b/src/test/java/io/codenotary/immudb4j/TxTest.java index 72b2493..0e76983 100644 --- a/src/test/java/io/codenotary/immudb4j/TxTest.java +++ b/src/test/java/io/codenotary/immudb4j/TxTest.java @@ -28,7 +28,7 @@ public class TxTest extends ImmuClientIntegrationTest { @Test(testName = "verifiedSet, txById, verifiedTxById") public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "test-txid"; byte[] val = "test-txid-value".getBytes(StandardCharsets.UTF_8); @@ -62,7 +62,7 @@ public void t1() { @Test(testName = "set, txScan") public void t2() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "txtest-t2"; byte[] val1 = "immuRocks!".getBytes(StandardCharsets.UTF_8); diff --git a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java index f338250..2de3f82 100644 --- a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java +++ b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java @@ -69,7 +69,7 @@ public void t1() { @Test(testName = "createUser, changePassword", priority = 101) public void t2() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); try { immuClient.createUser("testUser", "testTest123!", Permission.PERMISSION_ADMIN, "defaultdb"); diff --git a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java index a0a812d..d42ba06 100644 --- a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java @@ -26,7 +26,7 @@ public class VerifiedSetAndGetTest extends ImmuClientIntegrationTest { @Test(testName = "set, verifiedGet") public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "vsg"; byte[] val = "test-set-vget".getBytes(StandardCharsets.UTF_8); @@ -51,7 +51,7 @@ public void t1() { @Test(testName = "verifiedSet, verifiedGet, verifiedGetAt, verifiedGetSince") public void t2() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); byte[] key = "vsg".getBytes(StandardCharsets.UTF_8); byte[] val = "test-vset-vget".getBytes(StandardCharsets.UTF_8); diff --git a/src/test/java/io/codenotary/immudb4j/ZAddTest.java b/src/test/java/io/codenotary/immudb4j/ZAddTest.java index eb5be6d..c00cba1 100644 --- a/src/test/java/io/codenotary/immudb4j/ZAddTest.java +++ b/src/test/java/io/codenotary/immudb4j/ZAddTest.java @@ -26,7 +26,7 @@ public class ZAddTest extends ImmuClientIntegrationTest { @Test(testName = "zAdd, verifiedZAdd, verifiedZAddAt") public void t1() { - immuClient.openSession("immudb", "immudb", "defaultdb"); + immuClient.openSession("defaultdb", "immudb", "immudb"); String set = "test-zadd"; String key1 = "test-zadd-key1"; From 675e9eb0db652d5cdd7773a12ef588b57d78bafa Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Mon, 7 Nov 2022 15:23:34 -0300 Subject: [PATCH 02/25] test: session-related coverage Signed-off-by: Jeronimo Irazabal --- .../HealthCheckAndIndexCompactionTest.java | 72 +++++++++++++++++++ .../LoginAndHealthCheckAndCleanIndexTest.java | 41 ----------- .../io/codenotary/immudb4j/ShutdownTest.java | 3 +- 3 files changed, 74 insertions(+), 42 deletions(-) create mode 100644 src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java delete mode 100644 src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java diff --git a/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java b/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java new file mode 100644 index 0000000..10532ee --- /dev/null +++ b/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java @@ -0,0 +1,72 @@ +/* +Copyright 2022 CodeNotary, Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package io.codenotary.immudb4j; + +import io.grpc.StatusRuntimeException; + +import java.rmi.UnexpectedException; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class HealthCheckAndIndexCompactionTest extends ImmuClientIntegrationTest { + + @Test(testName = "openSession (with default credentials), healthCheck, logout") + public void t1() throws UnexpectedException { + try { + immuClient.openSession("defaultdb", "immudb", "immudb"); + } catch (Exception e) { + throw new UnexpectedException(e.getMessage()); + } + + Assert.assertTrue(immuClient.healthCheck()); + + immuClient.flushIndex(10.0f, true); + + immuClient.closeSession(); + } + + @Test(testName = "openSession (with wrong credentials)", expectedExceptions = StatusRuntimeException.class) + public void t2() { + immuClient.openSession("defaultdb", "immudb", "incorrect_password"); + } + + @Test(testName = "openSession (with wrong credentials)", expectedExceptions = StatusRuntimeException.class) + public void t3() { + immuClient.openSession("defaultdb"); + } + + @Test(testName = "openSession with session already open", expectedExceptions = IllegalStateException.class) + public void t4() throws UnexpectedException { + try { + immuClient.openSession("defaultdb", "immudb", "immudb"); + } catch (Exception e) { + throw new UnexpectedException(e.getMessage()); + } + + try { + immuClient.openSession("defaultdb", "immudb", "immudb"); + } finally { + immuClient.closeSession(); + } + } + + @Test(testName = "openSession with no open session", expectedExceptions = IllegalStateException.class) + public void t5() { + immuClient.closeSession(); + } + +} diff --git a/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java b/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java deleted file mode 100644 index fdb66b9..0000000 --- a/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2022 CodeNotary, Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package io.codenotary.immudb4j; - -import io.grpc.StatusRuntimeException; -import org.testng.Assert; -import org.testng.annotations.Test; - -public class LoginAndHealthCheckAndCleanIndexTest extends ImmuClientIntegrationTest { - - @Test(testName = "openSession (with default credentials), healthCheck, logout") - public void t1() { - immuClient.openSession("defaultdb", "immudb", "immudb"); - - boolean isHealthy = immuClient.healthCheck(); - Assert.assertTrue(isHealthy); - - immuClient.flushIndex(10.0f, true); - - immuClient.closeSession(); - } - - @Test(testName = "openSession (with wrong credentials)", expectedExceptions = StatusRuntimeException.class) - public void t2() { - immuClient.openSession("immudb", "incorrect_password", "defaultdb"); - } - -} diff --git a/src/test/java/io/codenotary/immudb4j/ShutdownTest.java b/src/test/java/io/codenotary/immudb4j/ShutdownTest.java index 14652c9..c3e5c65 100644 --- a/src/test/java/io/codenotary/immudb4j/ShutdownTest.java +++ b/src/test/java/io/codenotary/immudb4j/ShutdownTest.java @@ -23,9 +23,10 @@ public class ShutdownTest extends ImmuClientIntegrationTest { @Test(testName = "Login attempt after shutdown", expectedExceptions = StatusRuntimeException.class) public void t1() throws InterruptedException { - Assert.assertFalse(immuClient.isShutdown()); + immuClient.openSession("defaultdb", "immudb", "immudb"); + immuClient.shutdown(); immuClient.openSession("defaultdb", "immudb", "immudb"); From e8f52825e1f100a8a7551aba306c2170a422596f Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Mon, 14 Nov 2022 15:22:17 -0300 Subject: [PATCH 03/25] chore: scanAll, zscanAll, historyAll Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 50 +++++++++---------- .../immudb4j/SerializableImmuStateHolder.java | 6 ++- .../java/io/codenotary/immudb4j/TxHeader.java | 1 - .../io/codenotary/immudb4j/HistoryTest.java | 8 +-- .../java/io/codenotary/immudb4j/ScanTest.java | 11 ++-- .../io/codenotary/immudb4j/SetAndGetTest.java | 2 - .../java/io/codenotary/immudb4j/TxTest.java | 15 ++---- 7 files changed, 44 insertions(+), 49 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 6ffcbf4..652883e 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -560,11 +560,11 @@ public synchronized TxHeader delete(byte[] key) throws KeyNotFoundException { // ========== HISTORY ========== // - public List history(String key, int limit, long offset, boolean desc) throws KeyNotFoundException { - return history(Utils.toByteArray(key), limit, offset, desc); + public List historyAll(String key, int limit, long offset, boolean desc) throws KeyNotFoundException { + return historyAll(Utils.toByteArray(key), limit, offset, desc); } - public synchronized List history(byte[] key, int limit, long offset, boolean desc) + public synchronized List historyAll(byte[] key, int limit, long offset, boolean desc) throws KeyNotFoundException { try { ImmudbProto.Entries entries = blockingStub.history(ImmudbProto.HistoryRequest.newBuilder() @@ -588,39 +588,39 @@ public synchronized List history(byte[] key, int limit, long offset, bool // ========== SCAN ========== // - public List scan(String prefix) { - return scan(Utils.toByteArray(prefix)); + public List scanAll(String prefix) { + return scanAll(Utils.toByteArray(prefix)); } - public List scan(byte[] prefix) { - return scan(prefix, 0, false); + public List scanAll(byte[] prefix) { + return scanAll(prefix, 0, false); } - public List scan(String prefix, long limit, boolean desc) { - return scan(Utils.toByteArray(prefix), limit, desc); + public List scanAll(String prefix, long limit, boolean desc) { + return scanAll(Utils.toByteArray(prefix), limit, desc); } - public List scan(byte[] prefix, long limit, boolean desc) { - return scan(prefix, null, limit, desc); + public List scanAll(byte[] prefix, long limit, boolean desc) { + return scanAll(prefix, null, limit, desc); } - public List scan(String prefix, String seekKey, long limit, boolean desc) { - return scan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), limit, desc); + public List scanAll(String prefix, String seekKey, long limit, boolean desc) { + return scanAll(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), limit, desc); } - public List scan(String prefix, String seekKey, String endKey, long limit, boolean desc) { - return scan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), Utils.toByteArray(endKey), limit, desc); + public List scanAll(String prefix, String seekKey, String endKey, long limit, boolean desc) { + return scanAll(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), Utils.toByteArray(endKey), limit, desc); } - public List scan(byte[] prefix, byte[] seekKey, long limit, boolean desc) { - return scan(prefix, seekKey, null, limit, desc); + public List scanAll(byte[] prefix, byte[] seekKey, long limit, boolean desc) { + return scanAll(prefix, seekKey, null, limit, desc); } - public List scan(byte[] prefix, byte[] seekKey, byte[] endKey, long limit, boolean desc) { - return scan(prefix, seekKey, endKey, false, false, limit, desc); + public List scanAll(byte[] prefix, byte[] seekKey, byte[] endKey, long limit, boolean desc) { + return scanAll(prefix, seekKey, endKey, false, false, limit, desc); } - public synchronized List scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, + public synchronized List scanAll(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, boolean inclusiveEnd, long limit, boolean desc) { final ImmudbProto.ScanRequest req = ScanRequest.newBuilder() @@ -963,11 +963,11 @@ public synchronized TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, dou return TxHeader.valueOf(vtx.getTx().getHeader()); } - public List zScan(String set, long limit, boolean reverse) { - return zScan(Utils.toByteArray(set), limit, reverse); + public List zScanAll(String set, long limit, boolean reverse) { + return zScanAll(Utils.toByteArray(set), limit, reverse); } - public synchronized List zScan(byte[] set, long limit, boolean reverse) { + public synchronized List zScanAll(byte[] set, long limit, boolean reverse) { final ImmudbProto.ZScanRequest req = ImmudbProto.ZScanRequest .newBuilder() .setSet(Utils.toByteString(set)) @@ -1066,13 +1066,13 @@ public synchronized Tx verifiedTxById(long txId) throws TxNotFoundException, Ver return tx; } - public synchronized List txScan(long initialTxId) { + public synchronized List txScanAll(long initialTxId) { final ImmudbProto.TxScanRequest req = ImmudbProto.TxScanRequest.newBuilder().setInitialTx(initialTxId).build(); final ImmudbProto.TxList txList = blockingStub.txScan(req); return buildList(txList); } - public synchronized List txScan(long initialTxId, int limit, boolean desc) { + public synchronized List txScanAll(long initialTxId, int limit, boolean desc) { final ImmudbProto.TxScanRequest req = ImmudbProto.TxScanRequest .newBuilder() .setInitialTx(initialTxId) diff --git a/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java b/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java index 772318a..1489e6f 100644 --- a/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java +++ b/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java @@ -18,7 +18,11 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.HashMap; diff --git a/src/main/java/io/codenotary/immudb4j/TxHeader.java b/src/main/java/io/codenotary/immudb4j/TxHeader.java index f7cecf3..b982a34 100644 --- a/src/main/java/io/codenotary/immudb4j/TxHeader.java +++ b/src/main/java/io/codenotary/immudb4j/TxHeader.java @@ -20,7 +20,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.Base64; public class TxHeader { private final int version; diff --git a/src/test/java/io/codenotary/immudb4j/HistoryTest.java b/src/test/java/io/codenotary/immudb4j/HistoryTest.java index 4278b13..b4daebe 100644 --- a/src/test/java/io/codenotary/immudb4j/HistoryTest.java +++ b/src/test/java/io/codenotary/immudb4j/HistoryTest.java @@ -44,7 +44,7 @@ public void t1() { Assert.fail("Failed at set.", e); } - List historyResponse1 = immuClient.history("history1", 10, 0, false); + List historyResponse1 = immuClient.historyAll("history1", 10, 0, false); Assert.assertEquals(historyResponse1.size(), 2); @@ -54,7 +54,7 @@ public void t1() { Assert.assertEquals(historyResponse1.get(1).getKey(), "history1".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(historyResponse1.get(1).getValue(), value2); - List historyResponse2 = immuClient.history("history2", 10, 0, false); + List historyResponse2 = immuClient.historyAll("history2", 10, 0, false); Assert.assertEquals(historyResponse2.size(), 3); @@ -67,7 +67,7 @@ public void t1() { Assert.assertEquals(historyResponse2.get(2).getKey(), "history2".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(historyResponse2.get(2).getValue(), value3); - historyResponse2 = immuClient.history("history2", 10, 2, false); + historyResponse2 = immuClient.historyAll("history2", 10, 2, false); Assert.assertNotNull(historyResponse2); Assert.assertEquals(historyResponse2.size(), 1); @@ -78,7 +78,7 @@ public void t1() { Assert.assertNotNull(entry); try { - immuClient.history("nonExisting", 10, 0, false); + immuClient.historyAll("nonExisting", 10, 0, false); Assert.fail("key not found exception expected"); } catch (KeyNotFoundException e) { // exception is expected here diff --git a/src/test/java/io/codenotary/immudb4j/ScanTest.java b/src/test/java/io/codenotary/immudb4j/ScanTest.java index 9d9d328..cb974c4 100644 --- a/src/test/java/io/codenotary/immudb4j/ScanTest.java +++ b/src/test/java/io/codenotary/immudb4j/ScanTest.java @@ -20,7 +20,6 @@ import org.testng.annotations.Test; import java.nio.charset.StandardCharsets; -import java.util.Iterator; import java.util.List; public class ScanTest extends ImmuClientIntegrationTest { @@ -39,7 +38,7 @@ public void t1() { Assert.fail("Failed at set.", e); } - List scanResult = immuClient.scan("scan", 5, false); + List scanResult = immuClient.scanAll("scan", 5, false); System.out.println(scanResult.size()); Assert.assertEquals(scanResult.size(), 2); @@ -48,9 +47,9 @@ public void t1() { Assert.assertEquals(scanResult.get(1).getKey(), "scan2".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(scanResult.get(1).getValue(), value2); - Assert.assertTrue(immuClient.scan("scan").size() > 0); + Assert.assertTrue(immuClient.scanAll("scan").size() > 0); - Assert.assertEquals(immuClient.scan("scan", "scan1", 1, false).size(), 1); + Assert.assertEquals(immuClient.scanAll("scan", "scan1", 1, false).size(), 1); immuClient.closeSession(); } @@ -79,13 +78,13 @@ public void t2() { Assert.fail("Failed to zAdd", e); } - List zScan1 = immuClient.zScan("set1", 5, false); + List zScan1 = immuClient.zScanAll("set1", 5, false); Assert.assertEquals(zScan1.size(), 2); Assert.assertEquals(zScan1.get(0).getKey(), "zadd1".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(zScan1.get(0).getEntry().getValue(), value1); - List zScan2 = immuClient.zScan("set2", 5, false); + List zScan2 = immuClient.zScanAll("set2", 5, false); Assert.assertEquals(zScan2.size(), 2); immuClient.closeSession(); diff --git a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java index 6797358..4f711cd 100644 --- a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java @@ -20,8 +20,6 @@ import org.testng.Assert; import org.testng.annotations.Test; -import java.nio.charset.StandardCharsets; - public class SetAndGetTest extends ImmuClientIntegrationTest { @Test(testName = "set, get") diff --git a/src/test/java/io/codenotary/immudb4j/TxTest.java b/src/test/java/io/codenotary/immudb4j/TxTest.java index 0e76983..92c20e0 100644 --- a/src/test/java/io/codenotary/immudb4j/TxTest.java +++ b/src/test/java/io/codenotary/immudb4j/TxTest.java @@ -27,7 +27,7 @@ public class TxTest extends ImmuClientIntegrationTest { @Test(testName = "verifiedSet, txById, verifiedTxById") - public void t1() { + public void t1() throws NoSuchAlgorithmException{ immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "test-txid"; @@ -40,12 +40,7 @@ public void t1() { Assert.fail("Failed at verifiedSet", e); } - Tx tx = null; - try { - tx = immuClient.txById(txHdr.getId()); - } catch (NoSuchAlgorithmException e) { - Assert.fail("Failed at txById", e); - } + Tx tx = immuClient.txById(txHdr.getId()); Assert.assertEquals(txHdr.getId(), tx.getHeader().getId()); @@ -79,15 +74,15 @@ public void t2() { Assert.fail("Failed at set.", e); } - List txs = immuClient.txScan(initialTxId, 1, false); + List txs = immuClient.txScanAll(initialTxId, 1, false); Assert.assertNotNull(txs); Assert.assertEquals(txs.size(), 1); - txs = immuClient.txScan(initialTxId, 2, false); + txs = immuClient.txScanAll(initialTxId, 2, false); Assert.assertNotNull(txs); Assert.assertEquals(txs.size(), 2); - Assert.assertNotNull(immuClient.txScan(initialTxId)); + Assert.assertNotNull(immuClient.txScanAll(initialTxId)); immuClient.closeSession(); } From 938667c3bd51d97783fa175cd0bb79b544e3e6d0 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Tue, 15 Nov 2022 16:26:57 -0300 Subject: [PATCH 04/25] chore: simplified stream apis Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 114 ++++++++++-------- .../HealthCheckAndIndexCompactionTest.java | 2 +- .../io/codenotary/immudb4j/HistoryTest.java | 2 +- .../immudb4j/MultidatabaseTest.java | 3 + 4 files changed, 72 insertions(+), 49 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 652883e..61a8c26 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -68,7 +68,7 @@ public class ImmuClient { private Session session; private Timer sessionHeartBeat; - + public ImmuClient(Builder builder) { stateHolder = builder.getStateHolder(); serverSigningKey = builder.getServerSigningKey(); @@ -76,10 +76,10 @@ public ImmuClient(Builder builder) { chunkSize = builder.getChunkSize(); channel = ManagedChannelBuilder - .forAddress(builder.getServerUrl(), builder.getServerPort()) - .usePlaintext() - .intercept(new ImmudbAuthRequestInterceptor(this)) - .build(); + .forAddress(builder.getServerUrl(), builder.getServerPort()) + .usePlaintext() + .intercept(new ImmudbAuthRequestInterceptor(this)) + .build(); blockingStub = ImmuServiceGrpc.newBlockingStub(channel); nonBlockingStub = ImmuServiceGrpc.newStub(channel); @@ -88,7 +88,7 @@ public ImmuClient(Builder builder) { public static Builder newBuilder() { return new Builder(); } - + public synchronized void shutdown() throws InterruptedException { if (channel == null) { return; @@ -223,7 +223,8 @@ public synchronized void createDatabase(String database) { // if it has AutoLoad setting set to false or if it failed to load during // immudb startup. // - // This call requires SysAdmin permission level or admin permission to the database. + // This call requires SysAdmin permission level or admin permission to the + // database. public synchronized void loadDatabase(String database) { if (session == null) { throw new IllegalStateException("no open session"); @@ -236,10 +237,13 @@ public synchronized void loadDatabase(String database) { blockingStub.loadDatabase(req); } - // UnloadDatabase unloads database on the server. Such database becomes inaccessible - // by the client and server frees internal resources allocated for that database. + // UnloadDatabase unloads database on the server. Such database becomes + // inaccessible + // by the client and server frees internal resources allocated for that + // database. // - // This call requires SysAdmin permission level or admin permission to the database. + // This call requires SysAdmin permission level or admin permission to the + // database. public synchronized void unloadDatabase(String database) { if (session == null) { throw new IllegalStateException("no open session"); @@ -255,7 +259,8 @@ public synchronized void unloadDatabase(String database) { // DeleteDatabase removes an unloaded database. // This also removes locally stored files used by the database. // - // This call requires SysAdmin permission level or admin permission to the database. + // This call requires SysAdmin permission level or admin permission to the + // database. public synchronized void deleteDatabase(String database) { if (session == null) { throw new IllegalStateException("no open session"); @@ -1092,12 +1097,12 @@ private StreamObserver txHeaderStreamObserver(LatchHolder< @Override public void onCompleted() { } - + @Override public void onError(Throwable cause) { throw new RuntimeException(cause); } - + @Override public void onNext(ImmudbProto.TxHeader hdr) { latchHolder.doneWith(hdr); @@ -1106,14 +1111,25 @@ public void onNext(ImmudbProto.TxHeader hdr) { } private void chunkIt(byte[] bs, StreamObserver streamObserver) { - final ByteBuffer buf = ByteBuffer.allocate(Long.BYTES+bs.length).order(ByteOrder.BIG_ENDIAN); + final ByteBuffer buf = ByteBuffer.allocate(chunkSize).order(ByteOrder.BIG_ENDIAN); buf.putLong(bs.length); - buf.put(bs); - final Chunk chunk = Chunk.newBuilder().setContent(Utils.toByteString(buf.array())).build(); + int i = 0; + + while (i < bs.length) { + final int remaining = buf.remaining(); + + buf.put(bs, i, remaining); + + final Chunk chunk = Chunk.newBuilder().setContent(Utils.toByteString(buf.array())).build(); - streamObserver.onNext(chunk); + streamObserver.onNext(chunk); + + buf.clear(); + + i += remaining; + } } private byte[] dechunkIt(Iterator chunks) { @@ -1126,11 +1142,11 @@ private byte[] dechunkIt(Iterator chunks) { final ByteBuffer b = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.BIG_ENDIAN); b.put(firstChunkContent, 0, Long.BYTES); - + int payloadSize = (int) b.getLong(0); final ByteBuffer buf = ByteBuffer.allocate(payloadSize); - buf.put(firstChunkContent, Long.BYTES, firstChunkContent.length-Long.BYTES); + buf.put(firstChunkContent, Long.BYTES, firstChunkContent.length - Long.BYTES); while (buf.position() < payloadSize) { Chunk chunk = chunks.next(); @@ -1200,7 +1216,7 @@ public synchronized TxHeader streamSet(byte[] key, byte[] value) throws Interrup chunkIt(key, streamObserver); chunkIt(value, streamObserver); - + streamObserver.onCompleted(); return TxHeader.valueOf(latchHolder.awaitValue()); @@ -1248,40 +1264,40 @@ public synchronized Entry streamGet(byte[] key) throws KeyNotFoundException { // // ========== STREAM SCAN ========== // - - public Iterator streamScan(String prefix) { - return streamScan(Utils.toByteArray(prefix)); + + public Iterator scan(String prefix) { + return scan(Utils.toByteArray(prefix)); } - public Iterator streamScan(byte[] prefix) { - return streamScan(prefix, 0, false); + public Iterator scan(byte[] prefix) { + return scan(prefix, 0, false); } - public Iterator streamScan(String prefix, long limit, boolean desc) { - return streamScan(Utils.toByteArray(prefix), limit, desc); + public Iterator scan(String prefix, long limit, boolean desc) { + return scan(Utils.toByteArray(prefix), limit, desc); } - public Iterator streamScan(byte[] prefix, long limit, boolean desc) { - return streamScan(prefix, null, limit, desc); + public Iterator scan(byte[] prefix, long limit, boolean desc) { + return scan(prefix, null, limit, desc); } - public Iterator streamScan(String prefix, String seekKey, long limit, boolean desc) { - return streamScan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), limit, desc); + public Iterator scan(String prefix, String seekKey, long limit, boolean desc) { + return scan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), limit, desc); } - public Iterator streamScan(String prefix, String seekKey, String endKey, long limit, boolean desc) { - return streamScan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), Utils.toByteArray(endKey), limit, desc); + public Iterator scan(String prefix, String seekKey, String endKey, long limit, boolean desc) { + return scan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), Utils.toByteArray(endKey), limit, desc); } - public Iterator streamScan(byte[] prefix, byte[] seekKey, long limit, boolean desc) { - return streamScan(prefix, seekKey, null, limit, desc); + public Iterator scan(byte[] prefix, byte[] seekKey, long limit, boolean desc) { + return scan(prefix, seekKey, null, limit, desc); } - public Iterator streamScan(byte[] prefix, byte[] seekKey, byte[] endKey, long limit, boolean desc) { - return streamScan(prefix, seekKey, endKey, false, false, limit, desc); + public Iterator scan(byte[] prefix, byte[] seekKey, byte[] endKey, long limit, boolean desc) { + return scan(prefix, seekKey, endKey, false, false, limit, desc); } - public synchronized Iterator streamScan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, + public synchronized Iterator scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, boolean inclusiveEnd, long limit, boolean desc) { final ImmudbProto.ScanRequest req = ScanRequest.newBuilder() @@ -1303,11 +1319,11 @@ public synchronized Iterator streamScan(byte[] prefix, byte[] seekKey, by // ========== STREAM ZSCAN ========== // - public Iterator streamZScan(String set, long limit, boolean reverse) { - return streamZScan(Utils.toByteArray(set), limit, reverse); + public Iterator zScan(String set, long limit, boolean reverse) { + return zScan(Utils.toByteArray(set), limit, reverse); } - public synchronized Iterator streamZScan(byte[] set, long limit, boolean reverse) { + public synchronized Iterator zScan(byte[] set, long limit, boolean reverse) { final ImmudbProto.ZScanRequest req = ImmudbProto.ZScanRequest .newBuilder() .setSet(Utils.toByteString(set)) @@ -1319,16 +1335,16 @@ public synchronized Iterator streamZScan(byte[] set, long limit, boolean return zentryIterator(chunks); } - + // // ========== STREAM HISTORY ========== // - public Iterator streamHistory(String key, int limit, long offset, boolean desc) throws KeyNotFoundException { - return streamHistory(Utils.toByteArray(key), limit, offset, desc); + public Iterator history(String key, int limit, long offset, boolean desc) throws KeyNotFoundException { + return history(Utils.toByteArray(key), limit, offset, desc); } - public synchronized Iterator streamHistory(byte[] key, int limit, long offset, boolean desc) + public synchronized Iterator history(byte[] key, int limit, long offset, boolean desc) throws KeyNotFoundException { try { ImmudbProto.HistoryRequest req = ImmudbProto.HistoryRequest.newBuilder() @@ -1453,10 +1469,10 @@ public synchronized void revokePermission(String user, String database, int perm // ========== INDEX MGMT ========== // - public synchronized void flushIndex(float cleanupPercentage, boolean synced) { + public synchronized void flushIndex(float cleanupPercentage) { ImmudbProto.FlushIndexRequest req = ImmudbProto.FlushIndexRequest.newBuilder() .setCleanupPercentage(cleanupPercentage) - .setSynced(synced) + .setSynced(true) .build(); blockingStub.flushIndex(req); @@ -1576,6 +1592,10 @@ public Builder withStateHolder(ImmuStateHolder stateHolder) { } public Builder withChunkSize(int chunkSize) { + if (chunkSize < Long.BYTES) { + throw new RuntimeException("invalid chunk size"); + } + this.chunkSize = chunkSize; return this; } diff --git a/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java b/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java index 10532ee..cf6abff 100644 --- a/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java +++ b/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java @@ -34,7 +34,7 @@ public void t1() throws UnexpectedException { Assert.assertTrue(immuClient.healthCheck()); - immuClient.flushIndex(10.0f, true); + immuClient.flushIndex(10.0f); immuClient.closeSession(); } diff --git a/src/test/java/io/codenotary/immudb4j/HistoryTest.java b/src/test/java/io/codenotary/immudb4j/HistoryTest.java index b4daebe..989ee80 100644 --- a/src/test/java/io/codenotary/immudb4j/HistoryTest.java +++ b/src/test/java/io/codenotary/immudb4j/HistoryTest.java @@ -71,7 +71,7 @@ public void t1() { Assert.assertNotNull(historyResponse2); Assert.assertEquals(historyResponse2.size(), 1); - Iterator entriesIt = immuClient.streamHistory("history2", 10, 2, false); + Iterator entriesIt = immuClient.history("history2", 10, 2, false); Assert.assertTrue(entriesIt.hasNext()); Entry entry = entriesIt.next(); diff --git a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java index aae39d9..970b464 100644 --- a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java @@ -26,6 +26,9 @@ public class MultidatabaseTest extends ImmuClientIntegrationTest { @Test(testName = "Interacting with multiple databases (creating them, setting, and getting, listing)") public void t1() throws VerificationException { + + immuClient.createDatabase("db1"); + immuClient.openSession("defaultdb", "immudb", "immudb"); immuClient.createDatabase("db1"); From df0159899c44c44710a6dfd167a35bb919ba5afa Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Wed, 16 Nov 2022 11:15:39 -0300 Subject: [PATCH 05/25] fix: chunking implementation Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 19 ++++++++++++++----- .../immudb4j/MultidatabaseTest.java | 10 ++++------ .../immudb4j/SetAllAndGetAllTest.java | 11 ++--------- .../io/codenotary/immudb4j/UserMgmtTest.java | 4 ++-- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 61a8c26..6dbd868 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -135,7 +135,11 @@ public synchronized void openSession(String database, String username, String pa @Override public void run() { try { - blockingStub.keepAlive(Empty.getDefaultInstance()); + synchronized(ImmuClient.this) { + if (session != null) { + blockingStub.keepAlive(Empty.getDefaultInstance()); + } + } } catch (Exception e) { e.printStackTrace(); } @@ -1118,17 +1122,22 @@ private void chunkIt(byte[] bs, StreamObserver streamObserver) { int i = 0; while (i < bs.length) { - final int remaining = buf.remaining(); + final int chunkContentLen = Math.min(bs.length, buf.remaining()); - buf.put(bs, i, remaining); + buf.put(bs, i, chunkContentLen); - final Chunk chunk = Chunk.newBuilder().setContent(Utils.toByteString(buf.array())).build(); + buf.flip(); + + byte[] chunkContent = new byte[buf.limit()]; + buf.get(chunkContent); + + final Chunk chunk = Chunk.newBuilder().setContent(Utils.toByteString(chunkContent)).build(); streamObserver.onNext(chunk); buf.clear(); - i += remaining; + i += chunkContentLen; } } diff --git a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java index 970b464..2006746 100644 --- a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java @@ -27,8 +27,6 @@ public class MultidatabaseTest extends ImmuClientIntegrationTest { @Test(testName = "Interacting with multiple databases (creating them, setting, and getting, listing)") public void t1() throws VerificationException { - immuClient.createDatabase("db1"); - immuClient.openSession("defaultdb", "immudb", "immudb"); immuClient.createDatabase("db1"); @@ -36,7 +34,7 @@ public void t1() throws VerificationException { immuClient.closeSession(); - immuClient.openSession("immudb", "immudb", "db1"); + immuClient.openSession("db1", "immudb", "immudb"); byte[] v0 = new byte[]{0, 1, 2, 3}; try { @@ -47,7 +45,7 @@ public void t1() throws VerificationException { immuClient.closeSession(); - immuClient.openSession("immudb", "immudb", "db2"); + immuClient.openSession("db2", "immudb", "immudb"); byte[] v1 = new byte[]{3, 2, 1, 0}; try { @@ -58,7 +56,7 @@ public void t1() throws VerificationException { immuClient.closeSession(); - immuClient.openSession("immudb", "immudb", "db1"); + immuClient.openSession("db1", "immudb", "immudb"); Entry entry1 = immuClient.get("k0"); Assert.assertNotNull(entry1); @@ -70,7 +68,7 @@ public void t1() throws VerificationException { immuClient.closeSession(); - immuClient.openSession("immudb", "immudb", "db2"); + immuClient.openSession("db2", "immudb", "immudb"); Entry entry2 = immuClient.get("k1"); Assert.assertEquals(entry2.getValue(), v1); diff --git a/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java b/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java index ec06d36..4c879b9 100644 --- a/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java +++ b/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java @@ -42,19 +42,12 @@ public void t1() { .entries(); try { - TxHeader txMd = immuClient.setAll(kvs); - Assert.assertNotNull(txMd); + TxHeader txHdr = immuClient.setAll(kvs); + Assert.assertNotNull(txHdr); } catch (CorruptedDataException e) { Assert.fail("Failed at SetAll.", e); } - try { - TxHeader txMd = immuClient.streamSetAll(kvs); - Assert.assertNotNull(txMd); - } catch (InterruptedException e) { - Assert.fail("Failed at SetAll.", e); - } - List keys = Arrays.asList(key1, key2, key3); List got = immuClient.getAll(keys); diff --git a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java index 2de3f82..0fc16e8 100644 --- a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java +++ b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java @@ -33,7 +33,7 @@ public void t1() { String password = "testTest123!"; Permission permission = Permission.PERMISSION_RW; - immuClient.openSession("immudb", "immudb", database); + immuClient.openSession(database, "immudb", "immudb"); // Should not contain testCreateUser. Skipping it as not valid for the current unit tests setup // (where a clean immudb server is started for each Test class). @@ -90,7 +90,7 @@ public void t2() { // Login failed, everything's fine. } - immuClient.openSession("testUser", "newTestTest123!", "defaultdb"); + immuClient.openSession("defaultdb", "testUser", "newTestTest123!"); immuClient.closeSession(); From 152615681ca083e02a9f243913f54d07a9f6ce44 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Wed, 16 Nov 2022 11:24:42 -0300 Subject: [PATCH 06/25] test: unit testing for LatchHolder Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/BasicsTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/java/io/codenotary/immudb4j/BasicsTest.java b/src/test/java/io/codenotary/immudb4j/BasicsTest.java index e762002..d5ed5b9 100644 --- a/src/test/java/io/codenotary/immudb4j/BasicsTest.java +++ b/src/test/java/io/codenotary/immudb4j/BasicsTest.java @@ -15,6 +15,8 @@ */ package io.codenotary.immudb4j; +import io.codenotary.immudb4j.basics.LatchHolder; + // Note: This test is more for the sake of code coverage, as you may see. import io.codenotary.immudb4j.basics.Pair; @@ -58,4 +60,22 @@ public void t1() { Assert.assertNotEquals(triple, Triple.of("aDifferent", "", "")); } + @Test(testName = "LatchHolder test") + public void t2() throws InterruptedException { + final LatchHolder latchHolder = new LatchHolder<>(); + + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + + latchHolder.doneWith(true); + } + }).run(); + + Assert.assertTrue(latchHolder.awaitValue()); + } } From 33c7febbd52578e9f5b6c63c7421fb82aaec5dd3 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Wed, 16 Nov 2022 11:39:32 -0300 Subject: [PATCH 07/25] test: basic zadd unit testing Signed-off-by: Jeronimo Irazabal --- src/test/java/io/codenotary/immudb4j/ScanTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/io/codenotary/immudb4j/ScanTest.java b/src/test/java/io/codenotary/immudb4j/ScanTest.java index cb974c4..692a3c0 100644 --- a/src/test/java/io/codenotary/immudb4j/ScanTest.java +++ b/src/test/java/io/codenotary/immudb4j/ScanTest.java @@ -81,7 +81,10 @@ public void t2() { List zScan1 = immuClient.zScanAll("set1", 5, false); Assert.assertEquals(zScan1.size(), 2); + Assert.assertEquals(zScan1.get(0).getSet(), "set1".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(zScan1.get(0).getKey(), "zadd1".getBytes(StandardCharsets.UTF_8)); + Assert.assertEquals(zScan1.get(0).getScore(), 1.0); + Assert.assertEquals(zScan1.get(0).getAtTx(), 0); Assert.assertEquals(zScan1.get(0).getEntry().getValue(), value1); List zScan2 = immuClient.zScanAll("set2", 5, false); From ef0bf9e2322d7b7cde2e2f52b59af4bae2328d66 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Wed, 16 Nov 2022 11:52:39 -0300 Subject: [PATCH 08/25] chore: use temp folders during unit testing Signed-off-by: Jeronimo Irazabal --- immudb/clean.sh | 3 +-- .../io/codenotary/immudb4j/ImmuClientIntegrationTest.java | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/immudb/clean.sh b/immudb/clean.sh index 93583f0..2a16f6a 100755 --- a/immudb/clean.sh +++ b/immudb/clean.sh @@ -11,7 +11,6 @@ then fi rm -rf data -rm -rf states -echo "immudb's data and immudb4j's states folders were removed." +echo "immudb's data folder was removed." diff --git a/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java b/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java index 07bdc0d..f77afa0 100644 --- a/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java +++ b/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java @@ -18,7 +18,9 @@ import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; public abstract class ImmuClientIntegrationTest { @@ -26,8 +28,11 @@ public abstract class ImmuClientIntegrationTest { @BeforeClass public static void beforeClass() throws IOException { + final File statesDir = Files.createTempDirectory("immudb_states").toFile(); + statesDir.deleteOnExit(); + FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder() - .withStatesFolder("immudb/states") + .withStatesFolder(statesDir.getAbsolutePath()) .build(); immuClient = ImmuClient.newBuilder() From aad86cd72d6c566a60e6c95e4a7fd285cd808b7f Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Wed, 16 Nov 2022 13:26:32 -0300 Subject: [PATCH 09/25] test: unit testing database mgmt Signed-off-by: Jeronimo Irazabal test: update test script Signed-off-by: Jeronimo Irazabal test: create, unload, delete db Signed-off-by: Jeronimo Irazabal test: create, unload, delete db Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 11 ++-- .../immudb4j/BasicImmuClientTest.java | 1 - .../HealthCheckAndIndexCompactionTest.java | 9 +--- .../immudb4j/ListDatabasesTest.java | 7 ++- .../immudb4j/MultidatabaseTest.java | 52 ++++++++++++++++--- .../io/codenotary/immudb4j/StateTest.java | 39 ++++++++------ tests.sh | 17 ++++-- 7 files changed, 96 insertions(+), 40 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 6dbd868..01d4dd2 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -109,10 +109,6 @@ protected synchronized Session getSession() { return session; } - public synchronized void openSession(String database) { - openSession(database, "", ""); - } - public synchronized void openSession(String database, String username, String password) { if (session != null) { throw new IllegalStateException("session already opened"); @@ -211,13 +207,18 @@ public synchronized ImmuState currentState() throws VerificationException { // ========== DATABASE ========== // - public synchronized void createDatabase(String database) { + public void createDatabase(String database) { + createDatabase(database, false); + } + + public synchronized void createDatabase(String database, boolean ifNotExists) { if (session == null) { throw new IllegalStateException("no open session"); } final ImmudbProto.CreateDatabaseRequest req = ImmudbProto.CreateDatabaseRequest.newBuilder() .setName(database) + .setIfNotExists(ifNotExists) .build(); blockingStub.createDatabaseV2(req); diff --git a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java index 030664d..9642dd1 100644 --- a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java +++ b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java @@ -108,5 +108,4 @@ public void t2() { immuClient.closeSession(); } - } diff --git a/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java b/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java index cf6abff..9b57b5c 100644 --- a/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java +++ b/src/test/java/io/codenotary/immudb4j/HealthCheckAndIndexCompactionTest.java @@ -44,13 +44,8 @@ public void t2() { immuClient.openSession("defaultdb", "immudb", "incorrect_password"); } - @Test(testName = "openSession (with wrong credentials)", expectedExceptions = StatusRuntimeException.class) - public void t3() { - immuClient.openSession("defaultdb"); - } - @Test(testName = "openSession with session already open", expectedExceptions = IllegalStateException.class) - public void t4() throws UnexpectedException { + public void t3() throws UnexpectedException { try { immuClient.openSession("defaultdb", "immudb", "immudb"); } catch (Exception e) { @@ -65,7 +60,7 @@ public void t4() throws UnexpectedException { } @Test(testName = "openSession with no open session", expectedExceptions = IllegalStateException.class) - public void t5() { + public void t4() { immuClient.closeSession(); } diff --git a/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java b/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java index d45e578..8a6bf72 100644 --- a/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java +++ b/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java @@ -21,8 +21,13 @@ public class ListDatabasesTest extends ImmuClientIntegrationTest { + @Test(testName = "databases without open session", expectedExceptions = IllegalStateException.class) + public void t1() { + immuClient.databases(); + } + @Test(testName = "databases") - public void t1() { + public void t2() { immuClient.openSession("defaultdb", "immudb", "immudb"); List databases = immuClient.databases(); diff --git a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java index 2006746..d4f4391 100644 --- a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java @@ -17,6 +17,8 @@ import io.codenotary.immudb4j.exceptions.CorruptedDataException; import io.codenotary.immudb4j.exceptions.VerificationException; +import io.grpc.StatusRuntimeException; + import org.testng.Assert; import org.testng.annotations.Test; @@ -24,19 +26,38 @@ public class MultidatabaseTest extends ImmuClientIntegrationTest { - @Test(testName = "Interacting with multiple databases (creating them, setting, and getting, listing)") - public void t1() throws VerificationException { + @Test(testName = "createDatabase without open session", expectedExceptions = IllegalStateException.class) + public void t1() { + immuClient.createDatabase("db1"); + } + + @Test(testName = "loadDatabase without open session", expectedExceptions = IllegalStateException.class) + public void t2() { + immuClient.loadDatabase("db1"); + } + @Test(testName = "unloadDatabase without open session", expectedExceptions = IllegalStateException.class) + public void t3() { + immuClient.unloadDatabase("db1"); + } + + @Test(testName = "deleteDatabase without open session", expectedExceptions = IllegalStateException.class) + public void t4() { + immuClient.deleteDatabase("db1"); + } + + @Test(testName = "Interacting with multiple databases (creating them, setting, and getting, listing)") + public void t5() throws VerificationException { immuClient.openSession("defaultdb", "immudb", "immudb"); - immuClient.createDatabase("db1"); - immuClient.createDatabase("db2"); + immuClient.createDatabase("db1", true); + immuClient.createDatabase("db2", true); immuClient.closeSession(); immuClient.openSession("db1", "immudb", "immudb"); - byte[] v0 = new byte[]{0, 1, 2, 3}; + byte[] v0 = new byte[] { 0, 1, 2, 3 }; try { immuClient.set("k0", v0); } catch (CorruptedDataException e) { @@ -47,7 +68,7 @@ public void t1() throws VerificationException { immuClient.openSession("db2", "immudb", "immudb"); - byte[] v1 = new byte[]{3, 2, 1, 0}; + byte[] v1 = new byte[] { 3, 2, 1, 0 }; try { immuClient.set("k1", v1); } catch (CorruptedDataException e) { @@ -86,4 +107,23 @@ public void t1() throws VerificationException { immuClient.closeSession(); } + @Test(testName = "create, unload and delete database") + public void t6() { + immuClient.openSession("defaultdb", "immudb", "immudb"); + + immuClient.createDatabase("manageddb"); + + immuClient.unloadDatabase("manageddb"); + + immuClient.deleteDatabase("manageddb"); + + try { + immuClient.loadDatabase("manageddb"); + Assert.fail("exception expected"); + } catch (StatusRuntimeException e) { + Assert.assertTrue(e.getMessage().contains("database does not exist")); + } + + immuClient.closeSession(); + } } diff --git a/src/test/java/io/codenotary/immudb4j/StateTest.java b/src/test/java/io/codenotary/immudb4j/StateTest.java index 0201149..5040c1c 100644 --- a/src/test/java/io/codenotary/immudb4j/StateTest.java +++ b/src/test/java/io/codenotary/immudb4j/StateTest.java @@ -28,6 +28,11 @@ public class StateTest extends ImmuClientIntegrationTest { private static final String publicKeyResource = "test_public_key.pem"; + @Test(testName = "currentState without open session", expectedExceptions = IllegalStateException.class) + public void t1() throws VerificationException { + immuClient.currentState(); + } + @Test(testName = "currentState") public void t2() throws VerificationException { immuClient.openSession("defaultdb", "immudb", "immudb"); @@ -52,11 +57,13 @@ public void t2() throws VerificationException { return; } - // The signature verification in this case should fail for the same aforementioned reason. + // The signature verification in this case should fail for the same + // aforementioned reason. Assert.assertFalse(currState.checkSignature(publicKey)); // Again, "covering" `checkSignature` when there is a `signature` attached. - ImmuState someState = new ImmuState(currState.getDatabase(), currState.getTxId(), currState.getTxHash(), new byte[1]); + ImmuState someState = new ImmuState(currState.getDatabase(), currState.getTxId(), currState.getTxHash(), + new byte[1]); Assert.assertFalse(someState.checkSignature(publicKey)); immuClient.closeSession(); @@ -86,23 +93,23 @@ public void t3() { try { immuClient.currentState(); - Assert.fail("Did not fail as it should in this case when the signingKey is provisioned only on the client side"); + Assert.fail( + "Did not fail as it should in this case when the signingKey is provisioned only on the client side"); } catch (VerificationException ignored) { - // Expected this since in the current tests setup, immudb does not have that state signature feature active. - // (this feature is active when starting it like: `immudb --signingKey test_private_key.pem`). + // Expected this since in the current tests setup, immudb does not have that + // state signature feature active. + // (this feature is active when starting it like: `immudb --signingKey + // test_private_key.pem`). } immuClient.closeSession(); } - - - @Test(testName = "currentState with server signature checking", - description = "Testing `checkSignature` (indirectly, through `currentState`), " + - "the (state signing) feature being set up on both server and client side. " + - "This could remain a manual test, that's why it is disabled." + - "Of course, it must be `enabled = true`, if you want to run it from IDE or cli.", - enabled = false) + @Test(testName = "currentState with server signature checking", description = "Testing `checkSignature` (indirectly, through `currentState`), " + + + "the (state signing) feature being set up on both server and client side. " + + "This could remain a manual test, that's why it is disabled." + + "Of course, it must be `enabled = true`, if you want to run it from IDE or cli.", enabled = false) public void t4() { // Provisioning the client side with the public key file. @@ -128,8 +135,10 @@ public void t4() { try { ImmuState state = immuClient.currentState(); - // In this case, it should be ok as long as the immudb server has been started accordingly - // from `immudb` directory (on this repo root) using: `./immudb --signingKey test_private_key.pem` + // In this case, it should be ok as long as the immudb server has been started + // accordingly + // from `immudb` directory (on this repo root) using: `./immudb --signingKey + // test_private_key.pem` Assert.assertNotNull(state); } catch (VerificationException e) { Assert.fail(e.getMessage(), e.getCause()); diff --git a/tests.sh b/tests.sh index 3095cc7..05f6b60 100755 --- a/tests.sh +++ b/tests.sh @@ -9,20 +9,27 @@ echo ## Unit Tests TESTS="${TESTS} BasicImmuClientTest" +TESTS="${TESTS} BasicsTest" +TESTS="${TESTS} CryptoUtilsTest" +TESTS="${TESTS} ExceptionsTest" +TESTS="${TESTS} FileImmuStateHolderTest" +TESTS="${TESTS} HealthCheckAndIndexCompactionTest" TESTS="${TESTS} HistoryTest" TESTS="${TESTS} HTreeTest" -TESTS="${TESTS} ListDatabasesTest ListUsersTest" -TESTS="${TESTS} LoginAndHealthCheckAndCleanIndexTest" -TESTS="${TESTS} MultidatabaseTest MultithreadTest" +TESTS="${TESTS} ListDatabasesTest" +TESTS="${TESTS} ListUsersTest" +TESTS="${TESTS} MultidatabaseTest" +TESTS="${TESTS} MultithreadTest" TESTS="${TESTS} ReferenceTest" TESTS="${TESTS} ScanTest" -TESTS="${TESTS} SetAllAndGetAllTest SetAndGetTest" +TESTS="${TESTS} SetAllAndGetAllTest" +TESTS="${TESTS} SetAndGetTest" +TESTS="${TESTS} ShutdownTest" TESTS="${TESTS} StateTest" TESTS="${TESTS} TxTest" TESTS="${TESTS} UserMgmtTest" TESTS="${TESTS} VerifiedSetAndGetTest" TESTS="${TESTS} ZAddTest" -TESTS="${TESTS} ShutdownTest" # ----------------------------------------------------------------------------- From 7d50531dcfd1683824d19ad18fc3fba2e6d36de1 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Mon, 21 Nov 2022 07:11:51 -0300 Subject: [PATCH 10/25] chore: simplified start Signed-off-by: Jeronimo Irazabal --- build.gradle | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 2cf56fd..b66929d 100644 --- a/build.gradle +++ b/build.gradle @@ -133,14 +133,7 @@ task immudbStart { pb = new ProcessBuilder() pb.command("/bin/bash", "immudb/start.sh") Process proc = pb.start() - - proc.waitFor(5, TimeUnit.SECONDS) - - if (proc.isAlive()) { - println "immudb has been started." - } else { - throw new GradleException("Process exit bad: " + proc.exitValue()) - } + proc.waitFor(15, TimeUnit.SECONDS) } task immudbStop(type: Exec) { From 2fb8dac7d6290a5b552b5feb5f9c4d175f9f2a3b Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Mon, 21 Nov 2022 07:37:43 -0300 Subject: [PATCH 11/25] test: reduced db mgmt Signed-off-by: Jeronimo Irazabal --- src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java index d4f4391..8b9f1ef 100644 --- a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java @@ -117,12 +117,14 @@ public void t6() { immuClient.deleteDatabase("manageddb"); + /* try { immuClient.loadDatabase("manageddb"); Assert.fail("exception expected"); } catch (StatusRuntimeException e) { Assert.assertTrue(e.getMessage().contains("database does not exist")); } + */ immuClient.closeSession(); } From 6e29fe2deb70a9c807e13140a69384b0742f2a6e Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Mon, 21 Nov 2022 16:31:54 -0300 Subject: [PATCH 12/25] test: zscan unit testing Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 24 +++++++++++++++---- .../java/io/codenotary/immudb4j/ZEntry.java | 2 +- .../immudb4j/ImmuClientIntegrationTest.java | 3 ++- .../java/io/codenotary/immudb4j/ScanTest.java | 15 ++++++++++-- .../immudb4j/VerifiedSetAndGetTest.java | 24 +++++++++++++++++++ 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 01d4dd2..cc9cc5d 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -973,16 +973,24 @@ public synchronized TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, dou return TxHeader.valueOf(vtx.getTx().getHeader()); } - public List zScanAll(String set, long limit, boolean reverse) { - return zScanAll(Utils.toByteArray(set), limit, reverse); + public List zScanAll(String set) { + return zScanAll(set, false, 0); } - public synchronized List zScanAll(byte[] set, long limit, boolean reverse) { + public List zScanAll(String set, long limit) { + return zScanAll(set, false, limit); + } + + public List zScanAll(String set, boolean reverse, long limit) { + return zScanAll(Utils.toByteArray(set), reverse, limit); + } + + public synchronized List zScanAll(byte[] set, boolean reverse, long limit) { final ImmudbProto.ZScanRequest req = ImmudbProto.ZScanRequest .newBuilder() .setSet(Utils.toByteString(set)) - .setLimit(limit) .setDesc(reverse) + .setLimit(limit) .build(); final ImmudbProto.ZEntries zEntries = blockingStub.zScan(req); @@ -1329,6 +1337,14 @@ public synchronized Iterator scan(byte[] prefix, byte[] seekKey, byte[] e // ========== STREAM ZSCAN ========== // + public Iterator zScan(String set) { + return zScan(Utils.toByteArray(set), 0, false); + } + + public Iterator zScan(String set, long limit) { + return zScan(Utils.toByteArray(set), limit, false); + } + public Iterator zScan(String set, long limit, boolean reverse) { return zScan(Utils.toByteArray(set), limit, reverse); } diff --git a/src/main/java/io/codenotary/immudb4j/ZEntry.java b/src/main/java/io/codenotary/immudb4j/ZEntry.java index 6d58767..6012d74 100644 --- a/src/main/java/io/codenotary/immudb4j/ZEntry.java +++ b/src/main/java/io/codenotary/immudb4j/ZEntry.java @@ -106,5 +106,5 @@ public byte[] digestFor(int version) { return kv.digestFor(version); } - + } diff --git a/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java b/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java index f77afa0..b7b0873 100644 --- a/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java +++ b/src/test/java/io/codenotary/immudb4j/ImmuClientIntegrationTest.java @@ -25,10 +25,11 @@ public abstract class ImmuClientIntegrationTest { protected static ImmuClient immuClient; + protected static File statesDir; @BeforeClass public static void beforeClass() throws IOException { - final File statesDir = Files.createTempDirectory("immudb_states").toFile(); + statesDir = Files.createTempDirectory("immudb_states").toFile(); statesDir.deleteOnExit(); FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder() diff --git a/src/test/java/io/codenotary/immudb4j/ScanTest.java b/src/test/java/io/codenotary/immudb4j/ScanTest.java index 692a3c0..5060258 100644 --- a/src/test/java/io/codenotary/immudb4j/ScanTest.java +++ b/src/test/java/io/codenotary/immudb4j/ScanTest.java @@ -20,6 +20,7 @@ import org.testng.annotations.Test; import java.nio.charset.StandardCharsets; +import java.util.Iterator; import java.util.List; public class ScanTest extends ImmuClientIntegrationTest { @@ -78,7 +79,7 @@ public void t2() { Assert.fail("Failed to zAdd", e); } - List zScan1 = immuClient.zScanAll("set1", 5, false); + List zScan1 = immuClient.zScanAll("set1", false, 5); Assert.assertEquals(zScan1.size(), 2); Assert.assertEquals(zScan1.get(0).getSet(), "set1".getBytes(StandardCharsets.UTF_8)); @@ -87,9 +88,19 @@ public void t2() { Assert.assertEquals(zScan1.get(0).getAtTx(), 0); Assert.assertEquals(zScan1.get(0).getEntry().getValue(), value1); - List zScan2 = immuClient.zScanAll("set2", 5, false); + List zScan2 = immuClient.zScanAll("set2"); Assert.assertEquals(zScan2.size(), 2); + Iterator zScan3 = immuClient.zScan("set2"); + int i = 0; + + while (zScan3.hasNext()) { + Assert.assertEquals(zScan3.next().getKey(), zScan2.get(i).getKey()); + i++; + } + + Assert.assertEquals(i, 2); + immuClient.closeSession(); } diff --git a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java index d42ba06..a74b9dc 100644 --- a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java @@ -20,6 +20,7 @@ import org.testng.Assert; import org.testng.annotations.Test; +import java.io.IOException; import java.nio.charset.StandardCharsets; public class VerifiedSetAndGetTest extends ImmuClientIntegrationTest { @@ -95,6 +96,29 @@ public void t2() { immuClient.closeSession(); } + @Test(testName = "Login attempt after shutdown") + public void t3() throws InterruptedException, IllegalStateException, IOException, VerificationException { + immuClient.openSession("defaultdb", "immudb", "immudb"); + + immuClient.verifiedSet("key1", "val1".getBytes()); + + immuClient.closeSession(); + + immuClient.shutdown(); + + FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder() + .withStatesFolder(statesDir.getAbsolutePath()) + .build(); + immuClient = ImmuClient.newBuilder() + .withStateHolder(stateHolder) + .withServerUrl("localhost") + .withServerPort(3322) + .build(); + + immuClient.openSession("defaultdb", "immudb", "immudb"); + + immuClient.verifiedGet("key1"); + } } From 0f2fa05b22057ca78040aaff518d066aa78d7f65 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Mon, 21 Nov 2022 17:44:21 -0300 Subject: [PATCH 13/25] test: increase set, get coverage Signed-off-by: Jeronimo Irazabal --- .../java/io/codenotary/immudb4j/Entry.java | 8 ++++ .../immudb4j/BasicImmuClientTest.java | 46 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/main/java/io/codenotary/immudb4j/Entry.java b/src/main/java/io/codenotary/immudb4j/Entry.java index 15a3b6d..a89c5b7 100644 --- a/src/main/java/io/codenotary/immudb4j/Entry.java +++ b/src/main/java/io/codenotary/immudb4j/Entry.java @@ -28,6 +28,8 @@ public class Entry { private Reference referencedBy; + private long revision; + private Entry() {} public Entry(byte[] key, byte[] value) { @@ -50,6 +52,8 @@ public static Entry valueOf(ImmudbProto.Entry e) { entry.referencedBy = Reference.valueOf(e.getReferencedBy()); } + entry.revision = e.getRevision(); + return entry; } @@ -73,6 +77,10 @@ public Reference getReferenceBy() { return referencedBy; } + public long getRevision() { + return revision; + } + public byte[] getEncodedKey() { if (referencedBy == null) { return Utils.wrapWithPrefix(key, Consts.SET_KEY_PREFIX); diff --git a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java index 9642dd1..2b45252 100644 --- a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java +++ b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java @@ -17,7 +17,10 @@ import com.google.common.base.Charsets; import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import io.codenotary.immudb4j.exceptions.KeyNotFoundException; import io.codenotary.immudb4j.exceptions.VerificationException; +import io.grpc.StatusRuntimeException; + import org.testng.Assert; import org.testng.annotations.Test; @@ -63,6 +66,49 @@ public void t1() throws VerificationException, CorruptedDataException, Interrupt Assert.assertNotNull(e); Assert.assertEquals(e.getValue(), v2); + Assert.assertEquals(v2, immuClient.getSinceTx("k2", ventry2.getRevision()).getValue()); + + immuClient.set("k0", v1); + + Assert.assertEquals(v0, immuClient.getAtRevision("k0", entry0.getRevision()).getValue()); + Assert.assertEquals(v1, immuClient.getAtRevision("k0", entry0.getRevision()+1).getValue()); + + Assert.assertEquals(v0, immuClient.verifiedGetAtRevision("k0", entry0.getRevision()).getValue()); + Assert.assertEquals(v1, immuClient.verifiedGetAtRevision("k0", entry0.getRevision()+1).getValue()); + + try { + immuClient.verifiedGet("non-existent-key"); + Assert.fail("Failed at verifiedGet."); + } catch (KeyNotFoundException _) { + } + + try { + immuClient.getSinceTx("non-existent-key", 1); + Assert.fail("Failed at getSinceTx."); + } catch (KeyNotFoundException _) { + } + + try { + immuClient.verifiedGetSinceTx("non-existent-key", 1); + Assert.fail("Failed at verifiedGetSinceTx."); + } catch (KeyNotFoundException _) { + } + + try { + immuClient.getAtRevision("k0", entry0.getRevision()+2); + Assert.fail("Failed at getSinceTx."); + } catch (StatusRuntimeException e1) { + Assert.assertTrue(e1.getMessage().contains("invalid key revision number")); + } + + try { + immuClient.verifiedGetAtRevision("k0", entry0.getRevision()+2); + Assert.fail("Failed at verifiedGetAtRevision."); + } catch (StatusRuntimeException e1) { + Assert.assertTrue(e1.getMessage().contains("invalid key revision number")); + } + + immuClient.closeSession(); } From 83311e76ce3773d451d9e99a10e1dc074f5bb9b7 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Tue, 22 Nov 2022 12:43:40 -0300 Subject: [PATCH 14/25] test: history unit testing Signed-off-by: Jeronimo Irazabal --- .../java/io/codenotary/immudb4j/ImmuClient.java | 10 +++++----- .../java/io/codenotary/immudb4j/HistoryTest.java | 2 +- src/test/java/io/codenotary/immudb4j/TxTest.java | 15 ++++++++++++++- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index cc9cc5d..28fb025 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -1338,11 +1338,11 @@ public synchronized Iterator scan(byte[] prefix, byte[] seekKey, byte[] e // public Iterator zScan(String set) { - return zScan(Utils.toByteArray(set), 0, false); + return zScan(set, 0); } public Iterator zScan(String set, long limit) { - return zScan(Utils.toByteArray(set), limit, false); + return zScan(set, limit, false); } public Iterator zScan(String set, long limit, boolean reverse) { @@ -1366,11 +1366,11 @@ public synchronized Iterator zScan(byte[] set, long limit, boolean rever // ========== STREAM HISTORY ========== // - public Iterator history(String key, int limit, long offset, boolean desc) throws KeyNotFoundException { - return history(Utils.toByteArray(key), limit, offset, desc); + public Iterator history(String key, long offset, boolean desc, int limit) throws KeyNotFoundException { + return history(Utils.toByteArray(key), offset, desc, limit); } - public synchronized Iterator history(byte[] key, int limit, long offset, boolean desc) + public synchronized Iterator history(byte[] key, long offset, boolean desc, int limit) throws KeyNotFoundException { try { ImmudbProto.HistoryRequest req = ImmudbProto.HistoryRequest.newBuilder() diff --git a/src/test/java/io/codenotary/immudb4j/HistoryTest.java b/src/test/java/io/codenotary/immudb4j/HistoryTest.java index 989ee80..c41eaeb 100644 --- a/src/test/java/io/codenotary/immudb4j/HistoryTest.java +++ b/src/test/java/io/codenotary/immudb4j/HistoryTest.java @@ -71,7 +71,7 @@ public void t1() { Assert.assertNotNull(historyResponse2); Assert.assertEquals(historyResponse2.size(), 1); - Iterator entriesIt = immuClient.history("history2", 10, 2, false); + Iterator entriesIt = immuClient.history("history2", 2, false, 10); Assert.assertTrue(entriesIt.hasNext()); Entry entry = entriesIt.next(); diff --git a/src/test/java/io/codenotary/immudb4j/TxTest.java b/src/test/java/io/codenotary/immudb4j/TxTest.java index 92c20e0..11a26a5 100644 --- a/src/test/java/io/codenotary/immudb4j/TxTest.java +++ b/src/test/java/io/codenotary/immudb4j/TxTest.java @@ -16,6 +16,7 @@ package io.codenotary.immudb4j; import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import io.codenotary.immudb4j.exceptions.TxNotFoundException; import io.codenotary.immudb4j.exceptions.VerificationException; import org.testng.Assert; import org.testng.annotations.Test; @@ -27,7 +28,7 @@ public class TxTest extends ImmuClientIntegrationTest { @Test(testName = "verifiedSet, txById, verifiedTxById") - public void t1() throws NoSuchAlgorithmException{ + public void t1() throws NoSuchAlgorithmException, VerificationException{ immuClient.openSession("defaultdb", "immudb", "immudb"); String key = "test-txid"; @@ -52,6 +53,18 @@ public void t1() throws NoSuchAlgorithmException{ Assert.assertEquals(txHdr.getId(), tx.getHeader().getId()); + try { + immuClient.txById(txHdr.getId()+1); + Assert.fail("Failed at txById."); + } catch (TxNotFoundException _) { + } + + try { + immuClient.verifiedTxById(txHdr.getId()+1); + Assert.fail("Failed at verifiedTxById."); + } catch (TxNotFoundException _) { + } + immuClient.closeSession(); } From 4408243b556465d156cf8b5c90a19f82ecffa896 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Tue, 22 Nov 2022 12:57:01 -0300 Subject: [PATCH 15/25] test: stream set and get Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 15 +++-- .../immudb4j/StreamSetAndGetTest.java | 58 +++++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 src/test/java/io/codenotary/immudb4j/StreamSetAndGetTest.java diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 28fb025..0a2bdaf 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -131,7 +131,7 @@ public synchronized void openSession(String database, String username, String pa @Override public void run() { try { - synchronized(ImmuClient.this) { + synchronized (ImmuClient.this) { if (session != null) { blockingStub.keepAlive(Empty.getDefaultInstance()); } @@ -1224,11 +1224,12 @@ public ZEntry next() { // ========== STREAM SET ========== // - public TxHeader streamSet(String key, byte[] value) throws InterruptedException { + public TxHeader streamSet(String key, byte[] value) throws InterruptedException, CorruptedDataException { return streamSet(Utils.toByteArray(key), value); } - public synchronized TxHeader streamSet(byte[] key, byte[] value) throws InterruptedException { + public synchronized TxHeader streamSet(byte[] key, byte[] value) + throws InterruptedException, CorruptedDataException { final LatchHolder latchHolder = new LatchHolder<>(); final StreamObserver streamObserver = nonBlockingStub.streamSet(txHeaderStreamObserver(latchHolder)); @@ -1237,7 +1238,13 @@ public synchronized TxHeader streamSet(byte[] key, byte[] value) throws Interrup streamObserver.onCompleted(); - return TxHeader.valueOf(latchHolder.awaitValue()); + final ImmudbProto.TxHeader txHdr = latchHolder.awaitValue(); + + if (txHdr.getNentries() != 1) { + throw new CorruptedDataException(); + } + + return TxHeader.valueOf(txHdr); } public synchronized TxHeader streamSetAll(List kvList) throws InterruptedException { diff --git a/src/test/java/io/codenotary/immudb4j/StreamSetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/StreamSetAndGetTest.java new file mode 100644 index 0000000..c5ce019 --- /dev/null +++ b/src/test/java/io/codenotary/immudb4j/StreamSetAndGetTest.java @@ -0,0 +1,58 @@ +/* +Copyright 2022 CodeNotary, Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package io.codenotary.immudb4j; + +import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import io.codenotary.immudb4j.exceptions.KeyNotFoundException; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class StreamSetAndGetTest extends ImmuClientIntegrationTest { + + @Test(testName = "stream set, get") + public void t1() { + immuClient.openSession("defaultdb", "immudb", "immudb"); + + String key = "key1"; + byte[] val = new byte[]{1, 2, 3, 4, 5}; + + TxHeader txHdr = null; + try { + txHdr = immuClient.streamSet(key, val); + } catch (CorruptedDataException|InterruptedException e) { + Assert.fail("Failed at set.", e); + } + Assert.assertNotNull(txHdr); + + Entry entry1 = immuClient.streamGet(key); + Assert.assertNotNull(entry1); + Assert.assertEquals(entry1.getValue(), val); + + immuClient.delete(key); + + try { + immuClient.streamGet(key); + Assert.fail("key not found exception expected"); + } catch (KeyNotFoundException e) { + // expected + } catch (Exception e) { + Assert.fail("key not found exception expected"); + } + + immuClient.closeSession(); + } + +} From 362cef4391ed53b36711eb272b769464141e2711 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Tue, 22 Nov 2022 13:43:17 -0300 Subject: [PATCH 16/25] test: stream scan unit testing Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 58 +++++++------------ .../java/io/codenotary/immudb4j/ScanTest.java | 15 ++++- tests.sh | 1 + 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 0a2bdaf..3e1066c 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -603,36 +603,28 @@ public List scanAll(String prefix) { } public List scanAll(byte[] prefix) { - return scanAll(prefix, 0, false); + return scanAll(prefix, false, 0); } - public List scanAll(String prefix, long limit, boolean desc) { - return scanAll(Utils.toByteArray(prefix), limit, desc); + public List scanAll(String prefix, boolean desc, long limit) { + return scanAll(Utils.toByteArray(prefix), null, desc, limit); } - public List scanAll(byte[] prefix, long limit, boolean desc) { - return scanAll(prefix, null, limit, desc); + public List scanAll(byte[] prefix, boolean desc, long limit) { + return scanAll(prefix, null, desc, limit); } - public List scanAll(String prefix, String seekKey, long limit, boolean desc) { - return scanAll(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), limit, desc); + public List scanAll(byte[] prefix, byte[] seekKey, boolean desc, long limit) { + return scanAll(prefix, seekKey, null, desc, limit); } - public List scanAll(String prefix, String seekKey, String endKey, long limit, boolean desc) { - return scanAll(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), Utils.toByteArray(endKey), limit, desc); - } - - public List scanAll(byte[] prefix, byte[] seekKey, long limit, boolean desc) { - return scanAll(prefix, seekKey, null, limit, desc); - } - - public List scanAll(byte[] prefix, byte[] seekKey, byte[] endKey, long limit, boolean desc) { - return scanAll(prefix, seekKey, endKey, false, false, limit, desc); + public List scanAll(byte[] prefix, byte[] seekKey, byte[] endKey, boolean desc, long limit) { + return scanAll(prefix, seekKey, endKey, false, false, desc, limit); } public synchronized List scanAll(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, boolean inclusiveEnd, - long limit, boolean desc) { + boolean desc, long limit) { final ImmudbProto.ScanRequest req = ScanRequest.newBuilder() .setPrefix(Utils.toByteString(prefix)) .setSeekKey(Utils.toByteString(seekKey)) @@ -1295,44 +1287,36 @@ public Iterator scan(String prefix) { } public Iterator scan(byte[] prefix) { - return scan(prefix, 0, false); - } - - public Iterator scan(String prefix, long limit, boolean desc) { - return scan(Utils.toByteArray(prefix), limit, desc); - } - - public Iterator scan(byte[] prefix, long limit, boolean desc) { - return scan(prefix, null, limit, desc); + return scan(prefix, false, 0); } - public Iterator scan(String prefix, String seekKey, long limit, boolean desc) { - return scan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), limit, desc); + public Iterator scan(String prefix, boolean desc, long limit) { + return scan(Utils.toByteArray(prefix), desc, limit); } - public Iterator scan(String prefix, String seekKey, String endKey, long limit, boolean desc) { - return scan(Utils.toByteArray(prefix), Utils.toByteArray(seekKey), Utils.toByteArray(endKey), limit, desc); + public Iterator scan(byte[] prefix, boolean desc, long limit) { + return scan(prefix, null, desc, limit); } - public Iterator scan(byte[] prefix, byte[] seekKey, long limit, boolean desc) { - return scan(prefix, seekKey, null, limit, desc); + public Iterator scan(byte[] prefix, byte[] seekKey, boolean desc, long limit) { + return scan(prefix, seekKey, null, desc, limit); } - public Iterator scan(byte[] prefix, byte[] seekKey, byte[] endKey, long limit, boolean desc) { - return scan(prefix, seekKey, endKey, false, false, limit, desc); + public Iterator scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean desc, long limit) { + return scan(prefix, seekKey, endKey, false, false, desc, limit); } public synchronized Iterator scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, boolean inclusiveEnd, - long limit, boolean desc) { + boolean desc, long limit) { final ImmudbProto.ScanRequest req = ScanRequest.newBuilder() .setPrefix(Utils.toByteString(prefix)) .setSeekKey(Utils.toByteString(seekKey)) .setEndKey(Utils.toByteString(endKey)) .setInclusiveSeek(inclusiveSeek) .setInclusiveEnd(inclusiveEnd) - .setLimit(limit) .setDesc(desc) + .setLimit(limit) .build(); final Iterator chunks = blockingStub.streamScan(req); diff --git a/src/test/java/io/codenotary/immudb4j/ScanTest.java b/src/test/java/io/codenotary/immudb4j/ScanTest.java index 5060258..6f51080 100644 --- a/src/test/java/io/codenotary/immudb4j/ScanTest.java +++ b/src/test/java/io/codenotary/immudb4j/ScanTest.java @@ -39,7 +39,7 @@ public void t1() { Assert.fail("Failed at set.", e); } - List scanResult = immuClient.scanAll("scan", 5, false); + List scanResult = immuClient.scanAll("scan", false, 5); System.out.println(scanResult.size()); Assert.assertEquals(scanResult.size(), 2); @@ -50,7 +50,18 @@ public void t1() { Assert.assertTrue(immuClient.scanAll("scan").size() > 0); - Assert.assertEquals(immuClient.scanAll("scan", "scan1", 1, false).size(), 1); + Assert.assertEquals(immuClient.scanAll("scan".getBytes(), "scan1".getBytes(), false, 1).size(), 1); + + Iterator scanResult1 = immuClient.scan("scan", false, 5); + + int i = 0; + + while (scanResult1.hasNext()) { + Assert.assertEquals(scanResult1.next().getKey(), scanResult.get(i).getKey()); + i++; + } + + Assert.assertEquals(i, 2); immuClient.closeSession(); } diff --git a/tests.sh b/tests.sh index 05f6b60..28a57f2 100755 --- a/tests.sh +++ b/tests.sh @@ -24,6 +24,7 @@ TESTS="${TESTS} ReferenceTest" TESTS="${TESTS} ScanTest" TESTS="${TESTS} SetAllAndGetAllTest" TESTS="${TESTS} SetAndGetTest" +TESTS="${TESTS} StreamSetAndGetTest" TESTS="${TESTS} ShutdownTest" TESTS="${TESTS} StateTest" TESTS="${TESTS} TxTest" From 7198016ac31fc304d2303bbf9e52a7243e97ddb6 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Wed, 23 Nov 2022 11:24:38 -0300 Subject: [PATCH 17/25] test: shorter multithread unit testing Signed-off-by: Jeronimo Irazabal --- build.gradle | 4 ++++ src/test/java/io/codenotary/immudb4j/MultithreadTest.java | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index b66929d..b7d84c8 100644 --- a/build.gradle +++ b/build.gradle @@ -160,6 +160,10 @@ test { } // Run the unit tests sequentially. maxParallelForks = 1 + + testLogging { + events "FAILED" + } } test.dependsOn immudbStart diff --git a/src/test/java/io/codenotary/immudb4j/MultithreadTest.java b/src/test/java/io/codenotary/immudb4j/MultithreadTest.java index 73c381a..412a1dc 100644 --- a/src/test/java/io/codenotary/immudb4j/MultithreadTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultithreadTest.java @@ -31,8 +31,8 @@ public class MultithreadTest extends ImmuClientIntegrationTest { public void t1() throws InterruptedException, VerificationException { immuClient.openSession("defaultdb", "immudb", "immudb"); - final int threadCount = 10; - final int keyCount = 100; + final int threadCount = 5; + final int keyCount = 10; CountDownLatch latch = new CountDownLatch(threadCount); AtomicInteger succeeded = new AtomicInteger(0); @@ -77,8 +77,8 @@ public void t1() throws InterruptedException, VerificationException { public void t2() throws InterruptedException, VerificationException { immuClient.openSession("defaultdb", "immudb", "immudb"); - final int threadCount = 10; - final int keyCount = 100; + final int threadCount = 5; + final int keyCount = 10; CountDownLatch latch = new CountDownLatch(threadCount); AtomicInteger succeeded = new AtomicInteger(0); From b002915a99932d96089b67e454cdff7f6d5e06b0 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Wed, 23 Nov 2022 11:35:55 -0300 Subject: [PATCH 18/25] test: simplified user mgmt test Signed-off-by: Jeronimo Irazabal --- src/test/java/io/codenotary/immudb4j/UserMgmtTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java index 0fc16e8..103338d 100644 --- a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java +++ b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java @@ -22,7 +22,6 @@ import org.testng.annotations.Test; import java.util.Collections; -import java.util.List; public class UserMgmtTest extends ImmuClientIntegrationTest { @@ -47,9 +46,9 @@ public void t1() { } // Should contain testCreateUser. - System.out.println(">>> listUsers:"); - List users = immuClient.listUsers(); - users.forEach(user -> System.out.println("\t- " + user)); + //System.out.println(">>> listUsers:"); + //List users = immuClient.listUsers(); + //users.forEach(user -> System.out.println("\t- " + user)); // TODO: Temporary commented since currently there's a bug on immudb's side. // The next release will include the fix of 'listUsers'. This commit includes the fix: From 5f64bd2422008e0228fd393d38fa2deaaf5fb602 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Wed, 23 Nov 2022 16:42:05 -0300 Subject: [PATCH 19/25] test: validate no more entries Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 29 +++++++++++++++---- .../io/codenotary/immudb4j/HistoryTest.java | 26 +++++++++++++---- .../java/io/codenotary/immudb4j/ScanTest.java | 2 +- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 3e1066c..5bdee34 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -43,6 +43,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; @@ -570,18 +571,18 @@ public synchronized TxHeader delete(byte[] key) throws KeyNotFoundException { // ========== HISTORY ========== // - public List historyAll(String key, int limit, long offset, boolean desc) throws KeyNotFoundException { - return historyAll(Utils.toByteArray(key), limit, offset, desc); + public List historyAll(String key, long offset, boolean desc, int limit) throws KeyNotFoundException { + return historyAll(Utils.toByteArray(key), offset, desc, limit); } - public synchronized List historyAll(byte[] key, int limit, long offset, boolean desc) + public synchronized List historyAll(byte[] key, long offset, boolean desc, int limit) throws KeyNotFoundException { try { ImmudbProto.Entries entries = blockingStub.history(ImmudbProto.HistoryRequest.newBuilder() .setKey(Utils.toByteString(key)) - .setLimit(limit) .setOffset(offset) .setDesc(desc) + .setLimit(limit) .build()); return buildList(entries); @@ -1171,7 +1172,15 @@ private Iterator entryIterator(Iterator chunks) { @Override public boolean hasNext() { - return chunks.hasNext(); + try { + return chunks.hasNext(); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + return false; + } + + throw e; + } } @Override @@ -1187,7 +1196,15 @@ private Iterator zentryIterator(Iterator chunks) { @Override public boolean hasNext() { - return chunks.hasNext(); + try { + return chunks.hasNext(); + } catch (StatusRuntimeException e) { + if (e.getMessage().contains("key not found")) { + return false; + } + + throw e; + } } @Override diff --git a/src/test/java/io/codenotary/immudb4j/HistoryTest.java b/src/test/java/io/codenotary/immudb4j/HistoryTest.java index c41eaeb..b210cf4 100644 --- a/src/test/java/io/codenotary/immudb4j/HistoryTest.java +++ b/src/test/java/io/codenotary/immudb4j/HistoryTest.java @@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; public class HistoryTest extends ImmuClientIntegrationTest { @@ -44,7 +45,7 @@ public void t1() { Assert.fail("Failed at set.", e); } - List historyResponse1 = immuClient.historyAll("history1", 10, 0, false); + List historyResponse1 = immuClient.historyAll("history1", 0, false, 2); Assert.assertEquals(historyResponse1.size(), 2); @@ -54,7 +55,7 @@ public void t1() { Assert.assertEquals(historyResponse1.get(1).getKey(), "history1".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(historyResponse1.get(1).getValue(), value2); - List historyResponse2 = immuClient.historyAll("history2", 10, 0, false); + List historyResponse2 = immuClient.historyAll("history2", 0, false, 3); Assert.assertEquals(historyResponse2.size(), 3); @@ -67,23 +68,36 @@ public void t1() { Assert.assertEquals(historyResponse2.get(2).getKey(), "history2".getBytes(StandardCharsets.UTF_8)); Assert.assertEquals(historyResponse2.get(2).getValue(), value3); - historyResponse2 = immuClient.historyAll("history2", 10, 2, false); + historyResponse2 = immuClient.historyAll("history2", 2, false, 1); Assert.assertNotNull(historyResponse2); Assert.assertEquals(historyResponse2.size(), 1); - Iterator entriesIt = immuClient.history("history2", 2, false, 10); + Iterator entriesIt = immuClient.history("history2", 2, false, 1); Assert.assertTrue(entriesIt.hasNext()); Entry entry = entriesIt.next(); Assert.assertNotNull(entry); + Assert.assertFalse(entriesIt.hasNext()); + + try { + entriesIt.next(); + Assert.fail("NoSuchElementException exception expected"); + } catch (NoSuchElementException e) { + // exception is expected here + } + try { - immuClient.historyAll("nonExisting", 10, 0, false); + immuClient.historyAll("nonExisting", 0, false, 0); Assert.fail("key not found exception expected"); } catch (KeyNotFoundException e) { // exception is expected here } - + + Iterator entriesIt2 = immuClient.history("nonExisting", 0, false, 0); + + Assert.assertFalse(entriesIt2.hasNext()); + immuClient.closeSession(); } diff --git a/src/test/java/io/codenotary/immudb4j/ScanTest.java b/src/test/java/io/codenotary/immudb4j/ScanTest.java index 6f51080..73fd2eb 100644 --- a/src/test/java/io/codenotary/immudb4j/ScanTest.java +++ b/src/test/java/io/codenotary/immudb4j/ScanTest.java @@ -39,7 +39,7 @@ public void t1() { Assert.fail("Failed at set.", e); } - List scanResult = immuClient.scanAll("scan", false, 5); + List scanResult = immuClient.scanAll("scan"); System.out.println(scanResult.size()); Assert.assertEquals(scanResult.size(), 2); From afc9380e943c124ee3ae4a7a305573b2b2a5e009 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Wed, 23 Nov 2022 16:47:42 -0300 Subject: [PATCH 20/25] test: basic user mgmt test Signed-off-by: Jeronimo Irazabal --- src/main/java/io/codenotary/immudb4j/ImmuClient.java | 8 ++++---- src/test/java/io/codenotary/immudb4j/UserMgmtTest.java | 8 +++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 5bdee34..fefc7d9 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -1475,24 +1475,24 @@ public synchronized void changePassword(String user, String oldPassword, String blockingStub.changePassword(changePasswordRequest); } - public synchronized void grantPermission(String user, String database, int permissions) { + public synchronized void grantPermission(String user, String database, Permission permissions) { final ImmudbProto.ChangePermissionRequest req = ImmudbProto.ChangePermissionRequest.newBuilder() .setUsername(user) .setAction(ImmudbProto.PermissionAction.GRANT) .setDatabase(database) - .setPermission(permissions) + .setPermission(permissions.permissionCode) .build(); // noinspection ResultOfMethodCallIgnored blockingStub.changePermission(req); } - public synchronized void revokePermission(String user, String database, int permissions) { + public synchronized void revokePermission(String user, String database, Permission permissions) { final ImmudbProto.ChangePermissionRequest req = ImmudbProto.ChangePermissionRequest.newBuilder() .setUsername(user) .setAction(ImmudbProto.PermissionAction.REVOKE) .setDatabase(database) - .setPermission(permissions) + .setPermission(permissions.permissionCode) .build(); // noinspection ResultOfMethodCallIgnored diff --git a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java index 103338d..cd6e4a1 100644 --- a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java +++ b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java @@ -71,12 +71,18 @@ public void t2() { immuClient.openSession("defaultdb", "immudb", "immudb"); try { - immuClient.createUser("testUser", "testTest123!", Permission.PERMISSION_ADMIN, "defaultdb"); + immuClient.createUser("testUser", "testTest123!", Permission.PERMISSION_R, "defaultdb"); } catch (StatusRuntimeException e) { // The user could already exist, ignoring this. System.out.println(">>> UserMgmtTest > t2 > createUser exception: " + e.getMessage()); } + immuClient.activateUser("testUser", true); + + immuClient.revokePermission("testUser", "defaultdb", Permission.PERMISSION_R); + + immuClient.grantPermission("testUser", "defaultdb", Permission.PERMISSION_RW); + immuClient.changePassword("testUser", "testTest123!", "newTestTest123!"); immuClient.closeSession(); From f057c1448edeec5eafb7e9edb161d15a3ee274bc Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Fri, 25 Nov 2022 01:00:32 -0300 Subject: [PATCH 21/25] test: streamSetAll Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 10 ++- .../codenotary/immudb4j/StreamSetAllTest.java | 63 +++++++++++++++++++ tests.sh | 1 + 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/test/java/io/codenotary/immudb4j/StreamSetAllTest.java diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index fefc7d9..63bf39a 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -1256,7 +1256,7 @@ public synchronized TxHeader streamSet(byte[] key, byte[] value) return TxHeader.valueOf(txHdr); } - public synchronized TxHeader streamSetAll(List kvList) throws InterruptedException { + public synchronized TxHeader streamSetAll(List kvList) throws InterruptedException, CorruptedDataException { final LatchHolder latchHolder = new LatchHolder<>(); final StreamObserver streamObserver = nonBlockingStub.streamSet(txHeaderStreamObserver(latchHolder)); @@ -1266,8 +1266,14 @@ public synchronized TxHeader streamSetAll(List kvList) throws Interrupte } streamObserver.onCompleted(); + + final ImmudbProto.TxHeader txHdr = latchHolder.awaitValue(); + + if (txHdr.getNentries() != kvList.size()) { + throw new CorruptedDataException(); + } - return TxHeader.valueOf(latchHolder.awaitValue()); + return TxHeader.valueOf(txHdr); } // diff --git a/src/test/java/io/codenotary/immudb4j/StreamSetAllTest.java b/src/test/java/io/codenotary/immudb4j/StreamSetAllTest.java new file mode 100644 index 0000000..a08fe4a --- /dev/null +++ b/src/test/java/io/codenotary/immudb4j/StreamSetAllTest.java @@ -0,0 +1,63 @@ +/* +Copyright 2022 CodeNotary, Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package io.codenotary.immudb4j; + +import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.List; + +public class StreamSetAllTest extends ImmuClientIntegrationTest { + + @Test(testName = "setAll & getAll") + public void t1() { + immuClient.openSession("defaultdb", "immudb", "immudb"); + + String key1 = "sga-key1"; + byte[] val1 = new byte[] { 1 }; + String key2 = "sga-key2"; + byte[] val2 = new byte[] { 2, 3 }; + String key3 = "sga-key3"; + byte[] val3 = new byte[] { 3, 4, 5 }; + + final List kvs = KVListBuilder.newBuilder() + .add(new KVPair(key1, val1)) + .add(new KVPair(key2, val2)) + .add(new KVPair(key3, val3)) + .entries(); + + try { + TxHeader txHdr = immuClient.streamSetAll(kvs); + Assert.assertNotNull(txHdr); + } catch (InterruptedException|CorruptedDataException e) { + Assert.fail("Failed at SetAll.", e); + } + + List keys = Arrays.asList(key1, key2, key3); + List got = immuClient.getAll(keys); + + Assert.assertEquals(kvs.size(), got.size()); + + for (int i = 0; i < kvs.size(); i++) { + Assert.assertEquals(got.get(i).getValue(), kvs.get(i).getValue(), String.format("Expected: %s got: %s", kvs.get(i), got.get(i))); + } + + immuClient.closeSession(); + } + +} diff --git a/tests.sh b/tests.sh index 28a57f2..0399a7e 100755 --- a/tests.sh +++ b/tests.sh @@ -25,6 +25,7 @@ TESTS="${TESTS} ScanTest" TESTS="${TESTS} SetAllAndGetAllTest" TESTS="${TESTS} SetAndGetTest" TESTS="${TESTS} StreamSetAndGetTest" +TESTS="${TESTS} StreamSetAllTest" TESTS="${TESTS} ShutdownTest" TESTS="${TESTS} StateTest" TESTS="${TESTS} TxTest" From 631ceca1d669103d515861261a134a2048a6047c Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Fri, 25 Nov 2022 11:50:06 -0300 Subject: [PATCH 22/25] feat: extended scan and zscan api Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 86 +++++++++++++------ 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 63bf39a..4af070f 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -21,6 +21,7 @@ import io.codenotary.immudb.ImmudbProto; import io.codenotary.immudb.ImmudbProto.Chunk; import io.codenotary.immudb.ImmudbProto.ScanRequest; +import io.codenotary.immudb.ImmudbProto.Score; import io.codenotary.immudb4j.basics.LatchHolder; import io.codenotary.immudb4j.crypto.CryptoUtils; import io.codenotary.immudb4j.crypto.DualProof; @@ -970,23 +971,41 @@ public List zScanAll(String set) { return zScanAll(set, false, 0); } - public List zScanAll(String set, long limit) { - return zScanAll(set, false, limit); + public List zScanAll(String set, boolean reverse, long limit) { + return pzScanAll(Utils.toByteArray(set), null, null, 0, null, 0, true, reverse, limit); } - public List zScanAll(String set, boolean reverse, long limit) { - return zScanAll(Utils.toByteArray(set), reverse, limit); + public List zScanAll(byte[] set, double minScore, double maxScore, boolean reverse, long limit) { + return zScanAll(set, minScore, maxScore, 0, null, 0, true, false, 0); } - public synchronized List zScanAll(byte[] set, boolean reverse, long limit) { - final ImmudbProto.ZScanRequest req = ImmudbProto.ZScanRequest - .newBuilder() - .setSet(Utils.toByteString(set)) + public List zScanAll(byte[] set, double minScore, double maxScore, double seekScore, byte[] seekKey, + long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { + return pzScanAll(set, minScore, maxScore, seekScore, seekKey, seekAtTx, inclusiveSeek, reverse, limit); + } + + private List pzScanAll(byte[] set, Double minScore, Double maxScore, double seekScore, byte[] seekKey, + long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { + + final ImmudbProto.ZScanRequest.Builder reqBuilder = ImmudbProto.ZScanRequest.newBuilder(); + + reqBuilder.setSet(Utils.toByteString(set)) + .setSeekScore(seekScore) + .setSeekKey(Utils.toByteString(seekKey)) + .setSeekAtTx(seekAtTx) + .setInclusiveSeek(inclusiveSeek) .setDesc(reverse) - .setLimit(limit) - .build(); + .setLimit(limit); + + if (minScore != null) { + reqBuilder.setMinScore(Score.newBuilder().setScore(minScore).build()); + } + + if (maxScore != null) { + reqBuilder.setMaxScore(Score.newBuilder().setScore(maxScore).build()); + } - final ImmudbProto.ZEntries zEntries = blockingStub.zScan(req); + final ImmudbProto.ZEntries zEntries = blockingStub.zScan(reqBuilder.build()); return buildList(zEntries); } @@ -1266,7 +1285,7 @@ public synchronized TxHeader streamSetAll(List kvList) throws Interrupte } streamObserver.onCompleted(); - + final ImmudbProto.TxHeader txHdr = latchHolder.awaitValue(); if (txHdr.getNentries() != kvList.size()) { @@ -1352,26 +1371,45 @@ public synchronized Iterator scan(byte[] prefix, byte[] seekKey, byte[] e // public Iterator zScan(String set) { - return zScan(set, 0); + return zScan(set, false, 0); } - public Iterator zScan(String set, long limit) { - return zScan(set, limit, false); + public Iterator zScan(String set, boolean reverse, long limit) { + return pzScan(Utils.toByteArray(set), null, null, 0, null, 0, true, reverse, limit); } - public Iterator zScan(String set, long limit, boolean reverse) { - return zScan(Utils.toByteArray(set), limit, reverse); + public Iterator zScan(byte[] set, double minScore, double maxScore, boolean reverse, long limit) { + return zScan(set, minScore, maxScore, 0, null, 0, true, false, 0); } - public synchronized Iterator zScan(byte[] set, long limit, boolean reverse) { - final ImmudbProto.ZScanRequest req = ImmudbProto.ZScanRequest - .newBuilder() - .setSet(Utils.toByteString(set)) - .setLimit(limit) + public Iterator zScan(byte[] set, double minScore, double maxScore, double seekScore, byte[] seekKey, + long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { + return pzScan(set, minScore, maxScore, seekScore, seekKey, seekAtTx, inclusiveSeek, reverse, limit); + } + + private synchronized Iterator pzScan(byte[] set, Double minScore, Double maxScore, double seekScore, + byte[] seekKey, + long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { + + final ImmudbProto.ZScanRequest.Builder reqBuilder = ImmudbProto.ZScanRequest.newBuilder(); + + reqBuilder.setSet(Utils.toByteString(set)) + .setSeekScore(seekScore) + .setSeekKey(Utils.toByteString(seekKey)) + .setSeekAtTx(seekAtTx) + .setInclusiveSeek(inclusiveSeek) .setDesc(reverse) - .build(); + .setLimit(limit); + + if (minScore != null) { + reqBuilder.setMinScore(Score.newBuilder().setScore(minScore).build()); + } + + if (maxScore != null) { + reqBuilder.setMaxScore(Score.newBuilder().setScore(maxScore).build()); + } - final Iterator chunks = blockingStub.streamZScan(req); + final Iterator chunks = blockingStub.streamZScan(reqBuilder.build()); return zentryIterator(chunks); } From 2e09dd1a06357610efbb9f93713ea12fcbdad86b Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Fri, 25 Nov 2022 15:12:11 -0300 Subject: [PATCH 23/25] chore: gradle clean Signed-off-by: Jeronimo Irazabal --- .github/workflows/gradle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5705bdf..1f52101 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -23,7 +23,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew build + run: ./gradlew clean build - name: Coveralls env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} From d6d7c894ab1094c0c7e8ae3e349849e296809b72 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Fri, 25 Nov 2022 15:25:30 -0300 Subject: [PATCH 24/25] chore: bump gradle version Signed-off-by: Jeronimo Irazabal --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 12f7085..68f2a7d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip From 373a575fc178396af71f591913a32e4880276d90 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Fri, 25 Nov 2022 16:02:46 -0300 Subject: [PATCH 25/25] fix: zscanAll optional seekKey Signed-off-by: Jeronimo Irazabal chore: default non-inclusive seek Signed-off-by: Jeronimo Irazabal chore: zscanall optional seekKey Signed-off-by: Jeronimo Irazabal chore: scanall optional seekKey Signed-off-by: Jeronimo Irazabal fix: optional seekKey Signed-off-by: Jeronimo Irazabal chore: revert gradle version Signed-off-by: Jeronimo Irazabal test: split scan and zscan test cases Signed-off-by: Jeronimo Irazabal test: simplified zscan Signed-off-by: Jeronimo Irazabal test: simplified zscan Signed-off-by: Jeronimo Irazabal minor code changes Signed-off-by: Jeronimo Irazabal --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../io/codenotary/immudb4j/ImmuClient.java | 40 ++++++++------ .../java/io/codenotary/immudb4j/ScanTest.java | 54 +++++++++---------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 68f2a7d..12f7085 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 4af070f..ca00ac3 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -581,8 +581,8 @@ public synchronized List historyAll(byte[] key, long offset, boolean desc try { ImmudbProto.Entries entries = blockingStub.history(ImmudbProto.HistoryRequest.newBuilder() .setKey(Utils.toByteString(key)) - .setOffset(offset) .setDesc(desc) + .setOffset(offset) .setLimit(limit) .build()); @@ -633,8 +633,8 @@ public synchronized List scanAll(byte[] prefix, byte[] seekKey, byte[] en .setEndKey(Utils.toByteString(endKey)) .setInclusiveSeek(inclusiveSeek) .setInclusiveEnd(inclusiveEnd) - .setLimit(limit) .setDesc(desc) + .setLimit(limit) .build(); final ImmudbProto.Entries entries = blockingStub.scan(req); @@ -972,11 +972,11 @@ public List zScanAll(String set) { } public List zScanAll(String set, boolean reverse, long limit) { - return pzScanAll(Utils.toByteArray(set), null, null, 0, null, 0, true, reverse, limit); + return pzScanAll(Utils.toByteArray(set), null, null, null, null, 0, false, reverse, limit); } public List zScanAll(byte[] set, double minScore, double maxScore, boolean reverse, long limit) { - return zScanAll(set, minScore, maxScore, 0, null, 0, true, false, 0); + return pzScanAll(set, minScore, maxScore, null, null, 0, false, false, 0); } public List zScanAll(byte[] set, double minScore, double maxScore, double seekScore, byte[] seekKey, @@ -984,19 +984,23 @@ public List zScanAll(byte[] set, double minScore, double maxScore, doubl return pzScanAll(set, minScore, maxScore, seekScore, seekKey, seekAtTx, inclusiveSeek, reverse, limit); } - private List pzScanAll(byte[] set, Double minScore, Double maxScore, double seekScore, byte[] seekKey, + private synchronized List pzScanAll(byte[] set, Double minScore, Double maxScore, Double seekScore, + byte[] seekKey, long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { final ImmudbProto.ZScanRequest.Builder reqBuilder = ImmudbProto.ZScanRequest.newBuilder(); reqBuilder.setSet(Utils.toByteString(set)) - .setSeekScore(seekScore) .setSeekKey(Utils.toByteString(seekKey)) .setSeekAtTx(seekAtTx) .setInclusiveSeek(inclusiveSeek) .setDesc(reverse) .setLimit(limit); + if (seekScore != null) { + reqBuilder.setSeekScore(seekScore); + } + if (minScore != null) { reqBuilder.setMinScore(Score.newBuilder().setScore(minScore).build()); } @@ -1010,10 +1014,6 @@ private List pzScanAll(byte[] set, Double minScore, Double maxScore, dou return buildList(zEntries); } - // - // ========== TX ========== - // - public synchronized Tx txById(long txId) throws TxNotFoundException, NoSuchAlgorithmException { try { final ImmudbProto.Tx tx = blockingStub.txById(ImmudbProto.TxRequest.newBuilder().setTx(txId).build()); @@ -1099,6 +1099,7 @@ public synchronized Tx verifiedTxById(long txId) throws TxNotFoundException, Ver public synchronized List txScanAll(long initialTxId) { final ImmudbProto.TxScanRequest req = ImmudbProto.TxScanRequest.newBuilder().setInitialTx(initialTxId).build(); final ImmudbProto.TxList txList = blockingStub.txScan(req); + return buildList(txList); } @@ -1106,9 +1107,10 @@ public synchronized List txScanAll(long initialTxId, int limit, boolean desc final ImmudbProto.TxScanRequest req = ImmudbProto.TxScanRequest .newBuilder() .setInitialTx(initialTxId) - .setLimit(limit) .setDesc(desc) + .setLimit(limit) .build(); + final ImmudbProto.TxList txList = blockingStub.txScan(req); return buildList(txList); } @@ -1351,6 +1353,7 @@ public Iterator scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolea public synchronized Iterator scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, boolean inclusiveEnd, boolean desc, long limit) { + final ImmudbProto.ScanRequest req = ScanRequest.newBuilder() .setPrefix(Utils.toByteString(prefix)) .setSeekKey(Utils.toByteString(seekKey)) @@ -1375,11 +1378,11 @@ public Iterator zScan(String set) { } public Iterator zScan(String set, boolean reverse, long limit) { - return pzScan(Utils.toByteArray(set), null, null, 0, null, 0, true, reverse, limit); + return pzScan(Utils.toByteArray(set), null, null, null, null, 0, false, reverse, limit); } public Iterator zScan(byte[] set, double minScore, double maxScore, boolean reverse, long limit) { - return zScan(set, minScore, maxScore, 0, null, 0, true, false, 0); + return pzScan(set, minScore, maxScore, null, null, 0, false, false, 0); } public Iterator zScan(byte[] set, double minScore, double maxScore, double seekScore, byte[] seekKey, @@ -1387,20 +1390,23 @@ public Iterator zScan(byte[] set, double minScore, double maxScore, doub return pzScan(set, minScore, maxScore, seekScore, seekKey, seekAtTx, inclusiveSeek, reverse, limit); } - private synchronized Iterator pzScan(byte[] set, Double minScore, Double maxScore, double seekScore, + private synchronized Iterator pzScan(byte[] set, Double minScore, Double maxScore, Double seekScore, byte[] seekKey, long seekAtTx, boolean inclusiveSeek, boolean reverse, long limit) { final ImmudbProto.ZScanRequest.Builder reqBuilder = ImmudbProto.ZScanRequest.newBuilder(); reqBuilder.setSet(Utils.toByteString(set)) - .setSeekScore(seekScore) .setSeekKey(Utils.toByteString(seekKey)) .setSeekAtTx(seekAtTx) .setInclusiveSeek(inclusiveSeek) .setDesc(reverse) .setLimit(limit); + if (seekScore != null) { + reqBuilder.setSeekScore(seekScore); + } + if (minScore != null) { reqBuilder.setMinScore(Score.newBuilder().setScore(minScore).build()); } @@ -1427,9 +1433,9 @@ public synchronized Iterator history(byte[] key, long offset, boolean des try { ImmudbProto.HistoryRequest req = ImmudbProto.HistoryRequest.newBuilder() .setKey(Utils.toByteString(key)) - .setLimit(limit) - .setOffset(offset) .setDesc(desc) + .setOffset(offset) + .setLimit(limit) .build(); final Iterator chunks = blockingStub.streamHistory(req); diff --git a/src/test/java/io/codenotary/immudb4j/ScanTest.java b/src/test/java/io/codenotary/immudb4j/ScanTest.java index 73fd2eb..3fe658a 100644 --- a/src/test/java/io/codenotary/immudb4j/ScanTest.java +++ b/src/test/java/io/codenotary/immudb4j/ScanTest.java @@ -25,12 +25,12 @@ public class ScanTest extends ImmuClientIntegrationTest { - @Test(testName = "scan", priority = 2) + @Test(testName = "scan zscan") public void t1() { immuClient.openSession("defaultdb", "immudb", "immudb"); - byte[] value1 = {0, 1, 2, 3}; - byte[] value2 = {4, 5, 6, 7}; + byte[] value1 = { 0, 1, 2, 3 }; + byte[] value2 = { 4, 5, 6, 7 }; try { immuClient.set("scan1", value1); @@ -39,6 +39,23 @@ public void t1() { Assert.fail("Failed at set.", e); } + try { + immuClient.set("zadd1", value1); + immuClient.set("zadd2", value2); + } catch (CorruptedDataException e) { + Assert.fail("Failed at set.", e); + } + + try { + immuClient.zAdd("set1", "zadd1", 1); + immuClient.zAdd("set1", "zadd2", 2); + + immuClient.zAdd("set2", "zadd1", 2); + immuClient.zAdd("set2", "zadd2", 1); + } catch (CorruptedDataException e) { + Assert.fail("Failed to zAdd", e); + } + List scanResult = immuClient.scanAll("scan"); System.out.println(scanResult.size()); @@ -63,32 +80,7 @@ public void t1() { Assert.assertEquals(i, 2); - immuClient.closeSession(); - } - - @Test(testName = "set, zAdd, zScan", priority = 3) - public void t2() { - immuClient.openSession("defaultdb", "immudb", "immudb"); - - byte[] value1 = {0, 1, 2, 3}; - byte[] value2 = {4, 5, 6, 7}; - - try { - immuClient.set("zadd1", value1); - immuClient.set("zadd2", value2); - } catch (CorruptedDataException e) { - Assert.fail("Failed at set.", e); - } - - try { - immuClient.zAdd("set1", "zadd1", 1); - immuClient.zAdd("set1", "zadd2", 2); - - immuClient.zAdd("set2", "zadd1", 2); - immuClient.zAdd("set2", "zadd2", 1); - } catch (CorruptedDataException e) { - Assert.fail("Failed to zAdd", e); - } + Assert.assertFalse(immuClient.scan("nonexistent-prefix").hasNext()); List zScan1 = immuClient.zScanAll("set1", false, 5); Assert.assertEquals(zScan1.size(), 2); @@ -103,7 +95,7 @@ public void t2() { Assert.assertEquals(zScan2.size(), 2); Iterator zScan3 = immuClient.zScan("set2"); - int i = 0; + i = 0; while (zScan3.hasNext()) { Assert.assertEquals(zScan3.next().getKey(), zScan2.get(i).getKey()); @@ -112,6 +104,8 @@ public void t2() { Assert.assertEquals(i, 2); + Assert.assertFalse(immuClient.zScan("nonexistent-set").hasNext()); + immuClient.closeSession(); }