From f0038517ecb520bfe9b5e6e766e0a7b496c77098 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Tue, 25 Oct 2022 09:50:21 -0300 Subject: [PATCH 01/29] chore: use immudb v1.4.0 Signed-off-by: Jeronimo Irazabal --- build.gradle | 2 +- immudb/start.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 46bc2e9..2cf56fd 100644 --- a/build.gradle +++ b/build.gradle @@ -134,7 +134,7 @@ task immudbStart { pb.command("/bin/bash", "immudb/start.sh") Process proc = pb.start() - proc.waitFor(10, TimeUnit.SECONDS) + proc.waitFor(5, TimeUnit.SECONDS) if (proc.isAlive()) { println "immudb has been started." diff --git a/immudb/start.sh b/immudb/start.sh index 3e357e4..277a6e2 100755 --- a/immudb/start.sh +++ b/immudb/start.sh @@ -13,10 +13,10 @@ then echo "Downloading immudb..." if [[ "$OSTYPE" == "linux-gnu"* ]]; then - URL=https://github.com/vchain-us/immudb/releases/download/v1.3.0/immudb-v1.3.0-linux-amd64 + URL=https://github.com/vchain-us/immudb/releases/download/v1.4.0/immudb-v1.4.0-linux-amd64 wget -O immudb $URL elif [[ "$OSTYPE" == "darwin"* ]]; then - URL=https://github.com/vchain-us/immudb/releases/download/v1.3.0/immudb-v1.3.0-darwin-amd64 + URL=https://github.com/vchain-us/immudb/releases/download/v1.4.0/immudb-v1.4.0-darwin-amd64 curl -o immudb -L $URL fi From 4b6d55fe15d1573095c08fd2c58afe52bc11072b Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Tue, 25 Oct 2022 09:52:09 -0300 Subject: [PATCH 02/29] chore: state handling by database name Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/FileImmuStateHolder.java | 12 ++++++------ .../java/io/codenotary/immudb4j/ImmuStateHolder.java | 4 ++-- .../immudb4j/SerializableImmuStateHolder.java | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/FileImmuStateHolder.java b/src/main/java/io/codenotary/immudb4j/FileImmuStateHolder.java index 9f2be64..b30d701 100644 --- a/src/main/java/io/codenotary/immudb4j/FileImmuStateHolder.java +++ b/src/main/java/io/codenotary/immudb4j/FileImmuStateHolder.java @@ -59,21 +59,21 @@ private FileImmuStateHolder(Builder builder) throws IOException, IllegalStateExc } @Override - public synchronized ImmuState getState(String serverUuid, String database) { - return stateHolder.getState(serverUuid, database); + public synchronized ImmuState getState(String database) { + return stateHolder.getState(database); } @Override - public synchronized void setState(String serverUuid, ImmuState state) throws IllegalStateException { + public synchronized void setState(ImmuState state) throws IllegalStateException { - ImmuState currentState = stateHolder.getState(serverUuid, state.getDatabase()); + ImmuState currentState = stateHolder.getState(state.getDatabase()); if (currentState != null && currentState.getTxId() >= state.getTxId()) { return; } - stateHolder.setState(serverUuid, state); + stateHolder.setState(state); - Path newStateFile = statesFolder.resolve("state_" + serverUuid + "_" + state.getDatabase() + "_" + System.nanoTime()); + Path newStateFile = statesFolder.resolve("state_" + state.getDatabase() + "_" + System.nanoTime()); if (Files.exists(newStateFile)) { throw new RuntimeException("Failed attempting to create a new state file. Please retry."); diff --git a/src/main/java/io/codenotary/immudb4j/ImmuStateHolder.java b/src/main/java/io/codenotary/immudb4j/ImmuStateHolder.java index 7938b11..f2b7821 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuStateHolder.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuStateHolder.java @@ -18,8 +18,8 @@ public interface ImmuStateHolder { - ImmuState getState(String serverUuid, String database); + ImmuState getState(String database); - void setState(String serverUuid, ImmuState state); + void setState(ImmuState state); } diff --git a/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java b/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java index b72a867..772318a 100644 --- a/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java +++ b/src/main/java/io/codenotary/immudb4j/SerializableImmuStateHolder.java @@ -27,7 +27,7 @@ public class SerializableImmuStateHolder implements ImmuStateHolder { /** - * Mapping "{serverUuid}_{databaseName}" to the appropriate state. + * Mapping "{databaseName}" to the appropriate state. */ private Map statesMap = new HashMap<>(); @@ -46,13 +46,13 @@ public void writeTo(OutputStream os) throws IOException { } @Override - public ImmuState getState(String serverUuid, String database) { - return this.statesMap.get(serverUuid + "_" + database); + public ImmuState getState(String database) { + return this.statesMap.get(database); } @Override - public void setState(String serverUuid, ImmuState state) { - this.statesMap.put(serverUuid + "_" + state.getDatabase(), state); + public void setState(ImmuState state) { + this.statesMap.put(state.getDatabase(), state); } } From de50cdaaad35138ba6685fef4426cd47c016a932 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Tue, 25 Oct 2022 15:49:50 -0300 Subject: [PATCH 03/29] chore: session-based authentication Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 443 +++++----- .../immudb4j/ImmuServerUUIDInterceptor.java | 45 - .../ImmudbAuthRequestInterceptor.java | 37 + .../java/io/codenotary/immudb4j/Session.java} | 33 +- src/main/proto/schema.proto | 774 +++++++++--------- .../immudb4j/BasicImmuClientTest.java | 21 +- .../io/codenotary/immudb4j/HTreeTest.java | 4 - .../io/codenotary/immudb4j/HistoryTest.java | 7 +- .../immudb4j/ListDatabasesTest.java | 5 +- .../io/codenotary/immudb4j/ListUsersTest.java | 5 +- .../LoginAndHealthCheckAndCleanIndexTest.java | 14 +- .../immudb4j/MultidatabaseTest.java | 22 +- .../codenotary/immudb4j/MultithreadTest.java | 12 +- .../io/codenotary/immudb4j/ReferenceTest.java | 5 +- .../java/io/codenotary/immudb4j/ScanTest.java | 12 +- .../immudb4j/SetAllAndGetAllTest.java | 6 +- .../io/codenotary/immudb4j/SetAndGetTest.java | 6 +- .../io/codenotary/immudb4j/ShutdownTest.java | 9 +- .../io/codenotary/immudb4j/StateTest.java | 19 +- .../java/io/codenotary/immudb4j/TxTest.java | 12 +- .../io/codenotary/immudb4j/UserMgmtTest.java | 23 +- .../immudb4j/VerifiedSetAndGetTest.java | 12 +- .../java/io/codenotary/immudb4j/ZAddTest.java | 6 +- tests.sh | 11 +- 24 files changed, 758 insertions(+), 785 deletions(-) delete mode 100644 src/main/java/io/codenotary/immudb4j/ImmuServerUUIDInterceptor.java create mode 100644 src/main/java/io/codenotary/immudb4j/ImmudbAuthRequestInterceptor.java rename src/{test/java/io/codenotary/immudb4j/UseDatabaseTest.java => main/java/io/codenotary/immudb4j/Session.java} (56%) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index b62ae36..277fd97 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -28,9 +28,8 @@ import io.codenotary.immudb4j.user.User; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; -import io.grpc.Metadata; import io.grpc.StatusRuntimeException; -import io.grpc.stub.MetadataUtils; +import io.grpc.ConnectivityState; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; @@ -49,21 +48,19 @@ */ public class ImmuClient { - private static final String AUTH_HEADER = "authorization"; - private final ImmuServiceGrpc.ImmuServiceBlockingStub stub; - private final boolean withAuth; + private final PublicKey serverSigningKey; private final ImmuStateHolder stateHolder; + private ManagedChannel channel; - private String authToken; - private String currentServerUuid; - private String currentDb = "defaultdb"; - private final PublicKey serverSigningKey; + + private final ImmuServiceGrpc.ImmuServiceBlockingStub stub; + + private Session session; public ImmuClient(Builder builder) { - this.stub = createStubFrom(builder); - this.withAuth = builder.isWithAuth(); this.stateHolder = builder.getStateHolder(); this.serverSigningKey = builder.getServerSigningKey(); + this.stub = createStubFrom(builder); } public static Builder newBuilder() { @@ -73,96 +70,108 @@ public static Builder newBuilder() { private ImmuServiceGrpc.ImmuServiceBlockingStub createStubFrom(Builder builder) { channel = ManagedChannelBuilder.forAddress(builder.getServerUrl(), builder.getServerPort()) .usePlaintext() - .intercept(new ImmuServerUUIDInterceptor(this)) + .intercept(new ImmudbAuthRequestInterceptor(this)) .build(); return ImmuServiceGrpc.newBlockingStub(channel); } - // --------------------------------------------------------------------- - // These two currentServerUuid related methods are not publicly exposed, - // since these should be called by the ImmuServerUUIDInterceptor only. - - void setCurrentServerUuid(String serverUuid) { - currentServerUuid = serverUuid; - } - - String getCurrentServerUuid() { - return currentServerUuid; - } - // --------------------------------------------------------------------- - public synchronized void shutdown() { if (channel == null) { return; } - channel.shutdown(); - if (!channel.isShutdown()) { - try { - channel.awaitTermination(2, TimeUnit.SECONDS); - } catch (InterruptedException e) { - // nothing to do here. + + if (session != null) { + closeSession(); + } + + try { + channel.shutdown(); + if (!channel.isShutdown()) { + try { + channel.awaitTermination(1, TimeUnit.SECONDS); + } catch (InterruptedException e) { + channel.shutdownNow(); + } } + } finally { + channel = null; } - channel = null; } - public synchronized boolean isShutdown() { - return channel == null; + Session getSession() { + return this.session; } - private ImmuServiceGrpc.ImmuServiceBlockingStub getStub() { - if (!withAuth || authToken == null) { - return stub; + public synchronized void openSession(String username, String password, String database) { + if (this.session != null) { + throw new IllegalStateException("session already opened"); } - Metadata metadata = new Metadata(); - metadata.put(Metadata.Key.of(AUTH_HEADER, Metadata.ASCII_STRING_MARSHALLER), "Bearer " + authToken); - - return MetadataUtils.attachHeaders(stub, metadata); - } - - public synchronized void login(String username, String password) { - ImmudbProto.LoginRequest loginRequest = ImmudbProto.LoginRequest + final ImmudbProto.OpenSessionRequest req = ImmudbProto.OpenSessionRequest .newBuilder() - .setUser(Utils.toByteString(username)) + .setUsername(Utils.toByteString(username)) .setPassword(Utils.toByteString(password)) + .setDatabaseName(database) .build(); - ImmudbProto.LoginResponse loginResponse = getStub().login(loginRequest); - authToken = loginResponse.getToken(); + final ImmudbProto.OpenSessionResponse resp = this.stub.openSession(req); + + this.session = new Session(resp.getSessionID(), username, database); } - public synchronized void logout() { - getStub().logout(com.google.protobuf.Empty.getDefaultInstance()); - authToken = null; + public synchronized void closeSession() { + if (this.session == null) { + throw new IllegalStateException("no open session"); + } + + try { + this.stub.closeSession(Empty.getDefaultInstance()); + } finally { + this.session = null; + } } /** * Get the locally saved state of the current database. * If nothing exists already, it is fetched from the server and save it locally. */ - public ImmuState state() throws VerificationException { - ImmuState state = stateHolder.getState(currentServerUuid, currentDb); + private ImmuState state() throws VerificationException { + if (this.session == null) { + throw new IllegalStateException("no open session"); + } + + ImmuState state = this.stateHolder.getState(this.session.getDatabase()); + if (state == null) { - state = currentState(); - stateHolder.setState(currentServerUuid, state); + state = this.currentState(); + stateHolder.setState(state); } + return state; } /** * Get the current database state that exists on the server. - * It may throw a RuntimeException if server's state signature verification fails + * It may throw VerificationException if server's state signature verification + * fails * (if this feature is enabled on the client side, at least). + * Note: local state is not updated because this is not a verified operation */ - public ImmuState currentState() throws VerificationException { - final Empty empty = com.google.protobuf.Empty.getDefaultInstance(); - final ImmudbProto.ImmutableState state = getStub().currentState(empty); + public synchronized ImmuState currentState() throws VerificationException { + if (this.session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.ImmutableState state = this.stub.currentState(Empty.getDefaultInstance()); final ImmuState immuState = ImmuState.valueOf(state); - if (!immuState.checkSignature(serverSigningKey)) { + if (!this.session.getDatabase().equals(immuState.getDatabase())) { + throw new VerificationException("database mismatch"); + } + + if (!immuState.checkSignature(this.serverSigningKey)) { throw new VerificationException("State signature verification failed"); } @@ -173,32 +182,29 @@ public ImmuState currentState() throws VerificationException { // ========== DATABASE ========== // - public void createDatabase(String database) { - final ImmudbProto.CreateDatabaseRequest db = ImmudbProto.CreateDatabaseRequest.newBuilder() + public synchronized void createDatabase(String database) { + if (this.session == null) { + throw new IllegalStateException("no open session"); + } + + final ImmudbProto.CreateDatabaseRequest req = ImmudbProto.CreateDatabaseRequest.newBuilder() .setName(database) .build(); - getStub().createDatabaseV2(db); + this.stub.createDatabaseV2(req); } - public synchronized void useDatabase(String database) { - final ImmudbProto.Database db = ImmudbProto.Database.newBuilder() - .setDatabaseName(database) - .build(); + public synchronized List databases() { + if (this.session == null) { + throw new IllegalStateException("no open session"); + } - final ImmudbProto.UseDatabaseReply response = getStub().useDatabase(db); + final ImmudbProto.DatabaseListRequestV2 req = ImmudbProto.DatabaseListRequestV2.newBuilder().build(); + final ImmudbProto.DatabaseListResponseV2 resp = this.stub.databaseListV2(req); - authToken = response.getToken(); - currentDb = database; - } + final List list = new ArrayList<>(resp.getDatabasesCount()); - public List databases() { - final ImmudbProto.DatabaseListRequestV2 req = ImmudbProto.DatabaseListRequestV2.newBuilder().build(); - final ImmudbProto.DatabaseListResponseV2 res = getStub().databaseListV2(req); - - final List list = new ArrayList<>(res.getDatabasesCount()); - - for (ImmudbProto.DatabaseWithSettings db : res.getDatabasesList()) { + for (ImmudbProto.DatabaseWithSettings db : resp.getDatabasesList()) { list.add(db.getName()); } @@ -221,14 +227,14 @@ public Entry getAtTx(String key, long tx) throws KeyNotFoundException { return getAtTx(Utils.toByteArray(key), tx); } - public Entry getAtTx(byte[] key, long tx) throws KeyNotFoundException { - final ImmudbProto.KeyRequest req =ImmudbProto.KeyRequest.newBuilder() - .setKey(Utils.toByteString(key)) - .setAtTx(tx) - .build(); + public synchronized Entry getAtTx(byte[] key, long tx) throws KeyNotFoundException { + final ImmudbProto.KeyRequest req = ImmudbProto.KeyRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .setAtTx(tx) + .build(); try { - return Entry.valueOf(getStub().get(req)); + return Entry.valueOf(this.stub.get(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -242,14 +248,14 @@ public Entry getSinceTx(String key, long tx) throws KeyNotFoundException { return getSinceTx(Utils.toByteArray(key), tx); } - public Entry getSinceTx(byte[] key, long tx) throws KeyNotFoundException { - final ImmudbProto.KeyRequest req =ImmudbProto.KeyRequest.newBuilder() + public synchronized Entry getSinceTx(byte[] key, long tx) throws KeyNotFoundException { + final ImmudbProto.KeyRequest req = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setSinceTx(tx) .build(); try { - return Entry.valueOf(getStub().get(req)); + return Entry.valueOf(this.stub.get(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -263,14 +269,14 @@ public Entry getAtRevision(String key, long rev) throws KeyNotFoundException { return getAtRevision(Utils.toByteArray(key), rev); } - public Entry getAtRevision(byte[] key, long rev) throws KeyNotFoundException { - final ImmudbProto.KeyRequest req =ImmudbProto.KeyRequest.newBuilder() + public synchronized Entry getAtRevision(byte[] key, long rev) throws KeyNotFoundException { + final ImmudbProto.KeyRequest req = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setAtRevision(rev) .build(); try { - return Entry.valueOf(getStub().get(req)); + return Entry.valueOf(this.stub.get(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -280,15 +286,15 @@ public Entry getAtRevision(byte[] key, long rev) throws KeyNotFoundException { } } - public List getAll(List keys) { + public synchronized List getAll(List keys) { final List keysBS = new ArrayList<>(keys.size()); - + for (String key : keys) { keysBS.add(Utils.toByteString(key)); } final ImmudbProto.KeyListRequest req = ImmudbProto.KeyListRequest.newBuilder().addAllKeys(keysBS).build(); - final ImmudbProto.Entries entries = getStub().getAll(req); + final ImmudbProto.Entries entries = this.stub.getAll(req); final List result = new ArrayList<>(entries.getEntriesCount()); @@ -311,51 +317,62 @@ public Entry verifiedGetAtTx(String key, long tx) throws KeyNotFoundException, V return verifiedGetAtTx(Utils.toByteArray(key), tx); } - public Entry verifiedGetAtTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { + public synchronized Entry verifiedGetAtTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { + final ImmuState state = this.state(); + final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setAtTx(tx) .build(); - return verifiedGet(keyReq, state()); + return verifiedGet(keyReq, state); } public Entry verifiedGetSinceTx(String key, long tx) throws KeyNotFoundException, VerificationException { return verifiedGetSinceTx(Utils.toByteArray(key), tx); } - public Entry verifiedGetSinceTx(byte[] key, long tx) throws KeyNotFoundException, VerificationException { + public synchronized Entry verifiedGetSinceTx(byte[] key, long tx) + throws KeyNotFoundException, VerificationException { + + final ImmuState state = this.state(); + final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setSinceTx(tx) .build(); - return verifiedGet(keyReq, state()); + return verifiedGet(keyReq, state); } public Entry verifiedGetAtRevision(String key, long rev) throws KeyNotFoundException, VerificationException { return verifiedGetAtRevision(Utils.toByteArray(key), rev); } - public Entry verifiedGetAtRevision(byte[] key, long rev) throws KeyNotFoundException, VerificationException { + public synchronized Entry verifiedGetAtRevision(byte[] key, long rev) + throws KeyNotFoundException, VerificationException { + + final ImmuState state = this.state(); + final ImmudbProto.KeyRequest keyReq = ImmudbProto.KeyRequest.newBuilder() .setKey(Utils.toByteString(key)) .setAtRevision(rev) .build(); - return verifiedGet(keyReq, state()); + return verifiedGet(keyReq, state); } - private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throws KeyNotFoundException, VerificationException { + private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) + throws KeyNotFoundException, VerificationException { final ImmudbProto.VerifiableGetRequest vGetReq = ImmudbProto.VerifiableGetRequest.newBuilder() .setKeyRequest(keyReq) .setProveSinceTx(state.getTxId()) .build(); - + final ImmudbProto.VerifiableEntry vEntry; try { - vEntry = getStub().verifiableGet(vGetReq); + vEntry = this.stub.verifiableGet(vGetReq); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -371,14 +388,15 @@ private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throws long sourceId, targetId; byte[] sourceAlh; byte[] targetAlh; - + final Entry entry = Entry.valueOf(vEntry.getEntry()); if (entry.getReferenceBy() == null && !Arrays.equals(keyReq.getKey().toByteArray(), entry.getKey())) { throw new VerificationException("Data is corrupted: entry does not belong to specified key"); } - if (entry.getReferenceBy() != null && !Arrays.equals(keyReq.getKey().toByteArray(), entry.getReferenceBy().getKey())) { + if (entry.getReferenceBy() != null + && !Arrays.equals(keyReq.getKey().toByteArray(), entry.getReferenceBy().getKey())) { throw new VerificationException("Data is corrupted: entry does not belong to specified key"); } @@ -420,14 +438,13 @@ private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throws sourceId, targetId, sourceAlh, - targetAlh - )) { + targetAlh)) { throw new VerificationException("Dual proof verification failed."); } } final ImmuState newState = new ImmuState( - currentDb, + this.session.getDatabase(), targetId, targetAlh, vEntry.getVerifiableTx().getSignature().toByteArray()); @@ -436,7 +453,7 @@ private Entry verifiedGet(ImmudbProto.KeyRequest keyReq, ImmuState state) throws throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + this.stateHolder.setState(newState); return Entry.valueOf(vEntry.getEntry()); } @@ -449,13 +466,13 @@ public TxHeader delete(String key) throws KeyNotFoundException { return delete(Utils.toByteArray(key)); } - public TxHeader delete(byte[] key) throws KeyNotFoundException { + public synchronized TxHeader delete(byte[] key) throws KeyNotFoundException { try { final ImmudbProto.DeleteKeysRequest req = ImmudbProto.DeleteKeysRequest.newBuilder() .addKeys(Utils.toByteString(key)) .build(); - return TxHeader.valueOf(getStub().delete(req)); + return TxHeader.valueOf(this.stub.delete(req)); } catch (StatusRuntimeException e) { if (e.getMessage().contains("key not found")) { throw new KeyNotFoundException(); @@ -473,15 +490,15 @@ public List history(String key, int limit, long offset, boolean desc) thr return history(Utils.toByteArray(key), limit, offset, desc); } - public List history(byte[] key, int limit, long offset, boolean desc) throws KeyNotFoundException { + public synchronized List history(byte[] key, int limit, long offset, boolean desc) + throws KeyNotFoundException { try { - ImmudbProto.Entries entries = getStub().history(ImmudbProto.HistoryRequest.newBuilder() - .setKey(Utils.toByteString(key)) - .setLimit(limit) - .setOffset(offset) - .setDesc(desc) - .build() - ); + ImmudbProto.Entries entries = this.stub.history(ImmudbProto.HistoryRequest.newBuilder() + .setKey(Utils.toByteString(key)) + .setLimit(limit) + .setOffset(offset) + .setDesc(desc) + .build()); return buildList(entries); } catch (StatusRuntimeException e) { @@ -529,8 +546,9 @@ public List scan(byte[] prefix, byte[] seekKey, byte[] endKey, long limit return scan(prefix, seekKey, endKey, false, false, limit, desc); } - public List scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean inclusiveSeek, boolean inclusiveEnd, - long limit, boolean desc) { + public synchronized List scan(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)) @@ -541,7 +559,7 @@ public List scan(byte[] prefix, byte[] seekKey, byte[] endKey, boolean in .setDesc(desc) .build(); - final ImmudbProto.Entries entries = getStub().scan(req); + final ImmudbProto.Entries entries = this.stub.scan(req); return buildList(entries); } @@ -553,15 +571,15 @@ public TxHeader set(String key, byte[] value) throws CorruptedDataException { return set(Utils.toByteArray(key), value); } - public TxHeader set(byte[] key, byte[] value) throws CorruptedDataException { + public synchronized TxHeader set(byte[] key, byte[] value) throws CorruptedDataException { final ImmudbProto.KeyValue kv = ImmudbProto.KeyValue.newBuilder() .setKey(Utils.toByteString(key)) .setValue(Utils.toByteString(value)) .build(); final ImmudbProto.SetRequest req = ImmudbProto.SetRequest.newBuilder().addKVs(kv).build(); - final ImmudbProto.TxHeader txHdr = getStub().set(req); - + final ImmudbProto.TxHeader txHdr = this.stub.set(req); + if (txHdr.getNentries() != 1) { throw new CorruptedDataException(); } @@ -569,9 +587,9 @@ public TxHeader set(byte[] key, byte[] value) throws CorruptedDataException { return TxHeader.valueOf(txHdr); } - public TxHeader setAll(List kvList) throws CorruptedDataException { + public synchronized TxHeader setAll(List kvList) throws CorruptedDataException { final ImmudbProto.SetRequest.Builder reqBuilder = ImmudbProto.SetRequest.newBuilder(); - + for (KVPair kv : kvList) { ImmudbProto.KeyValue.Builder kvBuilder = ImmudbProto.KeyValue.newBuilder(); @@ -581,7 +599,7 @@ public TxHeader setAll(List kvList) throws CorruptedDataException { reqBuilder.addKVs(kvBuilder.build()); } - final ImmudbProto.TxHeader txHdr = getStub().set(reqBuilder.build()); + final ImmudbProto.TxHeader txHdr = this.stub.set(reqBuilder.build()); if (txHdr.getNentries() != kvList.size()) { throw new CorruptedDataException(); @@ -602,7 +620,8 @@ public TxHeader setReference(String key, String referencedKey, long atTx) throws return setReference(Utils.toByteArray(key), Utils.toByteArray(referencedKey), atTx); } - public TxHeader setReference(byte[] key, byte[] referencedKey, long atTx) throws CorruptedDataException { + public synchronized TxHeader setReference(byte[] key, byte[] referencedKey, long atTx) + throws CorruptedDataException { final ImmudbProto.ReferenceRequest req = ImmudbProto.ReferenceRequest.newBuilder() .setKey(Utils.toByteString(key)) .setReferencedKey(Utils.toByteString(referencedKey)) @@ -610,8 +629,8 @@ public TxHeader setReference(byte[] key, byte[] referencedKey, long atTx) throws .setBoundRef(atTx > 0) .build(); - final ImmudbProto.TxHeader txHdr = getStub().setReference(req); - + final ImmudbProto.TxHeader txHdr = this.stub.setReference(req); + if (txHdr.getNentries() != 1) { throw new CorruptedDataException(); } @@ -619,34 +638,34 @@ public TxHeader setReference(byte[] key, byte[] referencedKey, long atTx) throws return TxHeader.valueOf(txHdr); } - public TxHeader verifiedSet(String key, byte[] value) throws VerificationException { + public TxHeader verifiedSet(String key, byte[] value) throws VerificationException { return verifiedSet(Utils.toByteArray(key), value); } - public TxHeader verifiedSet(byte[] key, byte[] value) throws VerificationException { - final ImmuState state = state(); + public synchronized TxHeader verifiedSet(byte[] key, byte[] value) throws VerificationException { + final ImmuState state = this.state(); + final ImmudbProto.KeyValue kv = ImmudbProto.KeyValue.newBuilder() - .setKey(Utils.toByteString(key)) - .setValue(Utils.toByteString(value)) - .build(); - + .setKey(Utils.toByteString(key)) + .setValue(Utils.toByteString(value)) + .build(); + final ImmudbProto.VerifiableSetRequest vSetReq = ImmudbProto.VerifiableSetRequest.newBuilder() .setSetRequest(ImmudbProto.SetRequest.newBuilder().addKVs(kv).build()) .setProveSinceTx(state.getTxId()) .build(); - - final ImmudbProto.VerifiableTx vtx = getStub().verifiableSet(vSetReq); - + + final ImmudbProto.VerifiableTx vtx = this.stub.verifiableSet(vSetReq); + final int ne = vtx.getTx().getHeader().getNentries(); - + if (ne != 1 || vtx.getTx().getEntriesList().size() != 1) { throw new VerificationException( - String.format("Got back %d entries (in tx metadata) instead of 1.", ne) - ); + String.format("Got back %d entries (in tx metadata) instead of 1.", ne)); } - + final Tx tx; - + try { tx = Tx.valueOf(vtx.getTx()); } catch (Exception e) { @@ -658,8 +677,7 @@ public TxHeader verifiedSet(byte[] key, byte[] value) throws VerificationExcepti final Entry entry = Entry.valueOf(ImmudbProto.Entry.newBuilder() .setKey(Utils.toByteString(key)) .setValue(Utils.toByteString(value)) - .build() - ); + .build()); final InclusionProof inclusionProof = tx.proof(entry.getEncodedKey()); @@ -673,18 +691,19 @@ public TxHeader verifiedSet(byte[] key, byte[] value) throws VerificationExcepti throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + this.stateHolder.setState(newState); return TxHeader.valueOf(vtx.getTx().getHeader()); } - public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey) throws VerificationException { return verifiedSetReference(key, referencedKey, 0); } - public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx) throws VerificationException { - final ImmuState state = state(); + public synchronized TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx) + throws VerificationException { + + final ImmuState state = this.state(); final ImmudbProto.ReferenceRequest refReq = ImmudbProto.ReferenceRequest.newBuilder() .setKey(Utils.toByteString(key)) @@ -698,8 +717,8 @@ public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx .setProveSinceTx(state.getTxId()) .build(); - final ImmudbProto.VerifiableTx vtx = getStub().verifiableSetReference(vRefReq); - + final ImmudbProto.VerifiableTx vtx = this.stub.verifiableSetReference(vRefReq); + final int vtxNentries = vtx.getTx().getHeader().getNentries(); if (vtxNentries != 1) { throw new VerificationException(String.format("Data is corrupted (verifTx has %d Nentries instead of 1).", @@ -720,18 +739,17 @@ public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx .setReferencedBy(ImmudbProto.Reference.newBuilder() .setKey(Utils.toByteString(key)) .setAtTx(atTx) - .build() - ) - .build() - ); + .build()) + .build()); final InclusionProof inclusionProof = tx.proof(entry.getEncodedKey()); - + if (!CryptoUtils.verifyInclusion(inclusionProof, entry.digestFor(txHeader.getVersion()), txHeader.getEh())) { throw new VerificationException("Data is corrupted (inclusion verification failed)."); } - if (Arrays.equals(txHeader.getEh(), CryptoUtils.digestFrom(vtx.getDualProof().getTargetTxHeader().getEH().toByteArray()))) { + if (Arrays.equals(txHeader.getEh(), + CryptoUtils.digestFrom(vtx.getDualProof().getTargetTxHeader().getEH().toByteArray()))) { throw new VerificationException("Data is corrupted (different digests)."); } @@ -741,7 +759,7 @@ public TxHeader verifiedSetReference(byte[] key, byte[] referencedKey, long atTx throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + this.stateHolder.setState(newState); return TxHeader.valueOf(vtx.getTx().getHeader()); } @@ -760,13 +778,12 @@ private ImmuState verifyDualProof(ImmudbProto.VerifiableTx vtx, Tx tx, ImmuState sourceId, targetId, sourceAlh, - targetAlh - )) { + targetAlh)) { throw new VerificationException("Data is corrupted (dual proof verification failed)."); } } - return new ImmuState(currentDb, targetId, targetAlh, vtx.getSignature().getSignature().toByteArray()); + return new ImmuState(state.getDatabase(), targetId, targetAlh, vtx.getSignature().getSignature().toByteArray()); } // @@ -781,17 +798,16 @@ public TxHeader zAdd(byte[] set, byte[] key, double score) throws CorruptedDataE return zAdd(set, key, 0, score); } - public TxHeader zAdd(byte[] set, byte[] key, long atTx, double score) throws CorruptedDataException { - final ImmudbProto.TxHeader txHdr = getStub().zAdd( + public synchronized TxHeader zAdd(byte[] set, byte[] key, long atTx, double score) throws CorruptedDataException { + final ImmudbProto.TxHeader txHdr = this.stub.zAdd( ImmudbProto.ZAddRequest.newBuilder() .setSet(Utils.toByteString(set)) .setKey(Utils.toByteString(key)) .setAtTx(atTx) .setScore(score) .setBoundRef(atTx > 0) - .build() - ); - + .build()); + if (txHdr.getNentries() != 1) { throw new CorruptedDataException(); } @@ -799,21 +815,22 @@ public TxHeader zAdd(byte[] set, byte[] key, long atTx, double score) throws Cor return TxHeader.valueOf(txHdr); } - public TxHeader verifiedZAdd(String set, String key, double score) throws VerificationException { return verifiedZAdd(Utils.toByteArray(set), Utils.toByteArray(key), score); } public TxHeader verifiedZAdd(byte[] set, byte[] key, double score) throws VerificationException { - return verifiedZAdd(set, key,0, score); + return verifiedZAdd(set, key, 0, score); } public TxHeader verifiedZAdd(String set, String key, long atTx, double score) throws VerificationException { return verifiedZAdd(Utils.toByteArray(set), Utils.toByteArray(key), atTx, score); } - public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) throws VerificationException { - final ImmuState state = state(); + public synchronized TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) + throws VerificationException { + + final ImmuState state = this.state(); final ImmudbProto.ZAddRequest zAddReq = ImmudbProto.ZAddRequest.newBuilder() .setSet(Utils.toByteString(set)) @@ -822,13 +839,13 @@ public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) th .setBoundRef(atTx > 0) .setScore(score) .build(); - + final ImmudbProto.VerifiableZAddRequest vZAddReq = ImmudbProto.VerifiableZAddRequest.newBuilder() .setZAddRequest(zAddReq) .setProveSinceTx(state.getTxId()) .build(); - - final ImmudbProto.VerifiableTx vtx = getStub().verifiableZAdd(vZAddReq); + + final ImmudbProto.VerifiableTx vtx = this.stub.verifiableZAdd(vZAddReq); if (vtx.getTx().getHeader().getNentries() != 1) { throw new VerificationException("Data is corrupted."); @@ -848,8 +865,7 @@ public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) th .setKey(Utils.toByteString(key)) .setAtTx(atTx) .setScore(score) - .build() - ); + .build()); InclusionProof inclusionProof = tx.proof(entry.getEncodedKey()); @@ -857,7 +873,8 @@ public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) th throw new VerificationException("Data is corrupted (inclusion verification failed)."); } - if (!Arrays.equals(txHeader.getEh(), CryptoUtils.digestFrom(vtx.getDualProof().getTargetTxHeader().getEH().toByteArray()))) { + if (!Arrays.equals(txHeader.getEh(), + CryptoUtils.digestFrom(vtx.getDualProof().getTargetTxHeader().getEH().toByteArray()))) { throw new VerificationException("Data is corrupted (different digests)."); } @@ -867,7 +884,7 @@ public TxHeader verifiedZAdd(byte[] set, byte[] key, long atTx, double score) th throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + this.stateHolder.setState(newState); return TxHeader.valueOf(vtx.getTx().getHeader()); } @@ -876,7 +893,7 @@ public List zScan(String set, long limit, boolean reverse) { return zScan(Utils.toByteArray(set), limit, reverse); } - public List zScan(byte[] set, long limit, boolean reverse) { + public synchronized List zScan(byte[] set, long limit, boolean reverse) { final ImmudbProto.ZScanRequest req = ImmudbProto.ZScanRequest .newBuilder() .setSet(Utils.toByteString(set)) @@ -884,7 +901,7 @@ public List zScan(byte[] set, long limit, boolean reverse) { .setDesc(reverse) .build(); - final ImmudbProto.ZEntries zEntries = getStub().zScan(req); + final ImmudbProto.ZEntries zEntries = this.stub.zScan(req); return buildList(zEntries); } @@ -893,9 +910,9 @@ public List zScan(byte[] set, long limit, boolean reverse) { // ========== TX ========== // - public Tx txById(long txId) throws TxNotFoundException, NoSuchAlgorithmException { + public synchronized Tx txById(long txId) throws TxNotFoundException, NoSuchAlgorithmException { try { - final ImmudbProto.Tx tx = getStub().txById(ImmudbProto.TxRequest.newBuilder().setTx(txId).build()); + final ImmudbProto.Tx tx = this.stub.txById(ImmudbProto.TxRequest.newBuilder().setTx(txId).build()); return Tx.valueOf(tx); } catch (StatusRuntimeException e) { if (e.getMessage().contains("tx not found")) { @@ -906,17 +923,18 @@ public Tx txById(long txId) throws TxNotFoundException, NoSuchAlgorithmException } } - public Tx verifiedTxById(long txId) throws TxNotFoundException, VerificationException { - final ImmuState state = state(); + public synchronized Tx verifiedTxById(long txId) throws TxNotFoundException, VerificationException { + final ImmuState state = this.state(); + final ImmudbProto.VerifiableTxRequest vTxReq = ImmudbProto.VerifiableTxRequest.newBuilder() .setTx(txId) .setProveSinceTx(state.getTxId()) .build(); - + final ImmudbProto.VerifiableTx vtx; try { - vtx = getStub().verifiableTxById(vTxReq); + vtx = this.stub.verifiableTxById(vTxReq); } catch (StatusRuntimeException e) { if (e.getMessage().contains("tx not found")) { throw new TxNotFoundException(); @@ -950,8 +968,7 @@ public Tx verifiedTxById(long txId) throws TxNotFoundException, VerificationExce sourceId, targetId, sourceAlh, - targetAlh - )) { + targetAlh)) { throw new VerificationException("Data is corrupted (dual proof verification failed)."); } } @@ -963,31 +980,32 @@ public Tx verifiedTxById(long txId) throws TxNotFoundException, VerificationExce throw new VerificationException("Failed to extract the transaction.", e); } - final ImmuState newState = new ImmuState(currentDb, targetId, targetAlh, vtx.getSignature().getSignature().toByteArray()); + final ImmuState newState = new ImmuState(state.getDatabase(), targetId, targetAlh, + vtx.getSignature().getSignature().toByteArray()); if (!newState.checkSignature(serverSigningKey)) { throw new VerificationException("State signature verification failed"); } - stateHolder.setState(currentServerUuid, newState); + stateHolder.setState(newState); return tx; } - public List txScan(long initialTxId) { + public synchronized List txScan(long initialTxId) { final ImmudbProto.TxScanRequest req = ImmudbProto.TxScanRequest.newBuilder().setInitialTx(initialTxId).build(); - final ImmudbProto.TxList txList = getStub().txScan(req); + final ImmudbProto.TxList txList = this.stub.txScan(req); return buildList(txList); } - public List txScan(long initialTxId, int limit, boolean desc) { + public synchronized List txScan(long initialTxId, int limit, boolean desc) { final ImmudbProto.TxScanRequest req = ImmudbProto.TxScanRequest .newBuilder() .setInitialTx(initialTxId) .setLimit(limit) .setDesc(desc) .build(); - final ImmudbProto.TxList txList = getStub().txScan(req); + final ImmudbProto.TxList txList = this.stub.txScan(req); return buildList(txList); } @@ -995,20 +1013,24 @@ public List txScan(long initialTxId, int limit, boolean desc) { // ========== HEALTH ========== // - public boolean healthCheck() { - return getStub().health(Empty.getDefaultInstance()).getStatus(); + public boolean isConnected() { + return channel != null && channel.getState(false) == ConnectivityState.READY; } - public boolean isConnected() { - return channel != null; + public boolean isShutdown() { + return channel != null && channel.isShutdown(); + } + + public synchronized boolean healthCheck() { + return this.stub.serverInfo(ImmudbProto.ServerInfoRequest.getDefaultInstance()) != null; } // // ========== USER MGMT ========== // - public List listUsers() { - final ImmudbProto.UserList userList = getStub().listUsers(Empty.getDefaultInstance()); + public synchronized List listUsers() { + final ImmudbProto.UserList userList = this.stub.listUsers(Empty.getDefaultInstance()); return userList.getUsersList() .stream() @@ -1018,8 +1040,7 @@ public List listUsers() { .setCreatedAt(u.getCreatedat()) .setCreatedBy(u.getCreatedby()) .setPermissions(buildPermissions(u.getPermissionsList())) - .build() - ) + .build()) .collect(Collectors.toList()); } @@ -1030,7 +1051,7 @@ private List buildPermissions(List permissio .collect(Collectors.toList()); } - public void createUser(String user, String password, Permission permission, String database) { + public synchronized void createUser(String user, String password, Permission permission, String database) { final ImmudbProto.CreateUserRequest createUserRequest = ImmudbProto.CreateUserRequest.newBuilder() .setUser(Utils.toByteString(user)) .setPassword(Utils.toByteString(password)) @@ -1039,10 +1060,10 @@ public void createUser(String user, String password, Permission permission, Stri .build(); // noinspection ResultOfMethodCallIgnored - getStub().createUser(createUserRequest); + this.stub.createUser(createUserRequest); } - public void changePassword(String user, String oldPassword, String newPassword) { + public synchronized void changePassword(String user, String oldPassword, String newPassword) { final ImmudbProto.ChangePasswordRequest changePasswordRequest = ImmudbProto.ChangePasswordRequest.newBuilder() .setUser(Utils.toByteString(user)) .setOldPassword(Utils.toByteString(oldPassword)) @@ -1050,24 +1071,24 @@ public void changePassword(String user, String oldPassword, String newPassword) .build(); // noinspection ResultOfMethodCallIgnored - getStub().changePassword(changePasswordRequest); + this.stub.changePassword(changePasswordRequest); } // // ========== INDEX MGMT ========== // - public void flushIndex(float cleanupPercentage, boolean synced) { + public synchronized void flushIndex(float cleanupPercentage, boolean synced) { ImmudbProto.FlushIndexRequest req = ImmudbProto.FlushIndexRequest.newBuilder() .setCleanupPercentage(cleanupPercentage) .setSynced(synced) .build(); - getStub().flushIndex(req); + this.stub.flushIndex(req); } - public void compactIndex() { - getStub().compactIndex(Empty.getDefaultInstance()); + public synchronized void compactIndex() { + this.stub.compactIndex(Empty.getDefaultInstance()); } // diff --git a/src/main/java/io/codenotary/immudb4j/ImmuServerUUIDInterceptor.java b/src/main/java/io/codenotary/immudb4j/ImmuServerUUIDInterceptor.java deleted file mode 100644 index f665143..0000000 --- a/src/main/java/io/codenotary/immudb4j/ImmuServerUUIDInterceptor.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.codenotary.immudb4j; - -import io.grpc.*; -import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener; - - -public class ImmuServerUUIDInterceptor implements ClientInterceptor { - - private static final String SERVER_UUID = "immudb-uuid"; - - private final ImmuClient client; - - public ImmuServerUUIDInterceptor(ImmuClient client) { - this.client = client; - } - - @Override - public ClientCall interceptCall( - MethodDescriptor method, - CallOptions callOptions, Channel next) { - - return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { - - @Override - public void start(Listener responseListener, Metadata headers) { - - SimpleForwardingClientCallListener listener = new SimpleForwardingClientCallListener(responseListener) { - - @Override - public void onHeaders(Metadata headers) { - String serverUuid = headers.get(Metadata.Key.of(SERVER_UUID, Metadata.ASCII_STRING_MARSHALLER)); - if (serverUuid != null && !serverUuid.equals(client.getCurrentServerUuid())) { - client.setCurrentServerUuid(serverUuid); - } - super.onHeaders(headers); - } - }; - super.start(listener, headers); - } - - }; - - } - -} diff --git a/src/main/java/io/codenotary/immudb4j/ImmudbAuthRequestInterceptor.java b/src/main/java/io/codenotary/immudb4j/ImmudbAuthRequestInterceptor.java new file mode 100644 index 0000000..8b4b430 --- /dev/null +++ b/src/main/java/io/codenotary/immudb4j/ImmudbAuthRequestInterceptor.java @@ -0,0 +1,37 @@ +package io.codenotary.immudb4j; + +import io.grpc.*; + + +public class ImmudbAuthRequestInterceptor implements ClientInterceptor { + + private static final String SESSION_ID = "sessionid"; + + private final ImmuClient client; + + public ImmudbAuthRequestInterceptor(ImmuClient client) { + this.client = client; + } + + @Override + public ClientCall interceptCall( + MethodDescriptor method, + CallOptions callOptions, Channel next) { + + return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { + + @Override + public void start(Listener responseListener, Metadata headers) { + final Session session = client.getSession(); + + if (session != null) { + headers.put(Metadata.Key.of(SESSION_ID, Metadata.ASCII_STRING_MARSHALLER), session.getSessionID()); + } + + super.start(responseListener, headers); + } + + }; + } + +} diff --git a/src/test/java/io/codenotary/immudb4j/UseDatabaseTest.java b/src/main/java/io/codenotary/immudb4j/Session.java similarity index 56% rename from src/test/java/io/codenotary/immudb4j/UseDatabaseTest.java rename to src/main/java/io/codenotary/immudb4j/Session.java index 37edd34..701147c 100644 --- a/src/test/java/io/codenotary/immudb4j/UseDatabaseTest.java +++ b/src/main/java/io/codenotary/immudb4j/Session.java @@ -15,25 +15,28 @@ */ package io.codenotary.immudb4j; -import org.testng.Assert; -import org.testng.annotations.Test; +public class Session { -import java.util.List; + private String username; + private String sessionID; + private String database; + public Session(String sessionID, String username, String database) { + this.sessionID = sessionID; + this.username = username; + this.database = database; + } -public class UseDatabaseTest extends ImmuClientIntegrationTest { - - @Test(testName = "useDatabase") - public void t1() { - - immuClient.login("immudb", "immudb"); - - immuClient.useDatabase("defaultdb"); - - List databases = immuClient.databases(); - Assert.assertTrue(databases.size() > 0); + public String getSessionID() { + return sessionID; + } + + public String getUsername() { + return username; + } - immuClient.logout(); + public String getDatabase() { + return database; } } diff --git a/src/main/proto/schema.proto b/src/main/proto/schema.proto index 92f9fd6..dfa61f9 100644 --- a/src/main/proto/schema.proto +++ b/src/main/proto/schema.proto @@ -18,446 +18,443 @@ import "google/protobuf/struct.proto"; package immudb.schema; -option go_package = "github.com/codenotary/immudb/pkg/api/schema"; option java_package = "io.codenotary.immudb"; option java_outer_classname = "ImmudbProto"; message Key { - bytes key = 1; + bytes key = 1; } message Permission { - string database = 1; - uint32 permission = 2; + string database = 1; + uint32 permission = 2; } message User { - bytes user = 1; - repeated Permission permissions = 3; - string createdby = 4; - string createdat = 5; - bool active = 6; + bytes user = 1; + repeated Permission permissions = 3; + string createdby = 4; + string createdat = 5; + bool active = 6; } message UserList { - repeated User users = 1; + repeated User users = 1; } message CreateUserRequest { - bytes user = 1; - bytes password = 2; - uint32 permission = 3; - string database = 4; + bytes user = 1; + bytes password = 2; + uint32 permission = 3; + string database = 4; } message UserRequest { - bytes user = 1; + bytes user = 1; } message ChangePasswordRequest { - bytes user = 1; - bytes oldPassword = 2; - bytes newPassword = 3; -} - -message LoginRequest { bytes user = 1; - bytes password = 2; -} - -message LoginResponse { - string token = 1; - bytes warning = 2; + bytes oldPassword = 2; + bytes newPassword = 3; } message OpenSessionRequest { - bytes username = 1; - bytes password = 2; - string databaseName = 3; + bytes username = 1; + bytes password = 2; + string databaseName = 3; } message OpenSessionResponse { - string sessionID = 1; - string serverUUID = 2; + string sessionID = 1; + string serverUUID = 2; } //////////////////////////////////////////////////////// message Precondition { - message KeyMustExistPrecondition { - bytes key = 1; - } + message KeyMustExistPrecondition { + bytes key = 1; + } - message KeyMustNotExistPrecondition { - bytes key = 1; - } + message KeyMustNotExistPrecondition { + bytes key = 1; + } - message KeyNotModifiedAfterTXPrecondition { - bytes key = 1; - uint64 txID = 2; - } + message KeyNotModifiedAfterTXPrecondition { + bytes key = 1; + uint64 txID = 2; + } - oneof precondition { - KeyMustExistPrecondition keyMustExist = 1; - KeyMustNotExistPrecondition keyMustNotExist = 2; - KeyNotModifiedAfterTXPrecondition keyNotModifiedAfterTX = 3; - } + oneof precondition { + KeyMustExistPrecondition keyMustExist = 1; + KeyMustNotExistPrecondition keyMustNotExist = 2; + KeyNotModifiedAfterTXPrecondition keyNotModifiedAfterTX = 3; + } } message KeyValue { - bytes key = 1; - bytes value = 2; - KVMetadata metadata = 3; + bytes key = 1; + bytes value = 2; + KVMetadata metadata = 3; } message Entry { - uint64 tx = 1; - bytes key = 2; - bytes value = 3; - Reference referencedBy = 4; - KVMetadata metadata = 5; - bool expired = 6; + uint64 tx = 1; + bytes key = 2; + bytes value = 3; + Reference referencedBy = 4; + KVMetadata metadata = 5; + bool expired = 6; + uint64 revision = 7; } message Reference { - uint64 tx = 1; - bytes key = 2; - uint64 atTx = 3; - KVMetadata metadata = 4; + uint64 tx = 1; + bytes key = 2; + uint64 atTx = 3; + KVMetadata metadata = 4; + uint64 revision = 5; } message Op { - oneof operation { - KeyValue kv = 1; - ZAddRequest zAdd = 2; - ReferenceRequest ref = 3; - } + oneof operation { + KeyValue kv = 1; + ZAddRequest zAdd = 2; + ReferenceRequest ref = 3; + } } message ExecAllRequest { - repeated Op Operations = 1; - bool noWait = 2; - repeated Precondition preconditions = 3; + repeated Op Operations = 1; + bool noWait = 2; + repeated Precondition preconditions = 3; } message Entries { - repeated Entry entries = 1; + repeated Entry entries = 1; } message ZEntry { - bytes set = 1; - bytes key = 2; - Entry entry = 3; - double score = 4; - uint64 atTx = 5; + bytes set = 1; + bytes key = 2; + Entry entry = 3; + double score = 4; + uint64 atTx = 5; } message ZEntries { - repeated ZEntry entries = 1; + repeated ZEntry entries = 1; } message ScanRequest { - bytes seekKey = 1; - bytes endKey = 7; - bytes prefix = 2; - bool desc = 3; - uint64 limit = 4; - uint64 sinceTx = 5; - bool noWait = 6; - bool inclusiveSeek = 8; - bool inclusiveEnd = 9; + bytes seekKey = 1; + bytes endKey = 7; + bytes prefix = 2; + bool desc = 3; + uint64 limit = 4; + uint64 sinceTx = 5; + bool noWait = 6; + bool inclusiveSeek = 8; // If set to true, results will include seekKey + bool inclusiveEnd = 9; // If set to true, results will include endKey if needed + uint64 offset = 10; // Specify the initial entry to be returned by excluding the initial set of entries } message KeyPrefix { - bytes prefix = 1; + bytes prefix = 1; } message EntryCount { - uint64 count = 1; + uint64 count = 1; } /////////////// message Signature { - bytes publicKey = 1; - bytes signature = 2; + bytes publicKey = 1; + bytes signature = 2; } message TxHeader { - uint64 id = 1; - bytes prevAlh = 2; - int64 ts = 3; - int32 nentries = 4; - bytes eH = 5; - uint64 blTxId = 6; - bytes blRoot = 7; - int32 version = 8; - TxMetadata metadata = 9; + uint64 id = 1; + bytes prevAlh = 2; + int64 ts = 3; + int32 nentries = 4; + bytes eH = 5; + uint64 blTxId = 6; + bytes blRoot = 7; + int32 version = 8; + TxMetadata metadata = 9; } message TxMetadata { } message LinearProof { - uint64 sourceTxId = 1; - uint64 TargetTxId = 2; - repeated bytes terms = 3; + uint64 sourceTxId = 1; + uint64 TargetTxId = 2; + repeated bytes terms = 3; } message DualProof { - TxHeader sourceTxHeader = 1; - TxHeader targetTxHeader = 2; + TxHeader sourceTxHeader = 1; + TxHeader targetTxHeader = 2; - repeated bytes inclusionProof = 3; - repeated bytes consistencyProof = 4; + repeated bytes inclusionProof = 3; + repeated bytes consistencyProof = 4; - bytes targetBlTxAlh = 5; - repeated bytes lastInclusionProof = 6; + bytes targetBlTxAlh = 5; + repeated bytes lastInclusionProof = 6; - LinearProof linearProof = 7; + LinearProof linearProof = 7; } message Tx { - TxHeader header = 1; - repeated TxEntry entries = 2; - repeated Entry kvEntries = 3; - repeated ZEntry zEntries = 4; + TxHeader header = 1; + repeated TxEntry entries = 2; + repeated Entry kvEntries = 3; + repeated ZEntry zEntries = 4; } message TxEntry { - bytes key = 1; - bytes hValue = 2; - int32 vLen = 3; - KVMetadata metadata = 4; - bytes value = 5; // value must be ignored when len(value) == 0 and vLen > 0. Otherwise, sha256(value) must be equal to hValue + bytes key = 1; + bytes hValue = 2; + int32 vLen = 3; + KVMetadata metadata = 4; + bytes value = 5; // value must be ignored when len(value) == 0 and vLen > 0. Otherwise, sha256(value) must be equal to hValue } message KVMetadata { - bool deleted = 1; - Expiration expiration = 2; - bool nonIndexable = 3; + bool deleted = 1; + Expiration expiration = 2; + bool nonIndexable = 3; } message Expiration { - int64 expiresAt = 1; + int64 expiresAt = 1; } message VerifiableTx { - Tx tx = 1; - DualProof dualProof = 2; - Signature signature = 3; + Tx tx = 1; + DualProof dualProof = 2; + Signature signature = 3; } ////////////////// message VerifiableEntry { - Entry entry = 1; - VerifiableTx verifiableTx = 2; - InclusionProof inclusionProof = 3; + Entry entry = 1; + VerifiableTx verifiableTx = 2; + InclusionProof inclusionProof = 3; } message InclusionProof { - int32 leaf = 1; - int32 width = 2; - repeated bytes terms = 3; + int32 leaf = 1; + int32 width = 2; + repeated bytes terms = 3; } message SetRequest { - repeated KeyValue KVs = 1; - bool noWait = 2; - repeated Precondition preconditions = 3; + repeated KeyValue KVs = 1; + bool noWait = 2; + repeated Precondition preconditions = 3; } message KeyRequest { - bytes key = 1; - uint64 atTx = 2; - uint64 sinceTx = 3; - bool noWait = 4; - int64 atRevision = 5; + bytes key = 1; + uint64 atTx = 2; // if > 0, query for the value exactly at given transaction + + + // if 0 (and nowait=false), wait for the index to be up=to-date + uint64 sinceTx = 3; + + // if set to true - do not wait for any indexing update considering only the currently indexed state + bool noWait = 4; + + // if > 0, get the nth version of the value, 1 being the first version, 2 being the second and so on + // if < 0, get the historical nth value of the key, -1 being the previous version, -2 being the one before and so on + int64 atRevision = 5; + } message KeyListRequest { - repeated bytes keys = 1; - uint64 sinceTx = 2; + repeated bytes keys = 1; + uint64 sinceTx = 2; } message DeleteKeysRequest { - repeated bytes keys = 1; - uint64 sinceTx = 2; - bool noWait = 3; + repeated bytes keys = 1; + uint64 sinceTx = 2; + bool noWait = 3; } message VerifiableSetRequest { - SetRequest setRequest = 1; - uint64 proveSinceTx = 2; + SetRequest setRequest = 1; + uint64 proveSinceTx = 2; } message VerifiableGetRequest { - KeyRequest keyRequest = 1; - uint64 proveSinceTx = 2; + KeyRequest keyRequest = 1; + uint64 proveSinceTx = 2; +} + +// ServerInfoRequest exists to provide extensibility for rpc ServerInfo. +message ServerInfoRequest {} + +// ServerInfoResponse contains information about the server instance. +message ServerInfoResponse { + // The version of the server instance. + string version = 1; } message HealthResponse { - bool status = 1; - string version = 2; + bool status = 1; + string version = 2; } message DatabaseHealthResponse { - uint32 pendingRequests = 1; - int64 lastRequestCompletedAt = 2; + uint32 pendingRequests = 1; + int64 lastRequestCompletedAt = 2; } message ImmutableState { - string db = 1; - uint64 txId = 2; - bytes txHash = 3; - Signature signature = 4; + string db = 1; + uint64 txId = 2; + bytes txHash = 3; + Signature signature = 4; } message ReferenceRequest { - bytes key = 1; - bytes referencedKey = 2; - uint64 atTx = 3; - bool boundRef = 4; - bool noWait = 5; - repeated Precondition preconditions = 6; + bytes key = 1; + bytes referencedKey = 2; + uint64 atTx = 3; + bool boundRef = 4; + bool noWait = 5; + repeated Precondition preconditions = 6; } message VerifiableReferenceRequest { - ReferenceRequest referenceRequest = 1; - uint64 proveSinceTx = 2; + ReferenceRequest referenceRequest = 1; + uint64 proveSinceTx = 2; } message ZAddRequest { - bytes set = 1; - double score = 2; - bytes key = 3; - uint64 atTx = 4; - bool boundRef = 5; - bool noWait = 6; + bytes set = 1; + double score = 2; + bytes key = 3; + uint64 atTx = 4; + bool boundRef = 5; + bool noWait = 6; } message Score { - double score = 1; + double score = 1; } message ZScanRequest { - bytes set = 1; - bytes seekKey = 2; - double seekScore = 3; - uint64 seekAtTx = 4; - bool inclusiveSeek = 5; - uint64 limit = 6; - bool desc = 7; - Score minScore = 8; - Score maxScore = 9; - uint64 sinceTx = 10; - bool noWait = 11; + bytes set = 1; + bytes seekKey = 2; + double seekScore = 3; + uint64 seekAtTx = 4; + bool inclusiveSeek = 5; + uint64 limit = 6; + bool desc = 7; + Score minScore = 8; + Score maxScore = 9; + uint64 sinceTx = 10; + bool noWait = 11; + uint64 offset = 12; // Specify the initial entry to be returned by excluding the initial set of entries } message HistoryRequest { - bytes key = 1; - uint64 offset = 2; - int32 limit = 3; - bool desc = 4; - uint64 sinceTx = 5; + bytes key = 1; + uint64 offset = 2; // Specify the initial entry to be returned by excluding the initial set of entries + int32 limit = 3; + bool desc = 4; + uint64 sinceTx = 5; } message VerifiableZAddRequest { - ZAddRequest zAddRequest = 1; - uint64 proveSinceTx = 2; + ZAddRequest zAddRequest = 1; + uint64 proveSinceTx = 2; } message TxRequest { - uint64 tx = 1; - EntriesSpec entriesSpec = 2; - uint64 sinceTx = 3; - bool noWait = 4; + uint64 tx = 1; + EntriesSpec entriesSpec = 2; + uint64 sinceTx = 3; + bool noWait = 4; + bool keepReferencesUnresolved = 5; } message EntriesSpec { - EntryTypeSpec kvEntriesSpec = 1; - EntryTypeSpec zEntriesSpec = 2; - EntryTypeSpec sqlEntriesSpec = 3; + EntryTypeSpec kvEntriesSpec = 1; + EntryTypeSpec zEntriesSpec = 2; + EntryTypeSpec sqlEntriesSpec = 3; } message EntryTypeSpec { - EntryTypeAction action = 1; + EntryTypeAction action = 1; } enum EntryTypeAction { - EXCLUDE = 0; - ONLY_DIGEST = 1; - RAW_VALUE = 2; - RESOLVE = 3; + EXCLUDE = 0; + ONLY_DIGEST = 1; + RAW_VALUE = 2; + RESOLVE = 3; } message VerifiableTxRequest { - uint64 tx = 1; - uint64 proveSinceTx = 2; - EntriesSpec entriesSpec = 3; - uint64 sinceTx = 4; - bool noWait = 5; + uint64 tx = 1; + uint64 proveSinceTx = 2; + EntriesSpec entriesSpec = 3; + uint64 sinceTx = 4; + bool noWait = 5; + bool keepReferencesUnresolved = 6; } message TxScanRequest { - uint64 initialTx = 1; - uint32 limit = 2; - bool desc = 3; - EntriesSpec entriesSpec = 4; - uint64 sinceTx = 5; - bool noWait = 6; + uint64 initialTx = 1; + uint32 limit = 2; + bool desc = 3; + EntriesSpec entriesSpec = 4; + uint64 sinceTx = 5; + bool noWait = 6; } message TxList { - repeated Tx txs = 1; + repeated Tx txs = 1; } message ExportTxRequest { - uint64 tx = 1; + uint64 tx = 1; } message Database { - string databaseName = 1; -} - -message DatabaseSettings { - string databaseName = 1; - - bool replica = 2; - string masterDatabase = 3; - string masterAddress = 4; - uint32 masterPort = 5; - string followerUsername = 6; - string followerPassword = 7; - - uint32 fileSize = 8; - uint32 maxKeyLen = 9; - uint32 maxValueLen = 10; - uint32 maxTxEntries = 11; - - bool excludeCommitTime = 12; + string databaseName = 1; } message CreateDatabaseRequest { - string name = 1; - DatabaseNullableSettings settings = 2; + string name = 1; + DatabaseNullableSettings settings = 2; + bool ifNotExists = 3; } message CreateDatabaseResponse { - string name = 1; - DatabaseNullableSettings settings = 2; + string name = 1; + DatabaseNullableSettings settings = 2; + bool alreadyExisted = 3; } message UpdateDatabaseRequest { - string database = 1; - DatabaseNullableSettings settings = 2; + string database = 1; + DatabaseNullableSettings settings = 2; } message UpdateDatabaseResponse { // Reserved to reply with more advanced response later - string database = 1; - DatabaseNullableSettings settings = 2; + string database = 1; + DatabaseNullableSettings settings = 2; } message DatabaseSettingsRequest { @@ -465,279 +462,292 @@ message DatabaseSettingsRequest { } message DatabaseSettingsResponse { - string database = 1; - DatabaseNullableSettings settings = 2; + string database = 1; + DatabaseNullableSettings settings = 2; } message NullableUint32 { - uint32 value = 1; + uint32 value = 1; } message NullableUint64 { - uint64 value = 1; + uint64 value = 1; } message NullableFloat { - float value = 1; + float value = 1; } message NullableBool { - bool value = 1; + bool value = 1; } message NullableString { - string value = 1; + string value = 1; +} + +message NullableMilliseconds { + int64 value = 1; } message DatabaseNullableSettings { - ReplicationNullableSettings replicationSettings = 2; + ReplicationNullableSettings replicationSettings = 2; - NullableUint32 fileSize = 8; - NullableUint32 maxKeyLen = 9; - NullableUint32 maxValueLen = 10; - NullableUint32 maxTxEntries = 11; + NullableUint32 fileSize = 8; + NullableUint32 maxKeyLen = 9; + NullableUint32 maxValueLen = 10; + NullableUint32 maxTxEntries = 11; - NullableBool excludeCommitTime = 12; + NullableBool excludeCommitTime = 12; - NullableUint32 maxConcurrency = 13; - NullableUint32 maxIOConcurrency = 14; + NullableUint32 maxConcurrency = 13; + NullableUint32 maxIOConcurrency = 14; - NullableUint32 txLogCacheSize = 15; - NullableUint32 vLogMaxOpenedFiles = 16; - NullableUint32 txLogMaxOpenedFiles = 17; - NullableUint32 commitLogMaxOpenedFiles = 18; + NullableUint32 txLogCacheSize = 15; + NullableUint32 vLogMaxOpenedFiles = 16; + NullableUint32 txLogMaxOpenedFiles = 17; + NullableUint32 commitLogMaxOpenedFiles = 18; - IndexNullableSettings indexSettings = 19; + IndexNullableSettings indexSettings = 19; - NullableUint32 writeTxHeaderVersion = 20; + NullableUint32 writeTxHeaderVersion = 20; - NullableBool autoload = 21; + NullableBool autoload = 21; + + NullableUint32 readTxPoolSize = 22; + + NullableMilliseconds syncFrequency = 23; + + NullableUint32 writeBufferSize = 24; + + AHTNullableSettings ahtSettings = 25; } message ReplicationNullableSettings { - NullableBool replica = 1; - NullableString masterDatabase = 2; - NullableString masterAddress = 3; - NullableUint32 masterPort = 4; - NullableString followerUsername = 5; - NullableString followerPassword = 6; + NullableBool replica = 1; + NullableString masterDatabase = 2; + NullableString masterAddress = 3; + NullableUint32 masterPort = 4; + NullableString followerUsername = 5; + NullableString followerPassword = 6; } message IndexNullableSettings { - NullableUint32 flushThreshold = 1; - NullableUint32 syncThreshold = 2; - NullableUint32 cacheSize = 3; - NullableUint32 maxNodeSize = 4; - NullableUint32 maxActiveSnapshots = 5; - NullableUint64 renewSnapRootAfter = 6; - NullableUint32 compactionThld = 7; - NullableUint32 delayDuringCompaction = 8; - NullableUint32 nodesLogMaxOpenedFiles = 9; - NullableUint32 historyLogMaxOpenedFiles = 10; - NullableUint32 commitLogMaxOpenedFiles = 11; - NullableUint32 flushBufferSize = 12; - NullableFloat cleanupPercentage = 13; + NullableUint32 flushThreshold = 1; + NullableUint32 syncThreshold = 2; + NullableUint32 cacheSize = 3; + NullableUint32 maxNodeSize = 4; + NullableUint32 maxActiveSnapshots = 5; + NullableUint64 renewSnapRootAfter = 6; + NullableUint32 compactionThld = 7; + NullableUint32 delayDuringCompaction = 8; + NullableUint32 nodesLogMaxOpenedFiles = 9; + NullableUint32 historyLogMaxOpenedFiles = 10; + NullableUint32 commitLogMaxOpenedFiles = 11; + NullableUint32 flushBufferSize = 12; + NullableFloat cleanupPercentage = 13; +} + +message AHTNullableSettings { + NullableUint32 syncThreshold = 1; + NullableUint32 writeBufferSize = 2; } message LoadDatabaseRequest { - string database = 1; - // may add createIfNotExist + string database = 1; + // may add createIfNotExist } message LoadDatabaseResponse { - string database = 1; - // may add setttings + string database = 1; + // may add setttings } message UnloadDatabaseRequest { - string database = 1; + string database = 1; } message UnloadDatabaseResponse { - string database = 1; + string database = 1; } message DeleteDatabaseRequest { - string database = 1; + string database = 1; } message DeleteDatabaseResponse { - string database = 1; + string database = 1; } message FlushIndexRequest { - float cleanupPercentage = 1; - bool synced = 2; + float cleanupPercentage = 1; + bool synced = 2; } message FlushIndexResponse { - string database = 1; + string database = 1; } message Table { - string tableName = 1; + string tableName = 1; } message SQLGetRequest { - string table = 1; - repeated SQLValue pkValues = 2; - uint64 atTx = 3; - uint64 sinceTx = 4; + string table = 1; + repeated SQLValue pkValues = 2; + uint64 atTx = 3; + uint64 sinceTx = 4; } message VerifiableSQLGetRequest { - SQLGetRequest sqlGetRequest = 1; - uint64 proveSinceTx = 2; + SQLGetRequest sqlGetRequest = 1; + uint64 proveSinceTx = 2; } message SQLEntry { - uint64 tx = 1; - bytes key = 2; - bytes value = 3; - KVMetadata metadata = 4; + uint64 tx = 1; + bytes key = 2; + bytes value = 3; + KVMetadata metadata = 4; } message VerifiableSQLEntry { - reserved 6; - SQLEntry sqlEntry = 1; - VerifiableTx verifiableTx = 2; - InclusionProof inclusionProof = 3; - uint32 DatabaseId = 4; - uint32 TableId = 5; - repeated uint32 PKIDs = 16; - map ColNamesById = 8; - map ColIdsByName = 9; - map ColTypesById = 10; - map ColLenById = 11; + reserved 6; + SQLEntry sqlEntry = 1; + VerifiableTx verifiableTx = 2; + InclusionProof inclusionProof = 3; + uint32 DatabaseId = 4; + uint32 TableId = 5; + repeated uint32 PKIDs = 16; + map ColNamesById = 8; + map ColIdsByName = 9; + map ColTypesById = 10; + map ColLenById = 11; } message UseDatabaseReply{ - string token = 1; + string token = 1; } enum PermissionAction { - GRANT = 0; - REVOKE = 1; + GRANT = 0; + REVOKE = 1; } message ChangePermissionRequest { - PermissionAction action = 1; - string username = 2; - string database = 3; - uint32 permission = 4; + PermissionAction action = 1; + string username = 2; + string database = 3; + uint32 permission = 4; } message SetActiveUserRequest { - bool active = 1; - string username = 2; -} - -message DatabaseListResponse { - repeated Database databases = 1; + bool active = 1; + string username = 2; } message DatabaseListRequestV2 { } message DatabaseListResponseV2 { - repeated DatabaseWithSettings databases = 1; + repeated DatabaseWithSettings databases = 1; } message DatabaseWithSettings { - string name = 1; - DatabaseNullableSettings settings = 2; - bool loaded = 3; + string name = 1; + DatabaseNullableSettings settings = 2; + bool loaded = 3; } message Chunk { - bytes content = 1; + bytes content = 1; } message UseSnapshotRequest { - uint64 sinceTx = 1; - uint64 asBeforeTx = 2; + uint64 sinceTx = 1; + uint64 asBeforeTx = 2; } message SQLExecRequest { - string sql = 1; - repeated NamedParam params = 2; - bool noWait = 3; + string sql = 1; + repeated NamedParam params = 2; + bool noWait = 3; } message SQLQueryRequest { - string sql = 1; - repeated NamedParam params = 2; - bool reuseSnapshot = 3; + string sql = 1; + repeated NamedParam params = 2; + bool reuseSnapshot = 3; } message NamedParam { - string name = 1; - SQLValue value = 2; + string name = 1; + SQLValue value = 2; } message SQLExecResult { - repeated CommittedSQLTx txs = 5; - bool ongoingTx = 6; + repeated CommittedSQLTx txs = 5; + bool ongoingTx = 6; } message CommittedSQLTx { - TxHeader header = 1; - uint32 updatedRows = 2; - map lastInsertedPKs = 3; - map firstInsertedPKs = 4; + TxHeader header = 1; + uint32 updatedRows = 2; + map lastInsertedPKs = 3; + map firstInsertedPKs = 4; } message SQLQueryResult { - repeated Column columns = 2; - repeated Row rows = 1; + repeated Column columns = 2; + repeated Row rows = 1; } message Column { - string name = 1; - string type = 2; + string name = 1; + string type = 2; } message Row { - repeated string columns = 1; - repeated SQLValue values = 2; + repeated string columns = 1; + repeated SQLValue values = 2; } message SQLValue { - oneof value { - google.protobuf.NullValue null = 1; - int64 n = 2; - string s = 3; - bool b = 4; - bytes bs = 5; - int64 ts = 6; - } + oneof value { + google.protobuf.NullValue null = 1; + int64 n = 2; + string s = 3; + bool b = 4; + bytes bs = 5; + int64 ts = 6; + } } enum TxMode { - ReadOnly = 0; - WriteOnly = 1; - ReadWrite = 2; + ReadOnly = 0; + WriteOnly = 1; + ReadWrite = 2; } message NewTxRequest { - TxMode mode = 1; + TxMode mode = 1; } message NewTxResponse { - string transactionID = 1; + string transactionID = 1; } message ErrorInfo { - string code = 1; - string cause = 2; + string code = 1; + string cause = 2; } message DebugInfo { - string stack = 1; + string stack = 1; } message RetryInfo { - int32 retry_delay = 1; + int32 retry_delay = 1; } // immudb gRPC & REST service @@ -768,14 +778,6 @@ service ImmuService { rpc TxSQLExec(SQLExecRequest) returns (google.protobuf.Empty) {}; rpc TxSQLQuery(SQLQueryRequest) returns (SQLQueryResult) {}; - rpc Login (LoginRequest) returns (LoginResponse){ - option deprecated = true; - }; - - rpc Logout (google.protobuf.Empty) returns (google.protobuf.Empty){ - option deprecated = true; - }; - rpc Set (SetRequest) returns (TxHeader){ }; @@ -800,14 +802,6 @@ service ImmuService { rpc Scan(ScanRequest) returns (Entries){ }; - // NOT YET SUPPORTED - rpc Count(KeyPrefix) returns (EntryCount){ - }; - - // NOT YET SUPPORTED - rpc CountAll(google.protobuf.Empty) returns (EntryCount){ - }; - rpc TxById(TxRequest) returns (Tx){ }; @@ -820,11 +814,13 @@ service ImmuService { rpc History(HistoryRequest) returns (Entries){ }; - rpc Health (google.protobuf.Empty) returns (HealthResponse){ - }; + // ServerInfo returns information about the server instance. + // ServerInfoRequest is defined for future extensions. + rpc ServerInfo (ServerInfoRequest) returns (ServerInfoResponse){ + }; rpc DatabaseHealth (google.protobuf.Empty) returns (DatabaseHealthResponse){ - }; + }; rpc CurrentState (google.protobuf.Empty) returns (ImmutableState){ }; diff --git a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java index e3f214d..eaca0a5 100644 --- a/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java +++ b/src/test/java/io/codenotary/immudb4j/BasicImmuClientTest.java @@ -24,16 +24,14 @@ import java.util.ArrayList; import java.util.List; - public class BasicImmuClientTest extends ImmuClientIntegrationTest { @Test(testName = "set, get") public void t1() throws VerificationException, CorruptedDataException { - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); - byte[] v0 = new byte[]{0, 1, 2, 3}; - byte[] v1 = new byte[]{3, 2, 1, 0}; + byte[] v0 = new byte[] { 0, 1, 2, 3 }; + byte[] v1 = new byte[] { 3, 2, 1, 0 }; TxHeader hdr0 = immuClient.set("k0", v0); Assert.assertNotNull(hdr0); @@ -53,7 +51,7 @@ public void t1() throws VerificationException, CorruptedDataException { Entry ventry1 = immuClient.verifiedGet("k1"); Assert.assertEquals(ventry1.getValue(), v1); - byte[] v2 = new byte[]{0, 1, 2, 3}; + byte[] v2 = new byte[] { 0, 1, 2, 3 }; TxHeader hdr2 = immuClient.verifiedSet("k2", v2); Assert.assertNotNull(hdr2); @@ -65,21 +63,20 @@ public void t1() throws VerificationException, CorruptedDataException { Assert.assertNotNull(e); Assert.assertEquals(e.getValue(), v2); - immuClient.logout(); + immuClient.closeSession(); } @Test(testName = "setAll, getAll") public void t2() { - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); List keys = new ArrayList<>(); keys.add("k0"); keys.add("k1"); List values = new ArrayList<>(); - values.add(new byte[]{0, 1, 0, 1}); - values.add(new byte[]{1, 0, 1, 0}); + values.add(new byte[] { 0, 1, 0, 1 }); + values.add(new byte[] { 1, 0, 1, 0 }); KVListBuilder kvListBuilder = KVListBuilder.newBuilder(); @@ -109,7 +106,7 @@ public void t2() { Assert.assertEquals(entry.getValue(), values.get(i)); } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/HTreeTest.java b/src/test/java/io/codenotary/immudb4j/HTreeTest.java index f68cf4c..9c74e5c 100644 --- a/src/test/java/io/codenotary/immudb4j/HTreeTest.java +++ b/src/test/java/io/codenotary/immudb4j/HTreeTest.java @@ -17,7 +17,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.security.NoSuchAlgorithmException; import org.testng.Assert; import org.testng.annotations.Test; @@ -29,13 +28,11 @@ public class HTreeTest { @Test(testName = "Empty HTree", expectedExceptions = IllegalArgumentException.class) public void t1() { - new HTree(0); } @Test(testName = "HTree init & root", expectedExceptions = IllegalStateException.class) public void t2() { - final int maxWidth = 1000; HTree tree = new HTree(maxWidth); @@ -46,7 +43,6 @@ public void t2() { @Test(testName = "HTree buildWith, root, inclusionProof, verifyInclusion") public void t3() { - final int maxWidth = 1000; HTree tree = new HTree(maxWidth); diff --git a/src/test/java/io/codenotary/immudb4j/HistoryTest.java b/src/test/java/io/codenotary/immudb4j/HistoryTest.java index 4618ca7..f0b0cce 100644 --- a/src/test/java/io/codenotary/immudb4j/HistoryTest.java +++ b/src/test/java/io/codenotary/immudb4j/HistoryTest.java @@ -17,7 +17,6 @@ import io.codenotary.immudb4j.exceptions.CorruptedDataException; import io.codenotary.immudb4j.exceptions.KeyNotFoundException; -import io.grpc.StatusRuntimeException; import org.testng.Assert; import org.testng.annotations.Test; @@ -28,9 +27,7 @@ public class HistoryTest extends ImmuClientIntegrationTest { @Test(testName = "set, history", priority = 2) public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); byte[] value1 = {0, 1, 2, 3}; byte[] value2 = {4, 5, 6, 7}; @@ -80,7 +77,7 @@ public void t1() { // exception is expected here } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java b/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java index de15f97..fc19652 100644 --- a/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java +++ b/src/test/java/io/codenotary/immudb4j/ListDatabasesTest.java @@ -23,8 +23,7 @@ public class ListDatabasesTest extends ImmuClientIntegrationTest { @Test(testName = "databases") public void t1() { - - immuClient.login("immudb", "immudb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); List databases = immuClient.databases(); if (databases.size() > 0) { @@ -36,7 +35,7 @@ public void t1() { System.out.print(">>> There are no databases."); } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ListUsersTest.java b/src/test/java/io/codenotary/immudb4j/ListUsersTest.java index 5ee4e82..6e8f762 100644 --- a/src/test/java/io/codenotary/immudb4j/ListUsersTest.java +++ b/src/test/java/io/codenotary/immudb4j/ListUsersTest.java @@ -21,12 +21,11 @@ public class ListUsersTest extends ImmuClientIntegrationTest { @Test(testName = "listUsers") public void t1() { - - immuClient.login("immudb", "immudb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); immuClient.listUsers().forEach(user -> System.out.printf(">>> Got user %s", user)); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java b/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java index fa049c9..4b46e30 100644 --- a/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java +++ b/src/test/java/io/codenotary/immudb4j/LoginAndHealthCheckAndCleanIndexTest.java @@ -21,23 +21,21 @@ public class LoginAndHealthCheckAndCleanIndexTest extends ImmuClientIntegrationTest { - @Test(testName = "login (with default credentials), healthCheck, logout") + @Test(testName = "openSession (with default credentials), healthCheck, logout") public void t1() { - - immuClient.login("immudb", "immudb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); boolean isHealthy = immuClient.healthCheck(); Assert.assertTrue(isHealthy); - immuClient.compactIndex(); + immuClient.flushIndex(10.0f, true); - immuClient.logout(); + immuClient.closeSession(); } - @Test(testName = "login (with wrong credentials)", expectedExceptions = StatusRuntimeException.class) + @Test(testName = "openSession (with wrong credentials)", expectedExceptions = StatusRuntimeException.class) public void t2() { - - immuClient.login("immudb", "incorrect_password"); + immuClient.openSession("immudb", "incorrect_password", "defaultdb"); } } diff --git a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java index 4d0de41..777e19e 100644 --- a/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultidatabaseTest.java @@ -26,13 +26,15 @@ public class MultidatabaseTest extends ImmuClientIntegrationTest { @Test(testName = "Interacting with multiple databases (creating them, setting, and getting, listing)") public void t1() throws VerificationException { - - immuClient.login("immudb", "immudb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); immuClient.createDatabase("db1"); immuClient.createDatabase("db2"); - immuClient.useDatabase("db1"); + immuClient.closeSession(); + + immuClient.openSession("immudb", "immudb", "db1"); + byte[] v0 = new byte[]{0, 1, 2, 3}; try { immuClient.set("k0", v0); @@ -40,7 +42,9 @@ public void t1() throws VerificationException { Assert.fail("Failed at set.", e); } - immuClient.useDatabase("db2"); + immuClient.closeSession(); + + immuClient.openSession("immudb", "immudb", "db2"); byte[] v1 = new byte[]{3, 2, 1, 0}; try { @@ -49,7 +53,9 @@ public void t1() throws VerificationException { Assert.fail("Failed at set.", e); } - immuClient.useDatabase("db1"); + immuClient.closeSession(); + + immuClient.openSession("immudb", "immudb", "db1"); Entry entry1 = immuClient.get("k0"); Assert.assertNotNull(entry1); @@ -59,7 +65,9 @@ public void t1() throws VerificationException { Assert.assertNotNull(ventry1); Assert.assertEquals(ventry1.getValue(), v0); - immuClient.useDatabase("db2"); + immuClient.closeSession(); + + immuClient.openSession("immudb", "immudb", "db2"); Entry entry2 = immuClient.get("k1"); Assert.assertEquals(entry2.getValue(), v1); @@ -74,7 +82,7 @@ public void t1() throws VerificationException { Assert.assertTrue(dbs.contains("db1")); Assert.assertTrue(dbs.contains("db2")); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/MultithreadTest.java b/src/test/java/io/codenotary/immudb4j/MultithreadTest.java index 07d3f07..95fbe19 100644 --- a/src/test/java/io/codenotary/immudb4j/MultithreadTest.java +++ b/src/test/java/io/codenotary/immudb4j/MultithreadTest.java @@ -29,9 +29,7 @@ public class MultithreadTest extends ImmuClientIntegrationTest { @Test(testName = "Multithread without key overlap") public void t1() throws InterruptedException, VerificationException { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); final int threadCount = 10; final int keyCount = 100; @@ -71,13 +69,13 @@ public void t1() throws InterruptedException, VerificationException { immuClient.verifiedGet("t" + i + "k" + i); } } + + immuClient.closeSession(); } @Test(testName = "Multithread with key overlap") public void t2() throws InterruptedException, VerificationException { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); final int threadCount = 10; final int keyCount = 100; @@ -118,6 +116,8 @@ public void t2() throws InterruptedException, VerificationException { immuClient.verifiedGet("k" + i); } } + + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ReferenceTest.java b/src/test/java/io/codenotary/immudb4j/ReferenceTest.java index 99c4e84..7bc4c9c 100644 --- a/src/test/java/io/codenotary/immudb4j/ReferenceTest.java +++ b/src/test/java/io/codenotary/immudb4j/ReferenceTest.java @@ -25,8 +25,7 @@ public class ReferenceTest extends ImmuClientIntegrationTest { @Test(testName = "set, setReference, setReferenceAt") public void t1() { - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); byte[] key = "testRef".getBytes(StandardCharsets.UTF_8); byte[] val = "abc".getBytes(StandardCharsets.UTF_8); @@ -57,7 +56,7 @@ public void t1() { } Assert.assertNotNull(ref2TxHdr); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ScanTest.java b/src/test/java/io/codenotary/immudb4j/ScanTest.java index eda915d..3a66d8c 100644 --- a/src/test/java/io/codenotary/immudb4j/ScanTest.java +++ b/src/test/java/io/codenotary/immudb4j/ScanTest.java @@ -26,9 +26,7 @@ public class ScanTest extends ImmuClientIntegrationTest { @Test(testName = "scan", priority = 2) public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); byte[] value1 = {0, 1, 2, 3}; byte[] value2 = {4, 5, 6, 7}; @@ -53,14 +51,12 @@ public void t1() { Assert.assertEquals(immuClient.scan("scan", "scan1", 1, false).size(), 1); - immuClient.logout(); + immuClient.closeSession(); } @Test(testName = "set, zAdd, zScan", priority = 3) public void t2() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); byte[] value1 = {0, 1, 2, 3}; byte[] value2 = {4, 5, 6, 7}; @@ -91,7 +87,7 @@ public void t2() { List zScan2 = immuClient.zScan("set2", 5, false); Assert.assertEquals(zScan2.size(), 2); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java b/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java index e35a92c..71d91aa 100644 --- a/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java +++ b/src/test/java/io/codenotary/immudb4j/SetAllAndGetAllTest.java @@ -26,9 +26,7 @@ public class SetAllAndGetAllTest extends ImmuClientIntegrationTest { @Test(testName = "setAll & getAll") public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); String key1 = "sga-key1"; byte[] val1 = new byte[] { 1 }; @@ -59,7 +57,7 @@ public void t1() { Assert.assertEquals(got.get(i).getValue(), kvs.get(i).getValue(), String.format("Expected: %s got: %s", kvs.get(i), got.get(i))); } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java index 2e15ec3..6f39d81 100644 --- a/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/SetAndGetTest.java @@ -26,9 +26,7 @@ public class SetAndGetTest extends ImmuClientIntegrationTest { @Test(testName = "set, get") public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); String key = "key1"; byte[] val = new byte[]{1, 2, 3, 4, 5}; @@ -69,7 +67,7 @@ public void t1() { Assert.fail("key not found exception expected"); } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/ShutdownTest.java b/src/test/java/io/codenotary/immudb4j/ShutdownTest.java index 542ec99..6df992c 100644 --- a/src/test/java/io/codenotary/immudb4j/ShutdownTest.java +++ b/src/test/java/io/codenotary/immudb4j/ShutdownTest.java @@ -27,13 +27,8 @@ public void t1() { Assert.assertFalse(immuClient.isShutdown()); immuClient.shutdown(); - - Assert.assertTrue(immuClient.isShutdown()); - - Assert.assertFalse(immuClient.isConnected()); - - immuClient.login("immudb", "immudb"); - + + immuClient.openSession("immudb", "immudb", "defaultdb"); } } diff --git a/src/test/java/io/codenotary/immudb4j/StateTest.java b/src/test/java/io/codenotary/immudb4j/StateTest.java index 1a03876..2e5ab96 100644 --- a/src/test/java/io/codenotary/immudb4j/StateTest.java +++ b/src/test/java/io/codenotary/immudb4j/StateTest.java @@ -30,9 +30,7 @@ public class StateTest extends ImmuClientIntegrationTest { @Test(testName = "currentState") public void t2() throws VerificationException { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); ImmuState currState = immuClient.currentState(); @@ -50,7 +48,7 @@ public void t2() throws VerificationException { publicKey = CryptoUtils.getDERPublicKey(publicKeyFile.getAbsolutePath()); } catch (Exception e) { // Not a test itself fault, but we cannot continue it. - immuClient.logout(); + immuClient.closeSession(); return; } @@ -61,12 +59,11 @@ public void t2() throws VerificationException { ImmuState someState = new ImmuState(currState.getDatabase(), currState.getTxId(), currState.getTxHash(), new byte[1]); Assert.assertFalse(someState.checkSignature(publicKey)); - immuClient.logout(); + immuClient.closeSession(); } @Test(testName = "currentState with server signature checking, but only on the client side") public void t3() { - // Provisioning the client side with the public key file. ClassLoader classLoader = getClass().getClassLoader(); File publicKeyFile = new File(Objects.requireNonNull(classLoader.getResource(publicKeyResource)).getFile()); @@ -85,8 +82,7 @@ public void t3() { return; } - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); try { immuClient.currentState(); @@ -96,7 +92,7 @@ public void t3() { // (this feature is active when starting it like: `immudb --signingKey test_private_key.pem`). } - immuClient.logout(); + immuClient.closeSession(); } @@ -128,8 +124,7 @@ public void t4() { return; } - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); try { ImmuState state = immuClient.currentState(); @@ -140,7 +135,7 @@ public void t4() { Assert.fail(e.getMessage(), e.getCause()); } - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/TxTest.java b/src/test/java/io/codenotary/immudb4j/TxTest.java index a58cc7a..72b2493 100644 --- a/src/test/java/io/codenotary/immudb4j/TxTest.java +++ b/src/test/java/io/codenotary/immudb4j/TxTest.java @@ -28,9 +28,7 @@ public class TxTest extends ImmuClientIntegrationTest { @Test(testName = "verifiedSet, txById, verifiedTxById") public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); String key = "test-txid"; byte[] val = "test-txid-value".getBytes(StandardCharsets.UTF_8); @@ -59,14 +57,12 @@ public void t1() { Assert.assertEquals(txHdr.getId(), tx.getHeader().getId()); - immuClient.logout(); + immuClient.closeSession(); } @Test(testName = "set, txScan") public void t2() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); String key = "txtest-t2"; byte[] val1 = "immuRocks!".getBytes(StandardCharsets.UTF_8); @@ -93,7 +89,7 @@ public void t2() { Assert.assertNotNull(immuClient.txScan(initialTxId)); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java index 7681568..f338250 100644 --- a/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java +++ b/src/test/java/io/codenotary/immudb4j/UserMgmtTest.java @@ -23,21 +23,17 @@ import java.util.Collections; import java.util.List; -import java.util.Optional; - public class UserMgmtTest extends ImmuClientIntegrationTest { @Test(testName = "createUser, listUsers", priority = 100) public void t1() { - String database = "defaultdb"; String username = "testCreateUser"; String password = "testTest123!"; Permission permission = Permission.PERMISSION_RW; - immuClient.login("immudb", "immudb"); - immuClient.useDatabase(database); + immuClient.openSession("immudb", "immudb", database); // 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). @@ -68,14 +64,12 @@ public void t1() { // Assert.assertEquals(user.getCreatedBy(), "immudb"); // Assert.assertEquals(user.getPermissions().get(0), permission); - immuClient.logout(); + immuClient.closeSession(); } @Test(testName = "createUser, changePassword", priority = 101) public void t2() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); try { immuClient.createUser("testUser", "testTest123!", Permission.PERMISSION_ADMIN, "defaultdb"); @@ -86,18 +80,19 @@ public void t2() { immuClient.changePassword("testUser", "testTest123!", "newTestTest123!"); - immuClient.logout(); + immuClient.closeSession(); // This must fail. try { - immuClient.login("testUser", "testTest123!"); - Assert.fail("Login with wrong (old) password must fail."); + immuClient.openSession("testUser", "testTest123", "defaultdb"); + Assert.fail("should fail with wrong (old) password must fail."); } catch (StatusRuntimeException e) { // Login failed, everything's fine. } - immuClient.login("testUser", "newTestTest123!"); - immuClient.logout(); + immuClient.openSession("testUser", "newTestTest123!", "defaultdb"); + + immuClient.closeSession(); // Some basic test to temporary (until t1 test above can be used) increase the code coverage. User myUser = new User.UserBuilder().setUser("myUsername").setCreatedAt("someTimestamp").setCreatedBy("me") diff --git a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java index c85873a..a0a812d 100644 --- a/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java +++ b/src/test/java/io/codenotary/immudb4j/VerifiedSetAndGetTest.java @@ -26,9 +26,7 @@ public class VerifiedSetAndGetTest extends ImmuClientIntegrationTest { @Test(testName = "set, verifiedGet") public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); String key = "vsg"; byte[] val = "test-set-vget".getBytes(StandardCharsets.UTF_8); @@ -48,14 +46,12 @@ public void t1() { Assert.assertEquals(val, vEntry.getValue()); - immuClient.logout(); + immuClient.closeSession(); } @Test(testName = "verifiedSet, verifiedGet, verifiedGetAt, verifiedGetSince") public void t2() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); byte[] key = "vsg".getBytes(StandardCharsets.UTF_8); byte[] val = "test-vset-vget".getBytes(StandardCharsets.UTF_8); @@ -96,7 +92,7 @@ public void t2() { } // Assert.assertNotNull(txMd); - immuClient.logout(); + immuClient.closeSession(); } diff --git a/src/test/java/io/codenotary/immudb4j/ZAddTest.java b/src/test/java/io/codenotary/immudb4j/ZAddTest.java index f8030ce..eb5be6d 100644 --- a/src/test/java/io/codenotary/immudb4j/ZAddTest.java +++ b/src/test/java/io/codenotary/immudb4j/ZAddTest.java @@ -26,9 +26,7 @@ public class ZAddTest extends ImmuClientIntegrationTest { @Test(testName = "zAdd, verifiedZAdd, verifiedZAddAt") public void t1() { - - immuClient.login("immudb", "immudb"); - immuClient.useDatabase("defaultdb"); + immuClient.openSession("immudb", "immudb", "defaultdb"); String set = "test-zadd"; String key1 = "test-zadd-key1"; @@ -61,7 +59,7 @@ public void t1() { } Assert.assertNotNull(txHdr); - immuClient.logout(); + immuClient.closeSession(); } } diff --git a/tests.sh b/tests.sh index 0825e5d..3095cc7 100755 --- a/tests.sh +++ b/tests.sh @@ -9,19 +9,20 @@ echo ## Unit Tests TESTS="${TESTS} BasicImmuClientTest" +TESTS="${TESTS} HistoryTest" TESTS="${TESTS} HTreeTest" TESTS="${TESTS} ListDatabasesTest ListUsersTest" -TESTS="${TESTS} LoginAndHealthCheckTest" +TESTS="${TESTS} LoginAndHealthCheckAndCleanIndexTest" TESTS="${TESTS} MultidatabaseTest MultithreadTest" TESTS="${TESTS} ReferenceTest" -TESTS="${TESTS} ScanAndHistoryTest" -TESTS="${TESTS} SetAndGetTest SetAllAndGetAllTest" -TESTS="${TESTS} ShutdownTest" +TESTS="${TESTS} ScanTest" +TESTS="${TESTS} SetAllAndGetAllTest SetAndGetTest" TESTS="${TESTS} StateTest" TESTS="${TESTS} TxTest" -TESTS="${TESTS} UseDatabaseTest UserMgmtTest" +TESTS="${TESTS} UserMgmtTest" TESTS="${TESTS} VerifiedSetAndGetTest" TESTS="${TESTS} ZAddTest" +TESTS="${TESTS} ShutdownTest" # ----------------------------------------------------------------------------- From 90bac3c69af19ac0379e1a7af469b62042d8217d Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Tue, 1 Nov 2022 16:51:41 -0300 Subject: [PATCH 04/29] chore: session keep alive heartbeat Signed-off-by: Jeronimo Irazabal --- .../io/codenotary/immudb4j/ImmuClient.java | 36 ++++++++++++++----- .../java/io/codenotary/immudb4j/Session.java | 8 +---- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/codenotary/immudb4j/ImmuClient.java b/src/main/java/io/codenotary/immudb4j/ImmuClient.java index 277fd97..99ad078 100644 --- a/src/main/java/io/codenotary/immudb4j/ImmuClient.java +++ b/src/main/java/io/codenotary/immudb4j/ImmuClient.java @@ -37,6 +37,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -50,16 +52,19 @@ public class ImmuClient { private final PublicKey serverSigningKey; private final ImmuStateHolder stateHolder; + private long keepAlivePeriod; private ManagedChannel channel; private final ImmuServiceGrpc.ImmuServiceBlockingStub stub; 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); } @@ -117,7 +122,20 @@ public synchronized void openSession(String username, String password, String da final ImmudbProto.OpenSessionResponse resp = this.stub.openSession(req); - this.session = new Session(resp.getSessionID(), username, database); + this.session = new Session(resp.getSessionID(), database); + + this.sessionHeartBeat = new Timer(); + + this.sessionHeartBeat.schedule(new TimerTask() { + @Override + public void run() { + try { + stub.keepAlive(Empty.getDefaultInstance()); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 0, keepAlivePeriod); } public synchronized void closeSession() { @@ -125,6 +143,8 @@ public synchronized void closeSession() { throw new IllegalStateException("no open session"); } + this.sessionHeartBeat.cancel(); + try { this.stub.closeSession(Empty.getDefaultInstance()); } finally { @@ -1133,7 +1153,7 @@ public static class Builder { private PublicKey serverSigningKey; - private boolean withAuth; + private long keepAlivePeriod; private ImmuStateHolder stateHolder; @@ -1141,7 +1161,7 @@ private Builder() { this.serverUrl = "localhost"; this.serverPort = 3322; this.stateHolder = new SerializableImmuStateHolder(); - this.withAuth = true; + this.keepAlivePeriod = 60 * 1000; // 1 minute } public ImmuClient build() { @@ -1179,12 +1199,12 @@ public Builder withServerSigningKey(String publicKeyFilename) throws Exception { return this; } - public boolean isWithAuth() { - return withAuth; + public long getKeepAlivePeriod() { + return keepAlivePeriod; } - public Builder withAuth(boolean withAuth) { - this.withAuth = withAuth; + public Builder withKeepAlivePeriod(long keepAlivePeriod) { + this.keepAlivePeriod = keepAlivePeriod; return this; } diff --git a/src/main/java/io/codenotary/immudb4j/Session.java b/src/main/java/io/codenotary/immudb4j/Session.java index 701147c..5b4d130 100644 --- a/src/main/java/io/codenotary/immudb4j/Session.java +++ b/src/main/java/io/codenotary/immudb4j/Session.java @@ -17,23 +17,17 @@ public class Session { - private String username; private String sessionID; private String database; - public Session(String sessionID, String username, String database) { + public Session(String sessionID, String database) { this.sessionID = sessionID; - this.username = username; this.database = database; } public String getSessionID() { return sessionID; } - - public String getUsername() { - return username; - } public String getDatabase() { return database; From f4d73de4e2563b615285ba1fae11b08e720aa447 Mon Sep 17 00:00:00 2001 From: Jeronimo Irazabal Date: Mon, 7 Nov 2022 14:45:18 -0300 Subject: [PATCH 05/29] 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 06/29] 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 07/29] 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 08/29] 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 09/29] 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 10/29] 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 11/29] 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 12/29] 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 13/29] 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 14/29] 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 15/29] 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 16/29] 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 17/29] 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 18/29] 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 19/29] 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 20/29] 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 21/29] 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 22/29] 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 23/29] 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 24/29] 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 25/29] 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 26/29] 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 27/29] 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 28/29] 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 29/29] 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(); }