Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions .github/workflows/mvn-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
2 changes: 1 addition & 1 deletion bolt/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion console/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion coverage/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion e2e-perf/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion e2e/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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;
}
}
}
}
Expand Down Expand Up @@ -418,9 +430,14 @@ private Map<TypeIndex, Map<ComparableKey, RID>> 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);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.<Long>getProperty("edgeCount")).isEqualTo(1);
}
}
2 changes: 1 addition & 1 deletion graphql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion gremlin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion grpc-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion grpc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion grpcw/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion integration/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion metrics/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion mongodbw/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion network/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion package/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<packaging>pom</packaging>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>

<name>ArcadeDB</name>
<url>https://arcadedata.com/</url>
Expand Down
2 changes: 1 addition & 1 deletion postgresw/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion redisw/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<parent>
<groupId>com.arcadedb</groupId>
<artifactId>arcadedb-parent</artifactId>
<version>26.2.1</version>
<version>26.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
}
}
}
Expand Down Expand Up @@ -211,6 +220,11 @@ protected Map<String, TreeMap<TransactionIndexContext.ComparableKey, Map<Transac

final TransactionIndexContext.IndexKey v = new TransactionIndexContext.IndexKey(index.isUnique(), operation, keyValues,
rid);
if (operation == TransactionIndexContext.IndexKey.IndexKeyOperation.REPLACE) {
final byte hasOldRidFlag = uniqueKeysBuffer.getByte();
if (hasOldRidFlag == 1)
v.oldRid = new RID(database, (int) uniqueKeysBuffer.getUnsignedNumber(), uniqueKeysBuffer.getUnsignedNumber());
}
values.put(v, v);
}
}
Expand Down
2 changes: 1 addition & 1 deletion studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"audit-fix": "npm audit fix",
"update": "npm update",
"outdated": "npm outdated",
"security-check": "npm audit --audit-level=moderate",
"security-check": "npm audit --audit-level=none",
"security-audit": "./scripts/security-audit.sh",
"copy-swagger-ui": "node scripts/copy-swagger-ui.js",
"prebuild": "npm run copy-swagger-ui",
Expand Down
Loading