diff --git a/.github/workflows/mvn-release.yml b/.github/workflows/mvn-release.yml index 593a647a9d..3c904d3659 100644 --- a/.github/workflows/mvn-release.yml +++ b/.github/workflows/mvn-release.yml @@ -81,22 +81,3 @@ jobs: - name: Set next development version if: success() run: mvn versions:set "-DnewVersion=${{ github.event.inputs.nextversion }}" --no-transfer-progress - - - name: Update studio package.json to next development version - if: success() - run: | - cd studio - jq --arg version "${{ github.event.inputs.nextversion }}" '.version = $version' package.json > package.json.tmp - jq --arg version "${{ github.event.inputs.nextversion }}" '.version = $version' package-lock.json > package-lock.json.tmp - mv package.json.tmp package.json - mv package-lock.json.tmp package-lock.json - - - name: Commit next development version - if: success() - run: | - git config user.email "actions@github.com" - git config user.name "GitHub Actions" - git commit -am "Set next development version to ${{ github.event.inputs.nextversion }}" - git push origin main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/bolt/pom.xml b/bolt/pom.xml index 7617a876c1..12598380ba 100644 --- a/bolt/pom.xml +++ b/bolt/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/console/pom.xml b/console/pom.xml index e4d126e723..4b6cf61a25 100644 --- a/console/pom.xml +++ b/console/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/coverage/pom.xml b/coverage/pom.xml index 5b0604d82c..ce32f9e756 100644 --- a/coverage/pom.xml +++ b/coverage/pom.xml @@ -26,7 +26,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/e2e-perf/pom.xml b/e2e-perf/pom.xml index 321aad240a..aed2468a33 100644 --- a/e2e-perf/pom.xml +++ b/e2e-perf/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/e2e/pom.xml b/e2e/pom.xml index 0e42fe442f..db58400937 100644 --- a/e2e/pom.xml +++ b/e2e/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/engine/pom.xml b/engine/pom.xml index 55e0a504e3..39387a46ad 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/engine/src/main/java/com/arcadedb/database/TransactionIndexContext.java b/engine/src/main/java/com/arcadedb/database/TransactionIndexContext.java index 279cebcc29..26419eac0a 100644 --- a/engine/src/main/java/com/arcadedb/database/TransactionIndexContext.java +++ b/engine/src/main/java/com/arcadedb/database/TransactionIndexContext.java @@ -42,6 +42,7 @@ public static class IndexKey { public final boolean unique; public final Object[] keyValues; public final RID rid; + public RID oldRid; // for REPLACE created from same-bucket REMOVE→ADD: the old RID being replaced public IndexKeyOperation operation; public enum IndexKeyOperation { @@ -174,6 +175,9 @@ public void commit() { for (final IndexKey key : values) { if (key.operation == IndexKey.IndexKeyOperation.REMOVE) index.remove(key.keyValues, key.rid); + else if (key.operation == IndexKey.IndexKeyOperation.REPLACE && key.oldRid != null) + // REMOVE THE OLD RID THAT WAS REPLACED BY A NEW ONE IN THE SAME BUCKET + index.remove(key.keyValues, key.oldRid); } } } @@ -290,6 +294,14 @@ public void addIndexKeyLock(final IndexInternal index, IndexKey.IndexKeyOperatio // REPLACE EXISTENT WITH THIS v.operation = IndexKey.IndexKeyOperation.REPLACE; + if (entry != null) { + if (entry.operation == IndexKey.IndexKeyOperation.REMOVE) + // SAVE THE OLD RID SO IT CAN BE PROPERLY REMOVED FROM THE PERSISTED INDEX AT COMMIT TIME + v.oldRid = entry.rid; + else if (entry.operation == IndexKey.IndexKeyOperation.REPLACE) + // PROPAGATE THE OLD RID FROM THE PREVIOUS REPLACE OPERATION (e.g. REMOVE → ADD → ADD) + v.oldRid = entry.oldRid; + } } } } @@ -418,9 +430,14 @@ private Map> getTxDeletedEntries() { final ComparableKey key = new ComparableKey(entry.getValue().keyValues); final RID existent = entries.get(key); - if (existent == null || entry.getValue().operation == IndexKey.IndexKeyOperation.REMOVE) - // MULTIPLE OPERATIONS ON THE SAME KEY (DIFFERENT BUCKETS), PREFER THE REMOVE ONE - entries.put(key, entry.getKey().rid); + if (existent == null || entry.getValue().operation == IndexKey.IndexKeyOperation.REMOVE) { + // MULTIPLE OPERATIONS ON THE SAME KEY (DIFFERENT BUCKETS), PREFER THE REMOVE ONE. + // For REPLACE entries that originated from a same-bucket REMOVE→ADD merge, use the oldRid (the actual deleted RID). + final RID deletedRid = (entry.getValue().operation == IndexKey.IndexKeyOperation.REPLACE && entry.getValue().oldRid != null) + ? entry.getValue().oldRid + : entry.getKey().rid; + entries.put(key, deletedRid); + } } } } diff --git a/engine/src/test/java/com/arcadedb/graph/EdgeIndexDuplicateKeyTest.java b/engine/src/test/java/com/arcadedb/graph/EdgeIndexDuplicateKeyTest.java index 0e85165f43..a383ce9830 100644 --- a/engine/src/test/java/com/arcadedb/graph/EdgeIndexDuplicateKeyTest.java +++ b/engine/src/test/java/com/arcadedb/graph/EdgeIndexDuplicateKeyTest.java @@ -19,8 +19,11 @@ package com.arcadedb.graph; import com.arcadedb.TestHelper; +import com.arcadedb.query.sql.executor.Result; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; + /** * Test for issue #3097: Edge indexes become invalid in certain scenario #2 * Reproduces DuplicatedKeyException when deleting and recreating the same edge multiple times. @@ -33,49 +36,63 @@ class EdgeIndexDuplicateKeyTest extends TestHelper { void edgeDeleteAndRecreateMultipleTimes() { // Transaction #1: Create schema database.transaction(() -> { - database.command("sql", "CREATE VERTEX TYPE duct"); - database.command("sql", "CREATE VERTEX TYPE trs"); - database.command("sql", "CREATE PROPERTY duct.id STRING"); - database.command("sql", "CREATE INDEX ON duct (id) UNIQUE"); - database.command("sql", "CREATE PROPERTY trs.id STRING"); - database.command("sql", "CREATE INDEX ON trs (id) UNIQUE"); - database.command("sql", "CREATE EDGE TYPE trs_duct"); - database.command("sql", "CREATE PROPERTY trs_duct.from_id STRING"); - database.command("sql", "CREATE INDEX ON trs_duct (from_id) NOTUNIQUE"); - database.command("sql", "CREATE PROPERTY trs_duct.to_id STRING"); - database.command("sql", "CREATE INDEX ON trs_duct (to_id) NOTUNIQUE"); - database.command("sql", "CREATE PROPERTY trs_duct.swap STRING"); - database.command("sql", "CREATE PROPERTY trs_duct.order_number INTEGER"); - database.command("sql", "CREATE INDEX ON trs_duct (from_id,to_id,swap,order_number) UNIQUE"); + database.command("sqlscript", """ + CREATE VERTEX TYPE duct; + CREATE VERTEX TYPE trs; + CREATE PROPERTY duct.id STRING; + CREATE INDEX ON duct (id) UNIQUE; + CREATE PROPERTY trs.id STRING; + CREATE INDEX ON trs (id) UNIQUE; + CREATE EDGE TYPE trs_duct; + CREATE PROPERTY trs_duct.from_id STRING; + CREATE INDEX ON trs_duct (from_id) NOTUNIQUE; + CREATE PROPERTY trs_duct.to_id STRING; + CREATE INDEX ON trs_duct (to_id) NOTUNIQUE; + CREATE PROPERTY trs_duct.swap STRING; + CREATE PROPERTY trs_duct.order_number INTEGER; + CREATE INDEX ON trs_duct (from_id,to_id,swap,order_number) UNIQUE; + """); }); // Transaction #2: Insert vertices and create edge database.transaction(() -> { - database.command("sql", "INSERT INTO duct (id) VALUES ('duct_1')"); - database.command("sql", "INSERT INTO trs (id) VALUES ('trs_1')"); - database.command("sql", - "CREATE EDGE trs_duct from (SELECT FROM trs WHERE id='trs_1') to (SELECT FROM duct WHERE id='duct_1') " + - "SET from_id='trs_1', to_id='duct_1', swap='N', order_number=1"); + database.command("sqlscript", """ + INSERT INTO duct (id) VALUES ('duct_1'); + INSERT INTO trs (id) VALUES ('trs_1'); + + CREATE EDGE trs_duct + from (SELECT FROM trs WHERE id='trs_1') + to (SELECT FROM duct WHERE id='duct_1') + SET from_id='trs_1', to_id='duct_1', swap='N', order_number=1"""); }); // Transaction #3: Delete and recreate edge (first time - should work) database.transaction(() -> { - database.command("sql", - "DELETE FROM trs_duct WHERE (from_id='trs_1') AND (to_id='duct_1') AND (swap='N') AND (order_number=1)"); - database.command("sql", - "CREATE EDGE trs_duct from (SELECT FROM trs WHERE id='trs_1') to (SELECT FROM duct WHERE id='duct_1') " + - "SET from_id='trs_1', to_id='duct_1', swap='N', order_number=1"); + database.command("sqlscript", """ + DELETE FROM trs_duct WHERE (from_id='trs_1') AND (to_id='duct_1') AND (swap='N') AND (order_number=1); + + CREATE EDGE trs_duct + from (SELECT FROM trs WHERE id='trs_1') + to (SELECT FROM duct WHERE id='duct_1') + SET from_id='trs_1', to_id='duct_1', swap='N', order_number=1"""); }); // Transaction #4: Delete and recreate edge (second time - this should NOT throw DuplicatedKeyException) - database.transaction(() -> { - database.command("sql", - "DELETE FROM trs_duct WHERE (from_id='trs_1') AND (to_id='duct_1') AND (swap='N') AND (order_number=1)"); - database.command("sql", - "CREATE EDGE trs_duct from (SELECT FROM trs WHERE id='trs_1') to (SELECT FROM duct WHERE id='duct_1') " + - "SET from_id='trs_1', to_id='duct_1', swap='N', order_number=1"); - }); + for (int i = 0; i < 10; i++) { + database.transaction(() -> { + database.command("sqlscript", """ + DELETE FROM trs_duct WHERE (from_id='trs_1') AND (to_id='duct_1') AND (swap='N') AND (order_number=1); + + CREATE EDGE trs_duct + from (SELECT FROM trs WHERE id='trs_1') + to (SELECT FROM duct WHERE id='duct_1') + SET from_id='trs_1', to_id='duct_1', swap='N', order_number=1"""); + }); + } - // If we got here without exception, the test passes + Result result = database.query("sql", + "SELECT COUNT(*) AS edgeCount FROM trs_duct WHERE from_id='trs_1' AND to_id='duct_1' AND swap='N' AND order_number=1") + .next(); + assertThat(result.getProperty("edgeCount")).isEqualTo(1); } } diff --git a/graphql/pom.xml b/graphql/pom.xml index e04dbea28a..94fe85e417 100644 --- a/graphql/pom.xml +++ b/graphql/pom.xml @@ -24,7 +24,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/gremlin/pom.xml b/gremlin/pom.xml index 37303e4239..7a74edbcdc 100644 --- a/gremlin/pom.xml +++ b/gremlin/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/grpc-client/pom.xml b/grpc-client/pom.xml index 8a5facbeab..65fcba2779 100644 --- a/grpc-client/pom.xml +++ b/grpc-client/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/grpc/pom.xml b/grpc/pom.xml index 73bf9dd5c5..635db76086 100644 --- a/grpc/pom.xml +++ b/grpc/pom.xml @@ -22,7 +22,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/grpcw/pom.xml b/grpcw/pom.xml index c49ad13bdc..2665a9ca41 100644 --- a/grpcw/pom.xml +++ b/grpcw/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/integration/pom.xml b/integration/pom.xml index 4dee2c03d2..0d85b5e0e1 100644 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/metrics/pom.xml b/metrics/pom.xml index ee793259e0..ce6bf5a7bd 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/mongodbw/pom.xml b/mongodbw/pom.xml index 8948aff94b..eca6b837e3 100644 --- a/mongodbw/pom.xml +++ b/mongodbw/pom.xml @@ -24,7 +24,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/network/pom.xml b/network/pom.xml index b8cd550b2d..e328a086d6 100644 --- a/network/pom.xml +++ b/network/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/package/pom.xml b/package/pom.xml index f4118504fd..5bd5633dbf 100644 --- a/package/pom.xml +++ b/package/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index e8a9599dd9..eccdd889ba 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent pom - 26.2.1 + 26.2.2-SNAPSHOT ArcadeDB https://arcadedata.com/ diff --git a/postgresw/pom.xml b/postgresw/pom.xml index 8176c823a1..4f3ced9bea 100644 --- a/postgresw/pom.xml +++ b/postgresw/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/redisw/pom.xml b/redisw/pom.xml index 4421514b11..4c81102f6d 100644 --- a/redisw/pom.xml +++ b/redisw/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/server/pom.xml b/server/pom.xml index 81e84c038a..e4de74d455 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml diff --git a/server/src/main/java/com/arcadedb/server/ha/message/TxForwardRequest.java b/server/src/main/java/com/arcadedb/server/ha/message/TxForwardRequest.java index 2d639c2b20..ccbf56b6f8 100755 --- a/server/src/main/java/com/arcadedb/server/ha/message/TxForwardRequest.java +++ b/server/src/main/java/com/arcadedb/server/ha/message/TxForwardRequest.java @@ -160,6 +160,15 @@ protected void writeIndexKeysToBuffer(final DatabaseInternal database, uniqueKeysBuffer.putByte((byte) key.operation.ordinal()); uniqueKeysBuffer.putUnsignedNumber(key.rid.getBucketId()); uniqueKeysBuffer.putUnsignedNumber(key.rid.getPosition()); + if (key.operation == TransactionIndexContext.IndexKey.IndexKeyOperation.REPLACE) { + // Serialize oldRid for REPLACE entries (introduced to fix same-bucket REMOVE→ADD merge) + final boolean hasOldRid = key.oldRid != null; + uniqueKeysBuffer.putByte((byte) (hasOldRid ? 1 : 0)); + if (hasOldRid) { + uniqueKeysBuffer.putUnsignedNumber(key.oldRid.getBucketId()); + uniqueKeysBuffer.putUnsignedNumber(key.oldRid.getPosition()); + } + } } } } @@ -211,6 +220,11 @@ protected Map com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml @@ -72,16 +72,16 @@ - - npm audit - - npm - - compile - - audit --audit-level=moderate - - + + + + + + + + + + diff --git a/test-utils/pom.xml b/test-utils/pom.xml index 8c99d5a255..1ae3983b80 100644 --- a/test-utils/pom.xml +++ b/test-utils/pom.xml @@ -25,7 +25,7 @@ com.arcadedb arcadedb-parent - 26.2.1 + 26.2.2-SNAPSHOT ../pom.xml