Skip to content

Add atoms::Merger - #1205

Merged
ccgsnet merged 13 commits into
masterfrom
atom-merger
Jul 31, 2026
Merged

Add atoms::Merger#1205
ccgsnet merged 13 commits into
masterfrom
atom-merger

Conversation

@ccgsnet

@ccgsnet ccgsnet commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Replace bool throw_if_exists on AtomDB add_* APIs with an optional atoms::Merger strategy.

  • merger == nullptr (default): upsert — insert if missing, replace if present
  • Custom Merger: merge into a working copy, persist only on success
  • ThrowIfExistsMerger: rejects duplicates (former throw_if_exists=true)

Wired through InMemoryDB, RedisMongoDB, MorkDB, RemoteAtomDB/Peer, and AdapterDB. Call sites and tests updated accordingly.

Test plan

  • inmemorydb_test — replace / throw / custom merge for nodes and links
  • redis_mongodb_test — single + batch replace/merge; ThrowIfExists paths
  • adapterdb_test / chain_operator_test / related agent tests still pass

@ccgsnet ccgsnet self-assigned this Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
  • Replaced bool throw_if_exists with optional const atoms::Merger* merger across single-item and batch AtomDB APIs. Default insertion now performs upserts. ThrowIfExistsMerger preserves duplicate rejection across database backends, adapters, remote peers, and call sites.
  • Backends merge into working copies and persist only successful results. Correctness risks remain around preserving indexes and metadata after rejected merges, propagating merger exceptions, and the documented single-writer assumption for merge-enabled operations.
  • Merge conflict paths allocate cloned Node and Link objects. RAII cleanup limits leak risk, but cloning can increase memory use on hot paths. Batch handling reduces repeated merges by grouping items by handle.
  • Tests in src/tests/cpp/ cover default replacement, duplicate rejection, custom strength merging, rejected merges, batch atomicity, link indexes, composite metadata, and concurrent call-site updates.

Walkthrough

AtomDB insertion APIs now accept optional merger strategies instead of boolean duplicate flags. Backends implement replacement, duplicate rejection, custom batch merging, index updates, and transactional metadata handling. Callers and tests use the revised APIs.

Changes

Merger-based AtomDB insertion

Layer / File(s) Summary
Merger contracts and API propagation
src/commons/atoms/Merger.h, src/atomdb/..., src/atomdb/remotedb/..., src/tests/cpp/test_commons/mocks/MockAtomDB.h
The public add APIs replace throw_if_exists with optional Merger pointers. Batch methods use (items, is_transactional, merger).
Backend upsert and batch merging
src/atomdb/inmemorydb/..., src/atomdb/redis_mongodb/..., src/atomdb/morkdb/...
Backends merge existing and repeated atoms, persist accepted results, preserve rejected data, and update indexes and composite metadata from final objects.
Callers and ingestion updates
src/agents/..., src/db_adapter/..., src/main/db_loader.cc, src/db_adapter/AtomPersister.cc, src/tests/main/...
Broker, ingestion, persistence, link creation, remote release, and evaluation call sites use the revised signatures and streaming flags.
Behavior and integration tests
src/tests/cpp/...
Tests cover replacement, duplicate rejection, custom merging, rejected merges, batch atomicity, transactional metadata, remote calls, mocks, and updated setup calls.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant AtomDB
  participant Backend
  participant Merger
  participant Storage
  Caller->>AtomDB: add atom or batch
  AtomDB->>Backend: forward transactional flag and merger
  Backend->>Merger: merge existing and incoming atoms
  Backend->>Storage: persist merged atom and indexes
Loading

Possibly related PRs

  • singnet/das#1180: Modifies overlapping AtomDB composite-type insertion paths.
  • singnet/das#1181: Modifies overlapping AtomDB backend add logic.
  • singnet/das#1189: Overlaps in InMemoryDB, RemoteAtomDB, and RemoteAtomDBPeer APIs and implementations.

Suggested reviewers: marcocapozzoli

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding the atoms::Merger strategy and related API support.
Description check ✅ Passed The description accurately summarizes the Merger API change, behavior, affected components, and planned tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Tests For Behavior Changes ✅ Passed Production code under src/atomdb and related modules changed behavior (replaced throw_if_exists with Merger strategy) and PR adds/updates tests in inmemorydb_test.cc, redis_mongodb_test.cc, and 7 o...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch atom-merger

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/atomdb/morkdb/MorkDB.cc (1)

245-293: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Defer MORK writes until merger validation completes.

A batch with 5,000 successful links followed by a duplicate using ThrowIfExistsMerger posts MORK at Line 279, then throws on the later merge; Mongo upsert at Lines 301-303 is never reached. This leaves MORK and Mongo inconsistent. Stage MORK submissions until all merges succeed, and add a late-failure regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/morkdb/MorkDB.cc` around lines 245 - 293, The link loop in the
batch persistence flow submits MORK data before all merger operations have
succeeded, allowing a later merge failure to leave MORK ahead of Mongo. In the
code handling links and merger validation, accumulate metta expressions and
defer every mork_client->post call until the full batch completes successfully,
while preserving chunking only for staged data; add a regression test covering
many successful links followed by a ThrowIfExistsMerger duplicate and verify no
MORK submission occurs when merging fails.
🧹 Nitpick comments (3)
src/atomdb/inmemorydb/InMemoryDB.cc (2)

409-418: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Indexing uses link, not the merged to_store equivalent — diverges from RedisMongoDB::add_links.

RedisMongoDB computes match_pattern_index_schema(to_store) and iterates to_store->targets; here the incoming-set and pattern indexing always use the pre-merge link. Harmless while mergers preserve targets, but the two backends now index differently for anything that doesn't. Worth aligning, or documenting in Merger that mergers must not alter targets/type.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/inmemorydb/InMemoryDB.cc` around lines 409 - 418, Align InMemoryDB
indexing with RedisMongoDB by using the merged to_store link for both
incoming-set updates and match_pattern_index_schema instead of the pre-merge
link. Update the target iteration and pattern indexing in the surrounding
link-storage flow, preserving the existing add_incoming_set and add_pattern
calls.

320-325: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

catch (...) + RAISE_ERROR discards the merger's own error and hand-rolls cleanup.

ThrowIfExistsMerger's "Node already exists: " message is replaced by "Failed to merge node", so callers lose the reason. A unique_ptr working copy lets the original exception propagate with no manual delete:

-        Node* working = new Node(*dynamic_cast<Node*>(atom_trie_value->get_atom()));
-        try {
-            merger->merge(working, node);
-        } catch (...) {
-            delete working;
-            RAISE_ERROR("Failed to merge node: " + node->handle());
-        }
-        atoms_trie_->insert(handle, new AtomTrieValue(working));
+        auto working = make_unique<Node>(*dynamic_cast<Node*>(atom_trie_value->get_atom()));
+        merger->merge(working.get(), node);  // throws through; stored state untouched
+        atoms_trie_->insert(handle, new AtomTrieValue(working.release()));

Same applies to the link path at Lines 400-405. See the contract note on src/atomdb/AtomDB.h.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/inmemorydb/InMemoryDB.cc` around lines 320 - 325, Update the node
merge path around merger->merge(working, node) to manage working with a
unique_ptr and remove the catch-all handler, manual delete, and replacement
RAISE_ERROR so the merger’s original exception propagates unchanged. Apply the
same ownership and exception-propagation fix to the corresponding link merge
path.
src/tests/cpp/remote_atomdb_test.cc (1)

46-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise merger propagation through remote layers.

These tests only cover default insertion. Add duplicate-node/link cases through both RemoteAtomDBPeer and RemoteAtomDB using ThrowIfExistsMerger; otherwise a dropped merger argument can silently become an upsert.

As per path instructions, tests should validate “real behavior” and proxy interactions.

Also applies to: 412-414

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/cpp/remote_atomdb_test.cc` around lines 46 - 51, Extend
RemoteAtomDBPeerTest cases around AddAndGetNodes and the corresponding link
tests to insert duplicate nodes and links through both RemoteAtomDBPeer and
RemoteAtomDB, passing ThrowIfExistsMerger. Assert duplicates raise the expected
error so merger propagation through each proxy layer is verified rather than
allowing an unintended upsert.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/atomdb/AtomDB.h`:
- Around line 45-53: Standardize merger exception behavior across InMemoryDB and
RedisMongoDB by choosing either direct propagation or consistent wrapping, then
update add_node/add_nodes/add_link/add_links implementations accordingly.
Document the chosen guarantee in the add_atom/add_node/add_link contract near
the Merger behavior, preserving the merger’s expected caller-visible semantics
across backends.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc`:
- Around line 807-817: The merge path in RedisMongoDB::add_node performs an
unsafe read-modify-write that can lose concurrent merges. Serialize
merger-enabled updates per node handle, or implement an atomic MongoDB
findOneAndUpdate with optimistic revision checking and retry; apply the same
protection to add_nodes and add_links, preserving non-merge insert behavior.
- Around line 871-893: Update the node batch path around batch_merged so merging
still processes every occurrence but serializes exactly one MongodbDocument per
unique handle after the loop. Apply the same deduplication to the link batch
path at src/atomdb/redis_mongodb/RedisMongoDB.cc lines 927-956, emitting one
merged link per handle and skipping repeated incoming-set/pattern ZADD appends
so duplicate occurrences do not consume additional scores.

In `@src/tests/cpp/chain_operator_test.cc`:
- Around line 57-62: Update the duplicate insertion assertion in the node setup
around db->add_node to retain the newly allocated duplicate atoms::Node in a
local pointer, assert its handle, and delete it afterward. Preserve the existing
evaluation node and duplicate-tolerant behavior while ensuring both allocations
are released.

---

Outside diff comments:
In `@src/atomdb/morkdb/MorkDB.cc`:
- Around line 245-293: The link loop in the batch persistence flow submits MORK
data before all merger operations have succeeded, allowing a later merge failure
to leave MORK ahead of Mongo. In the code handling links and merger validation,
accumulate metta expressions and defer every mork_client->post call until the
full batch completes successfully, while preserving chunking only for staged
data; add a regression test covering many successful links followed by a
ThrowIfExistsMerger duplicate and verify no MORK submission occurs when merging
fails.

---

Nitpick comments:
In `@src/atomdb/inmemorydb/InMemoryDB.cc`:
- Around line 409-418: Align InMemoryDB indexing with RedisMongoDB by using the
merged to_store link for both incoming-set updates and
match_pattern_index_schema instead of the pre-merge link. Update the target
iteration and pattern indexing in the surrounding link-storage flow, preserving
the existing add_incoming_set and add_pattern calls.
- Around line 320-325: Update the node merge path around merger->merge(working,
node) to manage working with a unique_ptr and remove the catch-all handler,
manual delete, and replacement RAISE_ERROR so the merger’s original exception
propagates unchanged. Apply the same ownership and exception-propagation fix to
the corresponding link merge path.

In `@src/tests/cpp/remote_atomdb_test.cc`:
- Around line 46-51: Extend RemoteAtomDBPeerTest cases around AddAndGetNodes and
the corresponding link tests to insert duplicate nodes and links through both
RemoteAtomDBPeer and RemoteAtomDB, passing ThrowIfExistsMerger. Assert
duplicates raise the expected error so merger propagation through each proxy
layer is verified rather than allowing an unintended upsert.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 14be289e-3dd4-4899-bc94-d9cb153c2df0

📥 Commits

Reviewing files that changed from the base of the PR and between 3892bc6 and 56f7f6a.

📒 Files selected for processing (30)
  • src/agents/atomdb_broker/AtomDBProxy.cc
  • src/agents/link_creation_agent/MettaTemplateProcessor.cc
  • src/atomdb/AtomDB.h
  • src/atomdb/BUILD
  • src/atomdb/adapterdb/AdapterDB.cc
  • src/atomdb/adapterdb/AdapterDB.h
  • src/atomdb/inmemorydb/InMemoryDB.cc
  • src/atomdb/inmemorydb/InMemoryDB.h
  • src/atomdb/morkdb/MorkDB.cc
  • src/atomdb/morkdb/MorkDB.h
  • src/atomdb/redis_mongodb/RedisMongoDB.cc
  • src/atomdb/redis_mongodb/RedisMongoDB.h
  • src/atomdb/remotedb/RemoteAtomDB.cc
  • src/atomdb/remotedb/RemoteAtomDB.h
  • src/atomdb/remotedb/RemoteAtomDBPeer.cc
  • src/atomdb/remotedb/RemoteAtomDBPeer.h
  • src/commons/atoms/BUILD
  • src/commons/atoms/Merger.h
  • src/db_adapter/AtomPersister.cc
  • src/main/db_loader.cc
  • src/tests/cpp/adapterdb_test.cc
  • src/tests/cpp/chain_operator_test.cc
  • src/tests/cpp/inmemorydb_test.cc
  • src/tests/cpp/link_creation_agent_test.cc
  • src/tests/cpp/morkdb_test.cc
  • src/tests/cpp/redis_mongodb_test.cc
  • src/tests/cpp/redis_mongodb_test_2.cc
  • src/tests/cpp/remote_atomdb_test.cc
  • src/tests/cpp/test_commons/mocks/MockAtomDB.h
  • src/tests/main/evaluation_evolution.cc

Comment thread src/atomdb/AtomDB.h Outdated
Comment thread src/atomdb/redis_mongodb/RedisMongoDB.cc
Comment thread src/atomdb/redis_mongodb/RedisMongoDB.cc Outdated
Comment thread src/tests/cpp/chain_operator_test.cc Outdated
@ccgsnet
ccgsnet requested a review from andre-senna July 29, 2026 13:39
Comment thread src/atomdb/inmemorydb/InMemoryDB.cc Outdated
Comment thread src/atomdb/inmemorydb/InMemoryDB.cc Outdated
Comment thread src/atomdb/redis_mongodb/RedisMongoDB.cc Outdated
@andre-senna
andre-senna self-requested a review July 29, 2026 15:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/tests/cpp/morkdb_test.cc (1)

344-352: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

add_nodes/add_links(…, false, true) was collapsed to (…, true) in three tests. The old third argument was throw_if_exists, not is_transactional. Dropping it promoted is_transactional from false to true and removed duplicate rejection, so these tests now exercise the transactional composite-type path with plain upserts instead of what they were written to cover.

  • src/tests/cpp/morkdb_test.cc#L344-L352: restore false for is_transactional and pass &ThrowIfExistsMerger::instance() in both the in-loop flush and the post-loop flush.
  • src/tests/cpp/redis_mongodb_test.cc#L1240-L1241: restore the original is_transactional value for CompositeTypeEnabledFlag and pass &ThrowIfExistsMerger::instance() for the duplicate-rejection intent.
  • src/tests/cpp/redis_mongodb_test_2.cc#L244-L252: same two-line fix in the in-loop and post-loop db2->add_nodes/add_links calls.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/cpp/morkdb_test.cc` around lines 344 - 352, Restore the original
add_nodes/add_links argument semantics: in src/tests/cpp/morkdb_test.cc lines
344-352, update both in-loop and post-loop flushes to use is_transactional=false
and ThrowIfExistsMerger::instance(); apply the same duplicate-rejection merger
and original CompositeTypeEnabledFlag is_transactional value in
src/tests/cpp/redis_mongodb_test.cc lines 1240-1241, and both db2 flushes in
src/tests/cpp/redis_mongodb_test_2.cc lines 244-252.
🧹 Nitpick comments (5)
src/tests/cpp/inmemorydb_test.cc (1)

690-714: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test covers merge() returning false. That's the branch that Merger.h documents most emphatically ("backends must not persist the working copy") and it's implemented in InMemoryDB::add_node/add_links via the delete working; return/continue paths — currently untested in every test file in this PR. A tiny RejectMerger { bool merge(...) const override { return false; } } plus assertions that the stored atom keeps its original attributes (and, for links, that incoming-set/pattern queries are unchanged) would lock the contract down.

Want me to draft those cases?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/cpp/inmemorydb_test.cc` around lines 690 - 714, Add tests using a
RejectMerger whose merge method returns false, covering both
InMemoryDB::add_node and InMemoryDB::add_links. Assert rejected node merges
preserve the stored atom’s original attributes, and rejected link merges leave
incoming-set and pattern-query results unchanged; retain the existing successful
and throwing merger tests.
src/atomdb/inmemorydb/InMemoryDB.cc (1)

388-406: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Skipping index updates on a failed merge is the right call — worth a brief note.

The continue correctly bypasses incoming-set/pattern indexing since the link was already indexed by whichever add created it. One nit on ownership: the two manual delete working paths could collapse into a unique_ptr<Link> plus release() on the insert, removing the catch (...) entirely. Optional, and only if it matches the surrounding style.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/inmemorydb/InMemoryDB.cc` around lines 388 - 406, In the merge
branch of the InMemoryDB update flow, replace the manual ownership and cleanup
of working with a unique_ptr<Link>, releasing it only when atoms_trie_->insert
succeeds. Remove the redundant catch block while preserving the existing
behavior: failed merges skip insertion and indexing, and exceptions clean up
automatically.
src/atomdb/redis_mongodb/RedisMongoDB.cc (1)

955-986: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Batch merge issues one get_link round-trip per unique handle. For a merger-enabled add_links of N distinct handles this is N sequential Mongo lookups before a single bulk upsert, which will dominate ingestion time on large batches. The existing get_atom_documents(...) bulk path could pre-fetch the stored links once and feed the merge loop from a local map. Same shape applies to add_nodes above.

Not a blocker for this PR, but worth a follow-up if merger-enabled bulk ingestion is on a hot path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc` around lines 955 - 986, The
merger-enabled batch paths in add_links and add_nodes perform one sequential
get_link lookup per unique handle. Follow up by using the existing
get_atom_documents(...) bulk-fetch mechanism to load stored links once into a
local handle-to-link map, then have the merge loops reuse that map instead of
calling get_link for each handle while preserving current merge and persistence
behavior.
src/atomdb/morkdb/MorkDB.cc (2)

285-316: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

const_cast to write metta_expression onto the stored link. For the merger == NULL path to_store is still the caller's Link*, so this silently mutates the caller's object — same as before this PR, but the const_cast now makes it look deliberate. Since links_to_persist is vector<const atoms::Link*>, consider holding atoms::Link* instead and dropping the cast, or documenting that callers must expect metta_expression to be filled in.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/morkdb/MorkDB.cc` around lines 285 - 316, The persistence loop in
MorkDB currently uses const_cast to mutate links from links_to_persist,
including caller-owned links in the merger == NULL path. Remove this implicit
mutation by changing the collection or flow to use mutable atoms::Link* where
appropriate and assign metta_expression without const_cast, or explicitly
document the required caller-visible mutation if constness must remain.

247-282: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

This batch-merge block is a near-verbatim copy of RedisMongoDB::add_links/add_nodes. Three backends now carry the same dedup-into-batch_merged + unique_handles + links_to_persist dance, so any future fix to merge semantics has to be applied in three places. A small shared helper (e.g. in AtomDB or alongside Merger) taking the incoming vector, a "fetch existing" callable, and the merger, returning the list to persist, would keep the semantics in one spot.

Deferrable — flagging it now because the merge contract is likely to keep evolving.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/morkdb/MorkDB.cc` around lines 247 - 282, Extract the duplicated
batch-merge logic from MorkDB’s link persistence flow, centered on the merger,
batch_merged, unique_handles, and links_to_persist handling, into a shared
helper in AtomDB or alongside Merger. Have the helper accept incoming links, an
existing-link lookup callable, and the merger, then return the links to persist
while preserving current merge and deduplication behavior; update MorkDB and the
corresponding RedisMongoDB add_links/add_nodes paths to use it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/tests/cpp/morkdb_test.cc`:
- Around line 344-352: Restore the original add_nodes/add_links argument
semantics: in src/tests/cpp/morkdb_test.cc lines 344-352, update both in-loop
and post-loop flushes to use is_transactional=false and
ThrowIfExistsMerger::instance(); apply the same duplicate-rejection merger and
original CompositeTypeEnabledFlag is_transactional value in
src/tests/cpp/redis_mongodb_test.cc lines 1240-1241, and both db2 flushes in
src/tests/cpp/redis_mongodb_test_2.cc lines 244-252.

---

Nitpick comments:
In `@src/atomdb/inmemorydb/InMemoryDB.cc`:
- Around line 388-406: In the merge branch of the InMemoryDB update flow,
replace the manual ownership and cleanup of working with a unique_ptr<Link>,
releasing it only when atoms_trie_->insert succeeds. Remove the redundant catch
block while preserving the existing behavior: failed merges skip insertion and
indexing, and exceptions clean up automatically.

In `@src/atomdb/morkdb/MorkDB.cc`:
- Around line 285-316: The persistence loop in MorkDB currently uses const_cast
to mutate links from links_to_persist, including caller-owned links in the
merger == NULL path. Remove this implicit mutation by changing the collection or
flow to use mutable atoms::Link* where appropriate and assign metta_expression
without const_cast, or explicitly document the required caller-visible mutation
if constness must remain.
- Around line 247-282: Extract the duplicated batch-merge logic from MorkDB’s
link persistence flow, centered on the merger, batch_merged, unique_handles, and
links_to_persist handling, into a shared helper in AtomDB or alongside Merger.
Have the helper accept incoming links, an existing-link lookup callable, and the
merger, then return the links to persist while preserving current merge and
deduplication behavior; update MorkDB and the corresponding RedisMongoDB
add_links/add_nodes paths to use it.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc`:
- Around line 955-986: The merger-enabled batch paths in add_links and add_nodes
perform one sequential get_link lookup per unique handle. Follow up by using the
existing get_atom_documents(...) bulk-fetch mechanism to load stored links once
into a local handle-to-link map, then have the merge loops reuse that map
instead of calling get_link for each handle while preserving current merge and
persistence behavior.

In `@src/tests/cpp/inmemorydb_test.cc`:
- Around line 690-714: Add tests using a RejectMerger whose merge method returns
false, covering both InMemoryDB::add_node and InMemoryDB::add_links. Assert
rejected node merges preserve the stored atom’s original attributes, and
rejected link merges leave incoming-set and pattern-query results unchanged;
retain the existing successful and throwing merger tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 140314cf-a2bf-4e15-9bd9-f00007f9af67

📥 Commits

Reviewing files that changed from the base of the PR and between 56f7f6a and eaa85c1.

📒 Files selected for processing (10)
  • src/atomdb/AtomDB.h
  • src/atomdb/inmemorydb/InMemoryDB.cc
  • src/atomdb/morkdb/MorkDB.cc
  • src/atomdb/redis_mongodb/RedisMongoDB.cc
  • src/commons/atoms/Merger.h
  • src/tests/cpp/chain_operator_test.cc
  • src/tests/cpp/inmemorydb_test.cc
  • src/tests/cpp/morkdb_test.cc
  • src/tests/cpp/redis_mongodb_test.cc
  • src/tests/cpp/redis_mongodb_test_2.cc
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/tests/cpp/chain_operator_test.cc
  • src/atomdb/AtomDB.h

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/atomdb/redis_mongodb/RedisMongoDB.cc (2)

1018-1022: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Merged links persist stale composite-type metadata in both backends.

Compute composite-type entries and hashes from the final merged object, not from the original input batch:

  • src/atomdb/redis_mongodb/RedisMongoDB.cc#L1018-L1022: derive transactional metadata from each final to_store link.
  • src/atomdb/morkdb/MorkDB.cc#L309-L312: apply the same fix using the final merged link.

As per path instructions, behavior changes under src/ require matching *_test.cc coverage, including edge cases and error paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc` around lines 1018 - 1022, Update
the transactional metadata construction at RedisMongoDB.cc:1018-1022 and the
corresponding MorkDB.cc:309-312 logic to derive composite-type entries and
hashes from each final merged to_store link, not the original input batch. Add
matching *_test.cc coverage under src/ for merged-link metadata, including
relevant edge and error paths.

Source: Path instructions


994-1012: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Remove stale Redis index memberships during link upserts.

This path only adds current target/pattern memberships with ZADD; it never removes memberships belonging to the previously stored link. Replacing or merging a link with different targets or pattern handles can therefore make queries return the link under obsolete targets/patterns even though MongoDB contains the new link.

Capture the old index memberships and issue the corresponding removals, or rebuild the affected Redis indexes atomically. Add regression coverage for target and pattern replacement.

As per path instructions, behavior changes under src/ require matching *_test.cc coverage, including edge cases and error paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc` around lines 994 - 1012, Update the
link upsert path around to_store->targets and pattern_handles to remove stale
Redis ZSET memberships from the previously stored link before or atomically with
adding current memberships. Capture the old target and pattern index entries,
issue corresponding ZREM operations, and preserve current ZADD behavior for
replacements and merges. Add matching *_test.cc regression coverage for target
and pattern replacement, including relevant edge and error paths.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/atomdb/redis_mongodb/RedisMongoDB.cc`:
- Around line 1018-1022: Update the transactional metadata construction at
RedisMongoDB.cc:1018-1022 and the corresponding MorkDB.cc:309-312 logic to
derive composite-type entries and hashes from each final merged to_store link,
not the original input batch. Add matching *_test.cc coverage under src/ for
merged-link metadata, including relevant edge and error paths.
- Around line 994-1012: Update the link upsert path around to_store->targets and
pattern_handles to remove stale Redis ZSET memberships from the previously
stored link before or atomically with adding current memberships. Capture the
old target and pattern index entries, issue corresponding ZREM operations, and
preserve current ZADD behavior for replacements and merges. Add matching
*_test.cc regression coverage for target and pattern replacement, including
relevant edge and error paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3f36287a-aa93-4f42-9b1e-4b4b2d5e719f

📥 Commits

Reviewing files that changed from the base of the PR and between 06fdcea and 6f8cd60.

📒 Files selected for processing (2)
  • src/atomdb/morkdb/MorkDB.cc
  • src/atomdb/redis_mongodb/RedisMongoDB.cc

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/atomdb/morkdb/MorkDB.cc (1)

312-321: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Derive transactional composite_type_hash from composite_type_entries_map in MorkDB.

