Skip to content

Comments

fix: edge indexes become invalid when deleting and recreating same ed…#3512

Open
robfrank wants to merge 2 commits intomainfrom
release-26.2.2
Open

fix: edge indexes become invalid when deleting and recreating same ed…#3512
robfrank wants to merge 2 commits intomainfrom
release-26.2.2

Conversation

@robfrank
Copy link
Collaborator

…ge (#3097) (#3482)

In TransactionIndexContext.addIndexKeyLock(), when a unique index entry is deleted (REMOVE) and recreated (ADD) with the same key within the same transaction on the same bucket-level index, the REMOVE entry was silently replaced by a REPLACE entry — losing the old RID. At commit time the old persisted index entry was never removed, causing stale entries to accumulate. After 2+ iterations, checkUniqueIndexKeys detected >2 entries for the same unique key and threw DuplicatedKeyException.

Fix: store the old RID in the REPLACE entry (IndexKey.oldRid). At commit time, call index.remove(key, oldRid) for REPLACE entries with a non-null oldRid to properly clean up the old persisted entry. Also fix getTxDeletedEntries() to use oldRid as the deleted RID for correctness in checkUniqueIndexKeys. Update TxForwardRequest serialization to include oldRid for HA replication.

When the same unique key undergoes REMOVE → ADD → ADD in the same transaction on the same bucket, the second ADD finds an existing REPLACE entry. The oldRid from that REPLACE must be propagated to the new REPLACE so the original persisted index entry is still properly removed at commit time.

(cherry picked from commit dffb415)

…ge (#3097) (#3482)

* fix: edge indexes become invalid when deleting and recreating same edge (#3097)

In TransactionIndexContext.addIndexKeyLock(), when a unique index entry is
deleted (REMOVE) and recreated (ADD) with the same key within the same
transaction on the same bucket-level index, the REMOVE entry was silently
replaced by a REPLACE entry — losing the old RID. At commit time the old
persisted index entry was never removed, causing stale entries to accumulate.
After 2+ iterations, checkUniqueIndexKeys detected >2 entries for the same
unique key and threw DuplicatedKeyException.

Fix: store the old RID in the REPLACE entry (IndexKey.oldRid). At commit time,
call index.remove(key, oldRid) for REPLACE entries with a non-null oldRid to
properly clean up the old persisted entry. Also fix getTxDeletedEntries() to
use oldRid as the deleted RID for correctness in checkUniqueIndexKeys. Update
TxForwardRequest serialization to include oldRid for HA replication.

* fix: propagate oldRid through chained REPLACE operations (#3097)

When the same unique key undergoes REMOVE → ADD → ADD in the same transaction
on the same bucket, the second ADD finds an existing REPLACE entry. The oldRid
from that REPLACE must be propagated to the new REPLACE so the original
persisted index entry is still properly removed at commit time.

* test: assert edge count equals 1 after delete-recreate loop (#3097)

(cherry picked from commit dffb415)
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @robfrank, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical issue where unique edge indexes could become invalid after an edge was deleted and then immediately recreated within the same transaction. The fix ensures that the database correctly tracks and cleans up the original record IDs during these REPLACE operations, preventing the accumulation of stale index entries and avoiding DuplicatedKeyException errors. This significantly improves the reliability and data integrity of transactional operations involving unique edge indexes.

Highlights

  • Index Key Tracking Enhancement: Introduced an oldRid field in TransactionIndexContext.IndexKey to correctly track the original Record ID during index key replacements, especially for REPLACE operations resulting from same-bucket REMOVEADD merges.
  • Transactional Index Cleanup: Modified the transaction commit process to explicitly remove the oldRid from persisted indexes when a REPLACE operation with a non-null oldRid is encountered, preventing stale index entries.
  • Old RID Propagation: Enhanced addIndexKeyLock to save the oldRid from a REMOVE operation when it merges into a REPLACE operation, and to propagate the oldRid through chained REPLACE operations (e.g., REMOVEADDADD).
  • Accurate Deleted Entry Detection: Updated getTxDeletedEntries to use the oldRid for REPLACE entries, ensuring that checkUniqueIndexKeys correctly identifies the actual deleted RID for unique index validation.
  • HA Replication Support: Extended TxForwardRequest serialization and deserialization to include the oldRid for REPLACE index keys, ensuring proper High Availability replication of these transactional changes.
  • Regression Test Addition: Added a new test case, EdgeIndexDuplicateKeyTest, which repeatedly deletes and recreates an edge within transactions, asserting that no DuplicatedKeyException is thrown and the final edge count remains correct.
  • Version Updates: Updated the parent POM version across all modules to 26.2.2-SNAPSHOT.
  • NPM Audit Configuration: Adjusted the npm audit configuration in the studio module, changing the security-check script's audit level to none and commenting out the npm audit execution block in studio/pom.xml.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • bolt/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • console/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • coverage/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • e2e-perf/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • e2e/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • engine/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • engine/src/main/java/com/arcadedb/database/TransactionIndexContext.java
    • Added oldRid field to IndexKey class.
    • Modified commit() method to remove oldRid for REPLACE operations.
    • Updated addIndexKeyLock() to store and propagate oldRid for REPLACE entries.
    • Adjusted getTxDeletedEntries() to use oldRid for REPLACE entries.
  • engine/src/test/java/com/arcadedb/graph/EdgeIndexDuplicateKeyTest.java
    • Imported Result and assertThat.
    • Converted database.command("sql", ...) to database.command("sqlscript", """...""") for schema creation and edge operations.
    • Added a loop to perform delete and recreate operations multiple times.
    • Added an assertion to verify the final edge count is 1.
  • graphql/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • gremlin/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • grpc-client/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • grpc/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • grpcw/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • integration/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • metrics/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • mongodbw/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • network/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • package/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • postgresw/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • redisw/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • server/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
  • server/src/main/java/com/arcadedb/server/ha/message/TxForwardRequest.java
    • Added serialization of oldRid for REPLACE index keys.
    • Added deserialization of oldRid for REPLACE index keys.
  • studio/package.json
    • Modified the security-check script to use --audit-level=none.
  • studio/pom.xml
    • Commented out the npm audit execution block.
  • test-utils/pom.xml
    • Updated parent version to 26.2.2-SNAPSHOT.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request successfully addresses a bug concerning edge index invalidation when an edge is deleted and recreated within the same transaction. The core logic changes in TransactionIndexContext.java are sound and effectively fix the issue by tracking the old record ID. The necessary updates for High Availability replication in TxForwardRequest.java are also correctly implemented. The accompanying test case has been significantly improved and now robustly verifies the fix.

However, I've identified two high-severity security issues related to disabling the npm audit checks for frontend dependencies in studio/pom.xml and studio/package.json. These changes weaken the project's security posture and should be reverted. I've also included a medium-severity suggestion to improve code readability in TransactionIndexContext.java.

"update": "npm update",
"outdated": "npm outdated",
"security-check": "npm audit --audit-level=moderate",
"security-check": "npm audit --audit-level=none",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The security-check script has been changed to use npm audit --audit-level=none. This setting reports vulnerabilities but does not fail the build, effectively weakening the security gate. The previous setting of --audit-level=moderate is recommended to ensure that vulnerabilities of moderate severity or higher are addressed before merging.

Suggested change
"security-check": "npm audit --audit-level=none",
"security-check": "npm audit --audit-level=moderate",

Comment on lines +75 to +84
<!-- <execution>-->
<!-- <id>npm audit</id>-->
<!-- <goals>-->
<!-- <goal>npm</goal>-->
<!-- </goals>-->
<!-- <phase>compile</phase>-->
<!-- <configuration>-->
<!-- <arguments>audit &#45;&#45;audit-level=none</arguments>-->
<!-- </configuration>-->
<!-- </execution>-->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The npm audit execution has been commented out, which disables security vulnerability scanning for frontend dependencies during the Maven build process. This poses a security risk by potentially allowing known vulnerabilities into the application. It is strongly recommended to re-enable this check. The corresponding security-check script in package.json has also been weakened. Both should be reverted to maintain security standards.

Suggested change
<!-- <execution>-->
<!-- <id>npm audit</id>-->
<!-- <goals>-->
<!-- <goal>npm</goal>-->
<!-- </goals>-->
<!-- <phase>compile</phase>-->
<!-- <configuration>-->
<!-- <arguments>audit &#45;&#45;audit-level=none</arguments>-->
<!-- </configuration>-->
<!-- </execution>-->
<execution>
<id>npm audit</id>
<goals>
<goal>npm</goal>
</goals>
<phase>compile</phase>
<configuration>
<arguments>audit --audit-level=moderate</arguments>
</configuration>
</execution>

Comment on lines +436 to +439
final RID deletedRid = (entry.getValue().operation == IndexKey.IndexKeyOperation.REPLACE && entry.getValue().oldRid != null)
? entry.getValue().oldRid
: entry.getKey().rid;
entries.put(key, deletedRid);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For improved readability and maintainability, this dense ternary operator can be refactored into a more explicit if-else block.

                final RID deletedRid;
                if (entry.getValue().operation == IndexKey.IndexKeyOperation.REPLACE && entry.getValue().oldRid != null) {
                  deletedRid = entry.getValue().oldRid;
                } else {
                  deletedRid = entry.getKey().rid;
                }
                entries.put(key, deletedRid);

@mergify
Copy link
Contributor

mergify bot commented Feb 23, 2026

🧪 CI Insights

Here's what we observed from your CI run for 484414d.

🟢 All jobs passed!

But CI Insights is watching 👀

@codacy-production
Copy link

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
Report missing for ced65311 59.09%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (ced6531) Report Missing Report Missing Report Missing
Head commit (d4308e1) 85725 61930 72.24%

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#3512) 22 13 59.09%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

Footnotes

  1. Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.

@codecov
Copy link

codecov bot commented Feb 23, 2026

Codecov Report

❌ Patch coverage is 36.36364% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.27%. Comparing base (02cf473) to head (d4308e1).
⚠️ Report is 99 commits behind head on main.

Files with missing lines Patch % Lines
...m/arcadedb/server/ha/message/TxForwardRequest.java 0.00% 8 Missing and 2 partials ⚠️
...com/arcadedb/database/TransactionIndexContext.java 66.66% 1 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3512      +/-   ##
==========================================
+ Coverage   62.87%   63.27%   +0.40%     
==========================================
  Files        1295     1295              
  Lines       85705    85725      +20     
  Branches    17619    17630      +11     
==========================================
+ Hits        53884    54246     +362     
+ Misses      24151    23821     -330     
+ Partials     7670     7658      -12     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant