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