MorkDB::build_composite_type_entries_map is inherited from RedisMongoDB, where composite_type_hashes_map[link_handle] is set to the link’s named type hash, making MorkDB store named_type_hash while composite_type_entries_map contains the recursive composite type. Use Hasher::composite_handle(composite_type_entries_map[link_handle]) for the stored transactional document before upserting, matching RedisMongoDB and the intended composite_type shape.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/morkdb/MorkDB.cc` around lines 312 - 321, In the transactional
branch of the document construction flow, update the value passed to the MorkDB
`mongodb_doc.emplace` call so the stored composite type hash is derived with
`Hasher::composite_handle(composite_type_entries_map[link_handle])` instead of
`composite_type_hashes_map_copy[link_handle]`. Leave the non-transactional and
empty-composite branches unchanged.
src/atomdb/redis_mongodb/RedisMongoDB.cc (1)

948-996: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fix composite-type metadata for transactional rejected merges.

add_links() should not skip composite-type bookkeeping when merge() returns false. In the existing TxRejectCT test, a later link in the same transactional batch stores a default empty target hash for the rejected duplicate link, so its composite_type array has an extra bogus entry beyond the fresh link’s expected 4 target hashes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc` around lines 948 - 996, Update
add_links() so a failed merger->merge() does not bypass composite-type
bookkeeping: retain the rejected link for build_composite_type_entries_map while
keeping it excluded from links_to_persist. Ensure metadata is derived from both
persisted final objects and rejected transactional links without changing
persistence behavior.
🧹 Nitpick comments (3)
src/atomdb/morkdb/MorkDB.cc (1)

279-287: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Copying the whole composite_type_hashes_map just to read a handful of handles.

composite_type_hashes_map accumulates an entry per atom seen in the transaction, so this snapshot grows with load size while only the links_to_persist handles are ever read back at Line 315. Copy just those, still under the lock.

♻️ Proposed refactor
     if (this->composite_type_enabled() && is_transactional) {
         this->build_composite_type_entries_map(links_to_persist, composite_type_entries_map);
         lock_guard<mutex> composite_type_hashes_map_lock(this->composite_type_hashes_map_mutex);
-        composite_type_hashes_map_copy = this->composite_type_hashes_map;
+        for (const auto* link : links_to_persist) {
+            const string handle = link->handle();
+            auto it = this->composite_type_hashes_map.find(handle);
+            if (it != this->composite_type_hashes_map.end()) {
+                composite_type_hashes_map_copy[handle] = it->second;
+            }
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/morkdb/MorkDB.cc` around lines 279 - 287, Update the transactional
metadata setup around composite_type_hashes_map_copy to copy only entries whose
handles are present in links_to_persist, while holding
composite_type_hashes_map_mutex. Preserve the existing lookup behavior at the
later read site and avoid snapshotting the entire composite_type_hashes_map.
src/atomdb/redis_mongodb/RedisMongoDB.cc (2)

948-983: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

The merge/batch-dedup loop is duplicated verbatim across two backends. Both files implement the identical batch_merged + unique_handles read-merge-dedup algorithm (including the "failed merge → continue" rule), so any future fix to the merge contract has to be applied twice and will silently drift.

  • src/atomdb/redis_mongodb/RedisMongoDB.cc#L948-L983: extract this loop into a shared helper (e.g. an AtomDB-level merge_batch(links, merger, out_handles) returning the deduped working copies) and call it here.
  • src/atomdb/morkdb/MorkDB.cc#L242-L277: replace the copy with a call to the same helper, keeping the local non-const links_to_persist for the metta_expression fill-in.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc` around lines 948 - 983, Extract the
duplicated batch merge/deduplication logic from RedisMongoDB.cc lines 948-983
and MorkDB.cc lines 242-277 into a shared AtomDB-level helper, such as
merge_batch, preserving the batch_merged and unique_handles behavior and the
failed-merge continue rule. Replace both loops with calls to this helper; in
MorkDB.cc, retain the local non-const links_to_persist handling required for
metta_expression fill-in.

985-996: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drop the shadow vector + const_cast: declare links_to_persist as vector<atoms::Link*>.

MorkDB::add_links (line 240 of src/atomdb/morkdb/MorkDB.cc) already stores non-const atoms::Link* and passes the container straight through. Matching it here removes an extra per-batch allocation and the const_cast.

♻️ Proposed refactor
-    vector<const atoms::Link*> links_to_persist;
+    vector<atoms::Link*> links_to_persist;
@@
-    map<string, vector<string>> composite_type_entries_map;
-    if (this->composite_type_enabled_ && is_transactional) {
-        vector<atoms::Link*> links_for_composite;
-        links_for_composite.reserve(links_to_persist.size());
-        for (const auto* link : links_to_persist) {
-            links_for_composite.push_back(const_cast<atoms::Link*>(link));
-        }
-        this->build_composite_type_entries_map(links_for_composite, composite_type_entries_map);
-    }
+    map<string, vector<string>> composite_type_entries_map;
+    if (this->composite_type_enabled_ && is_transactional) {
+        this->build_composite_type_entries_map(links_to_persist, composite_type_entries_map);
+    }

(links_to_persist.reserve(links.size()) before the loop would also avoid the incremental growth.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc` around lines 985 - 996, Declare
links_to_persist as vector<atoms::Link*> and pass it directly to
build_composite_type_entries_map in the transactional composite-type path.
Remove the temporary links_for_composite vector, its reserve call, and the
const_cast conversion; also reserve links_to_persist from the source links
before populating it if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/atomdb/morkdb/MorkDB.cc`:
- Around line 312-321: In the transactional branch of the document construction
flow, update the value passed to the MorkDB `mongodb_doc.emplace` call so the
stored composite type hash is derived with
`Hasher::composite_handle(composite_type_entries_map[link_handle])` instead of
`composite_type_hashes_map_copy[link_handle]`. Leave the non-transactional and
empty-composite branches unchanged.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc`:
- Around line 948-996: Update add_links() so a failed merger->merge() does not
bypass composite-type bookkeeping: retain the rejected link for
build_composite_type_entries_map while keeping it excluded from
links_to_persist. Ensure metadata is derived from both persisted final objects
and rejected transactional links without changing persistence behavior.

---

Nitpick comments:
In `@src/atomdb/morkdb/MorkDB.cc`:
- Around line 279-287: Update the transactional metadata setup around
composite_type_hashes_map_copy to copy only entries whose handles are present in
links_to_persist, while holding composite_type_hashes_map_mutex. Preserve the
existing lookup behavior at the later read site and avoid snapshotting the
entire composite_type_hashes_map.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc`:
- Around line 948-983: Extract the duplicated batch merge/deduplication logic
from RedisMongoDB.cc lines 948-983 and MorkDB.cc lines 242-277 into a shared
AtomDB-level helper, such as merge_batch, preserving the batch_merged and
unique_handles behavior and the failed-merge continue rule. Replace both loops
with calls to this helper; in MorkDB.cc, retain the local non-const
links_to_persist handling required for metta_expression fill-in.
- Around line 985-996: Declare links_to_persist as vector<atoms::Link*> and pass
it directly to build_composite_type_entries_map in the transactional
composite-type path. Remove the temporary links_for_composite vector, its
reserve call, and the const_cast conversion; also reserve links_to_persist from
the source links before populating it if needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f7159360-8daa-4bfa-b1b3-a56c3a51be18

📥 Commits

Reviewing files that changed from the base of the PR and between 6f8cd60 and 6395eeb.

📒 Files selected for processing (3)
  • src/atomdb/morkdb/MorkDB.cc
  • src/atomdb/redis_mongodb/RedisMongoDB.cc
  • src/tests/cpp/redis_mongodb_test.cc

@ccgsnet

ccgsnet commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/atomdb/inmemorydb/InMemoryDB.cc (1)

354-384: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Duplicate handles inside one batch bypass the all-or-nothing precheck.

nodes_exist(handles) only tests the store. If the same handle appears twice in nodes, the precheck passes. The first add_node inserts it, and the second call reaches the merge branch with ThrowIfExistsMerger, which raises. The batch then throws after a partial insert, so the comment on Line 367 no longer holds. The same gap exists in add_links at Lines 386-409.

Consider detecting repeated handles in the precheck as well.

🛡️ Proposed precheck extension
     if (merger == &ThrowIfExistsMerger::instance()) {
+        set<string> seen;
+        for (const auto& handle : handles) {
+            if (!seen.insert(handle).second) {
+                RAISE_ERROR("Failed to insert nodes, duplicated node in batch: " + handle);
+                return {};
+            }
+        }
         auto existing_handles = this->nodes_exist(handles);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/inmemorydb/InMemoryDB.cc` around lines 354 - 384, Extend the
all-or-nothing prechecks in InMemoryDB::add_nodes and add_links to detect
duplicate handles within the incoming batch, not only handles already stored.
Treat any repeated handle as a conflict and raise before invoking add_node or
the corresponding link insertion, preserving zero partial inserts for
ThrowIfExistsMerger.
🧹 Nitpick comments (3)
src/tests/cpp/inmemorydb_test.cc (1)

838-888: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add the in-batch duplicate case to these all-or-nothing tests.

Both tests only cover a handle that already exists in the store. Add a case where the duplicate exists only inside the batch, for example db->add_nodes({a, a_copy}, false, &ThrowIfExistsMerger::instance()). That case currently escapes the nodes_exist precheck and reveals the partial-insert path described in src/atomdb/inmemorydb/InMemoryDB.cc Lines 354-384.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tests/cpp/inmemorydb_test.cc` around lines 838 - 888, Extend
AddNodesThrowIfExistsIsAllOrNothing and AddLinksThrowIfExistsIsAllOrNothing with
batches containing two objects sharing the same handle, where that handle is
absent from the database beforehand. Assert that ThrowIfExistsMerger raises
runtime_error and that neither duplicate nor any other batch item remains
inserted, matching the existing all-or-nothing assertions.
src/atomdb/redis_mongodb/RedisMongoDB.cc (1)

874-904: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Unnecessary copy of the fetched atom on every batch merge path. get_node() and get_link() return freshly built, detached objects that no other code owns, so cloning them before calling merger->merge() duplicates a full Properties map (and, for links, the target vector) once per unique handle. RedisMongoDB::add_node at Line 813 already merges into the fetched object directly; the batch paths should do the same.

  • src/atomdb/redis_mongodb/RedisMongoDB.cc#L874-L904: drop make_shared<Node>(*existing_node) and merge into existing_node, storing it in batch_merged.
  • src/atomdb/redis_mongodb/RedisMongoDB.cc#L964-L1008: drop make_shared<Link>(*existing_link) and merge into existing_link, storing it in batch_merged, composite_keepalive, and links_for_composite.
  • src/atomdb/morkdb/MorkDB.cc#L244-L285: apply the same change to the existing_link clone at Line 259.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/redis_mongodb/RedisMongoDB.cc` around lines 874 - 904, Remove the
unnecessary clones before merge operations: in RedisMongoDB.cc lines 874-904,
merge directly into existing_node and store it in batch_merged; in
RedisMongoDB.cc lines 964-1008, merge directly into existing_link and store it
in batch_merged, composite_keepalive, and links_for_composite; in MorkDB.cc
lines 244-285, apply the same direct merge to existing_link. Preserve the
existing failure handling and ownership flows.

Source: Path instructions

src/atomdb/remotedb/RemoteAtomDB.cc (1)

330-383: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use this->remote_db_ for each fan-out loop.

The changed methods access the RemoteAtomDB::remote_db_ member without this->. Update all six loops to use this->remote_db_ consistently.

Proposed fix
-    for (auto& [uid, peer] : remote_db_) {
+    for (auto& [uid, peer] : this->remote_db_) {

Apply this change at Lines 330, 339, 348, 359, 370, and 381.

As per coding guidelines, “Access class members with this->field consistently in C++.” As per path instructions, “Use this-> for member access.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/atomdb/remotedb/RemoteAtomDB.cc` around lines 330 - 383, Update the
fan-out loops in RemoteAtomDB::add_atom, add_node, add_link, add_atoms,
add_nodes, and add_links to iterate over this->remote_db_ instead of remote_db_.
Preserve the existing loop bodies and behavior.

Sources: Coding guidelines, Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/atomdb/AtomDB.h`:
- Around line 58-66: Update all batch add_atoms, add_nodes, and add_links call
sites to use the three-argument form explicitly: pass the intended
is_transactional value and either the appropriate merger instance, such as
ThrowIfExistsMerger::instance(), or nullptr. Do not leave two-argument calls
that could silently change duplicate-handling behavior.

---

Outside diff comments:
In `@src/atomdb/inmemorydb/InMemoryDB.cc`:
- Around line 354-384: Extend the all-or-nothing prechecks in
InMemoryDB::add_nodes and add_links to detect duplicate handles within the
incoming batch, not only handles already stored. Treat any repeated handle as a
conflict and raise before invoking add_node or the corresponding link insertion,
preserving zero partial inserts for ThrowIfExistsMerger.

---

Nitpick comments:
In `@src/atomdb/redis_mongodb/RedisMongoDB.cc`:
- Around line 874-904: Remove the unnecessary clones before merge operations: in
RedisMongoDB.cc lines 874-904, merge directly into existing_node and store it in
batch_merged; in RedisMongoDB.cc lines 964-1008, merge directly into
existing_link and store it in batch_merged, composite_keepalive, and
links_for_composite; in MorkDB.cc lines 244-285, apply the same direct merge to
existing_link. Preserve the existing failure handling and ownership flows.

In `@src/atomdb/remotedb/RemoteAtomDB.cc`:
- Around line 330-383: Update the fan-out loops in RemoteAtomDB::add_atom,
add_node, add_link, add_atoms, add_nodes, and add_links to iterate over
this->remote_db_ instead of remote_db_. Preserve the existing loop bodies and
behavior.

In `@src/tests/cpp/inmemorydb_test.cc`:
- Around line 838-888: Extend AddNodesThrowIfExistsIsAllOrNothing and
AddLinksThrowIfExistsIsAllOrNothing with batches containing two objects sharing
the same handle, where that handle is absent from the database beforehand.
Assert that ThrowIfExistsMerger raises runtime_error and that neither duplicate
nor any other batch item remains inserted, matching the existing all-or-nothing
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c41cbc3-c114-4238-ac8e-a78b76b87364

📥 Commits

Reviewing files that changed from the base of the PR and between b76e939 and 65601b1.

📒 Files selected for processing (30)
  • src/agents/atomdb_broker/AtomDBProxy.cc
  • src/agents/link_creation_agent/MettaTemplateProcessor.cc
  • src/atomdb/AtomDB.h
  • src/atomdb/BUILD
  • src/atomdb/adapterdb/AdapterDB.cc
  • src/atomdb/adapterdb/AdapterDB.h
  • src/atomdb/inmemorydb/InMemoryDB.cc
  • src/atomdb/inmemorydb/InMemoryDB.h
  • src/atomdb/morkdb/MorkDB.cc
  • src/atomdb/morkdb/MorkDB.h
  • src/atomdb/redis_mongodb/RedisMongoDB.cc
  • src/atomdb/redis_mongodb/RedisMongoDB.h
  • src/atomdb/remotedb/RemoteAtomDB.cc
  • src/atomdb/remotedb/RemoteAtomDB.h
  • src/atomdb/remotedb/RemoteAtomDBPeer.cc
  • src/atomdb/remotedb/RemoteAtomDBPeer.h
  • src/commons/atoms/BUILD
  • src/commons/atoms/Merger.h
  • src/db_adapter/AtomPersister.cc
  • src/main/db_loader.cc
  • src/tests/cpp/adapterdb_test.cc
  • src/tests/cpp/chain_operator_test.cc
  • src/tests/cpp/inmemorydb_test.cc
  • src/tests/cpp/link_creation_agent_test.cc
  • src/tests/cpp/morkdb_test.cc
  • src/tests/cpp/redis_mongodb_test.cc
  • src/tests/cpp/redis_mongodb_test_2.cc
  • src/tests/cpp/remote_atomdb_test.cc
  • src/tests/cpp/test_commons/mocks/MockAtomDB.h
  • src/tests/main/evaluation_evolution.cc

Comment thread src/atomdb/AtomDB.h Outdated
@andre-senna
andre-senna self-requested a review July 31, 2026 12:45
@ccgsnet
ccgsnet merged commit 1be99f7 into master Jul 31, 2026
3 checks passed
@ccgsnet
ccgsnet deleted the atom-merger branch July 31, 2026 15:10
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.

2 participants