[Toy] Fix RemoteDB concurrent logic - #1189
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
🚧 Files skipped from review as they are similar to previous changes (5)
WalkthroughThis change adds snapshot-based concurrency to ChangesRemote federation behavior
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Evaluation
participant RemoteAtomDB
participant RemoteAtomDBPeer
participant LocalPersistence
Evaluation->>RemoteAtomDB: release_caches(link_schema, persist, force)
RemoteAtomDB->>RemoteAtomDBPeer: release(link_schema, persist, force)
RemoteAtomDBPeer->>LocalPersistence: persist staged nodes and links
LocalPersistence-->>RemoteAtomDBPeer: persistence result
RemoteAtomDBPeer-->>RemoteAtomDB: release result
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 (3)
src/atomdb/remotedb/RemoteAtomDB.cc (1)
415-440: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAdd a multi-peer batch-delete test.
maxmatches the replicated-peer model here, butremote_atomdb_test.ccdoesn’t coverdelete_atoms/delete_nodes/delete_linksacross multiple peers, so the count semantics aren’t pinned down.🤖 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 415 - 440, Add a multi-peer batch-delete test in remote_atomdb_test.cc covering delete_atoms, delete_nodes, and delete_links through RemoteAtomDB with multiple replicated peers. Assert that each method returns the maximum deletion count reported by any peer, preserving the existing max-based semantics.src/atomdb/inmemorydb/InMemoryDB.cc (1)
190-208: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick winCritical:
get_matching_atomswraps trie-ownedAtom*in a freshshared_ptr, causing a double-free/use-after-free.
shared_ptr<Node>(dynamic_cast<Node*>(atom))(and theLinkbranch) take ownership of the same raw pointer still owned byAtomTrieValue. When either the returnedshared_ptror~AtomTrieValue()runs first, the other holder is left with a dangling pointer, and the second destruction double-frees.get_atom()andget_all_atoms()were just fixed to return safe deep clones via the newclone_atom()helper — this method was left with the pre-existing unsafe pattern despite the added locking, which only serializes access to (but doesn't fix) the unsafe aliasing. Given this PR's explicit goal is eliminating concurrency-related crashes, this is a live segfault/UB risk on this hot path.🐛 Proposed fix using the new `clone_atom()` helper
Atom* atom = atom_trie_value->get_atom(); - if (Atom::is_node(*atom)) { - matching_atoms.push_back(shared_ptr<Node>(dynamic_cast<Node*>(atom))); - } else { - matching_atoms.push_back(shared_ptr<Link>(dynamic_cast<Link*>(atom))); - } + auto cloned = clone_atom(atom); + if (cloned != nullptr) { + matching_atoms.push_back(cloned); + } return matching_atoms;🤖 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 190 - 208, Update InMemoryDB::get_matching_atoms to return deep clones of the trie-owned atom using the existing clone_atom() helper instead of constructing shared_ptr<Node> or shared_ptr<Link> from the raw Atom*. Preserve the node/link result behavior while ensuring the returned pointers have independent ownership from AtomTrieValue.src/tests/cpp/inmemorydb_test.cc (1)
546-737: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy liftNo concurrency test for the newly synchronized
InMemoryDB.All new tests (
GetAllAtoms,DropAll*,UpsertReplacesCustomAttributes) are single-threaded. The PR's stated motivation is fixing segfaults from concurrent races on this exact class, and the header now documents it as thread-safe — but nothing here exercises multiple threads callingadd_link/get_atom/drop_all/query_for_patternconcurrently to validate that claim. As per path instructions, tests here should prioritize "thread/proxy interactions" for behavior changes like this.Want me to draft a stress test that spawns several threads doing concurrent
add_link/get_atom/delete_atomagainst a sharedInMemoryDBand asserts no crash/inconsistent state underThreadSanitizer?🤖 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 546 - 737, Extend the InMemoryDB tests with a multithreaded stress test that shares one database across threads and concurrently exercises add_link, get_atom, drop_all or delete_atom, and query_for_pattern. Synchronize thread start and completion, run enough iterations to expose races, and assert only safe post-join invariants such as a valid atom count or empty state; ensure the test is suitable for ThreadSanitizer and does not dereference results invalidated by concurrent drop_all.Source: Path instructions
🧹 Nitpick comments (2)
src/atomdb/inmemorydb/InMemoryDB.h (1)
20-27: 🚀 Performance & Scalability | 🔵 TrivialSingle recursive mutex serializes all readers and writers.
Every public method — including pure reads like
get_atom/query_for_pattern— now contends on the samerecursive_mutex. This is a reasonable, safe trade-off for reentrancy (methods call each other), but under heavy concurrent pattern-query load it removes any read/read parallelism that existed before. Worth keeping an eye on if this becomes a throughput bottleneck; ashared_mutexisn't easily reentrant-safe for the current call patterns, so this isn't a quick swap.Also applies to: 89-91
🤖 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.h` around lines 20 - 27, Keep the single internal recursive mutex and its serialization of all public InMemoryDB methods, including read operations such as get_atom and query_for_pattern; no code change is required for this advisory performance observation.src/atomdb/inmemorydb/InMemoryDB.cc (1)
462-513: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winStale comment: "Release locks before calling delete_atom to avoid deadlock" no longer reflects the locking model.
With the new
recursive_mutexheld vialock_guardat function entry (line 463), no lock is actually released before the recursive call intodelete_atom— the comment predates the new locking and is now misleading about how re-entrancy is achieved.✏️ Suggested comment update
- // Release locks before calling delete_atom to avoid deadlock - // Delete targets that have no other incoming links + // Safe to re-enter: api_mutex_ is recursive, so delete_atom can be called + // while this lock is still held by the current thread. + // Delete targets that have no other incoming links🤖 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 462 - 513, Update the comment immediately before the targets_to_delete loop in InMemoryDB::delete_link to accurately describe the current recursive_mutex-based locking behavior; remove the claim that locks are released before delete_atom, while preserving the existing recursive deletion logic.
🤖 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/inmemorydb/InMemoryDB.h`:
- Around line 84-85: Add brief Doxygen blocks immediately above the public
InMemoryDB methods get_all_atoms() and drop_all() in the header, documenting
each API’s purpose according to the project’s existing style.
---
Outside diff comments:
In `@src/atomdb/inmemorydb/InMemoryDB.cc`:
- Around line 190-208: Update InMemoryDB::get_matching_atoms to return deep
clones of the trie-owned atom using the existing clone_atom() helper instead of
constructing shared_ptr<Node> or shared_ptr<Link> from the raw Atom*. Preserve
the node/link result behavior while ensuring the returned pointers have
independent ownership from AtomTrieValue.
In `@src/atomdb/remotedb/RemoteAtomDB.cc`:
- Around line 415-440: Add a multi-peer batch-delete test in
remote_atomdb_test.cc covering delete_atoms, delete_nodes, and delete_links
through RemoteAtomDB with multiple replicated peers. Assert that each method
returns the maximum deletion count reported by any peer, preserving the existing
max-based semantics.
In `@src/tests/cpp/inmemorydb_test.cc`:
- Around line 546-737: Extend the InMemoryDB tests with a multithreaded stress
test that shares one database across threads and concurrently exercises
add_link, get_atom, drop_all or delete_atom, and query_for_pattern. Synchronize
thread start and completion, run enough iterations to expose races, and assert
only safe post-join invariants such as a valid atom count or empty state; ensure
the test is suitable for ThreadSanitizer and does not dereference results
invalidated by concurrent drop_all.
---
Nitpick comments:
In `@src/atomdb/inmemorydb/InMemoryDB.cc`:
- Around line 462-513: Update the comment immediately before the
targets_to_delete loop in InMemoryDB::delete_link to accurately describe the
current recursive_mutex-based locking behavior; remove the claim that locks are
released before delete_atom, while preserving the existing recursive deletion
logic.
In `@src/atomdb/inmemorydb/InMemoryDB.h`:
- Around line 20-27: Keep the single internal recursive mutex and its
serialization of all public InMemoryDB methods, including read operations such
as get_atom and query_for_pattern; no code change is required for this advisory
performance observation.
🪄 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: 793c694b-19d0-449d-9f26-da5f0f483739
📒 Files selected for processing (14)
config/das.jsonsrc/atomdb/inmemorydb/InMemoryDB.ccsrc/atomdb/inmemorydb/InMemoryDB.hsrc/atomdb/redis_mongodb/RedisMongoDB.ccsrc/atomdb/remotedb/RemoteAtomDB.ccsrc/atomdb/remotedb/RemoteAtomDB.hsrc/atomdb/remotedb/RemoteAtomDBPeer.ccsrc/atomdb/remotedb/RemoteAtomDBPeer.hsrc/tests/assets/remotedb_config_single.jsonsrc/tests/cpp/BUILDsrc/tests/cpp/inmemorydb_test.ccsrc/tests/cpp/redis_mongodb_test.ccsrc/tests/cpp/remote_atomdb_test.ccsrc/tests/main/evaluation_evolution.cc
| vector<shared_ptr<Atom>> get_all_atoms(); | ||
| void drop_all(); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Add brief Doxygen blocks for the new public get_all_atoms() / drop_all() APIs.
As per coding guidelines, src/**/*.h files should "Use brief Doxygen /** ... */ blocks above public API methods in C++ header files." Neither new method has one.
+ /** Returns deep clones of every stored atom. */
vector<shared_ptr<Atom>> get_all_atoms();
+ /** Removes all stored atoms and resets all indexes. */
void drop_all();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| vector<shared_ptr<Atom>> get_all_atoms(); | |
| void drop_all(); | |
| /** Returns deep clones of every stored atom. */ | |
| vector<shared_ptr<Atom>> get_all_atoms(); | |
| /** Removes all stored atoms and resets all indexes. */ | |
| void drop_all(); |
🤖 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.h` around lines 84 - 85, Add brief Doxygen
blocks immediately above the public InMemoryDB methods get_all_atoms() and
drop_all() in the header, documenting each API’s purpose according to the
project’s existing style.
Sources: Coding guidelines, Path instructions
| } | ||
|
|
||
| static void flush_remote_link_template_cache(bool force = false) { | ||
| if (auto remote_db = dynamic_pointer_cast<RemoteAtomDB>(db); remote_db != nullptr) { |
There was a problem hiding this comment.
| if (auto remote_db = dynamic_pointer_cast<RemoteAtomDB>(db); remote_db != nullptr) { | |
| if (auto remote_db = dynamic_pointer_cast<RemoteAtomDB>(db), remote_db != nullptr) { |
| if (atom == nullptr) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| if (atom->arity() == 0) { | ||
| auto node = dynamic_cast<const Node*>(atom); | ||
| return (node != nullptr) ? make_shared<Node>(*node) : nullptr; | ||
| } | ||
|
|
||
| auto link = dynamic_cast<const Link*>(atom); | ||
| return (link != nullptr) ? make_shared<Link>(*link) : nullptr; |
There was a problem hiding this comment.
| if (atom == nullptr) { | |
| return nullptr; | |
| } | |
| if (atom->arity() == 0) { | |
| auto node = dynamic_cast<const Node*>(atom); | |
| return (node != nullptr) ? make_shared<Node>(*node) : nullptr; | |
| } | |
| auto link = dynamic_cast<const Link*>(atom); | |
| return (link != nullptr) ? make_shared<Link>(*link) : nullptr; | |
| auto link = dynamic_cast<const Link*>(atom); | |
| if (link != nullptr) { | |
| return make_shared<Link>(*link); | |
| } | |
| auto node = dynamic_cast<const Node*>(atom); | |
| if (node != nullptr) { | |
| return make_shared<Node>(*node); | |
| } | |
| return nullptr; |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/atomdb/remotedb/RemoteAtomDB.cc (1)
165-179: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
get_matching_atomskeeps the first peer's copy, whileget_atomprefers writable peers.
get_atomwas changed to probewritable_peers_first, because the writable peer holds the authoritative copy of a content-addressed handle whose custom attributes changed.StrengthUpdateVisibleAcrossPeerslocks that behavior in.This method still iterates
remote_db_in map order and keeps the first atom seen for each handle. A readonly peer whose uid sorts before the writable peer returns the stale copy, and theseenset then rejects the fresh one. The two read paths disagree for the same handle.Iterate
writable_peers_first, thenreadonly_peers_, so the precedence matchesget_atom.🤖 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 165 - 179, Update RemoteAtomDB::get_matching_atoms to iterate writable_peers_ before readonly_peers_, preserving the existing handle-based deduplication so writable-peer atoms are retained and readonly duplicates are ignored. Align its peer precedence with get_atom without changing the matching or result behavior.src/atomdb/inmemorydb/InMemoryDB.cc (1)
375-420: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winConfirm that
is_newgating cannot skip required index entries.Index building now runs only when the handle is absent from
atoms_trie_. This is correct while every link insertion path passes throughadd_links_unlockedand every deletion path removes the pattern and incoming-set entries together with the atom.delete_link_unlockeddoes both, so the invariant holds today.One case deserves a test: a link that is deleted and then re-added in the same
InMemoryDBinstance must regain its incoming-set and pattern entries. A second case:re_index_patterns(false)followed byadd_linkson an existing handle must not silently lose entries. As per coding guidelines, test updates are required when production code changes behavior. Please add these tosrc/tests/cpp/inmemorydb_test.cc.🤖 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 375 - 420, Add regression tests in inmemorydb_test.cc covering re-adding a deleted link and verifying its incoming-set and pattern entries are restored, plus calling re_index_patterns(false) followed by add_links for an existing handle and verifying required index entries remain present. Use the existing InMemoryDB test helpers and assert both scenarios preserve indexing behavior.Source: Coding guidelines
🧹 Nitpick comments (6)
src/tests/cpp/redis_mongodb_test.cc (1)
1171-1177: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReuse
test_atomdb_json_config()instead of rebuilding the config inline.
CompositeTypeEnabledFlagat lines 1252-1254 already builds a disabled-composite database withtest_atomdb_json_config(). This test duplicates the endpoints, credentials, and type fields by hand. If the shared fixture endpoints change, this test breaks alone.♻️ Proposed reuse of the shared config helper
- auto json = nlohmann::json(); - json["type"] = "redismongodb"; - json["composite_type_enabled"] = false; - json["redis"] = {{"endpoint", "localhost:40020"}, {"cluster", false}}; - json["mongodb"] = {{"endpoint", "localhost:40021"}, {"username", "admin"}, {"password", "admin"}}; - auto local_db = make_shared<RedisMongoDB>("test_", false, commons::JsonConfig(json)); + auto config_disabled = test_atomdb_json_config(); + config_disabled["composite_type_enabled"] = false; + auto local_db = make_shared<RedisMongoDB>("test_", false, config_disabled);Note: the hardcoded ports themselves are fine. Based on learnings, hardcoded ports in
src/tests/cpp/are an intentional team convention.🤖 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/redis_mongodb_test.cc` around lines 1171 - 1177, Update the test setup around RedisMongoDB construction to reuse the existing test_atomdb_json_config() helper instead of assembling the nlohmann::json configuration inline. Preserve the disabled composite_type_enabled setting and existing test behavior while relying on the shared fixture for type, endpoints, and credentials.Source: Learnings
src/atomdb/inmemorydb/InMemoryDB.cc (1)
573-576: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the leftover reasoning from this comment.
The comment describes an approach that was considered and rejected ("We temporarily expose the unlocked path by having the public methods detect nested use... Instead, inline:"). It describes code that does not exist. Keep only the fact that matters: the visitor must use the unlocked helpers because
write_mutex_is not recursive.♻️ Proposed comment cleanup
- // re_index_visitor calls the public add_pattern / match_pattern_index_schema locking - // wrappers — which would deadlock on a plain mutex. Use unlocked helpers via a thin - // visitor that calls a free function taking InMemoryDB*. We temporarily expose the - // unlocked path by having the public methods detect nested use... Instead, inline: + // write_mutex_ is not recursive, so the visitor must call the *_unlocked helpers + // instead of the public locking wrappers.🤖 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 573 - 576, Remove the discarded-approach text from the comment near re_index_visitor. Keep only that the visitor must call unlocked helpers because write_mutex_ is non-recursive.src/atomdb/remotedb/RemoteAtomDB.h (1)
87-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a brief Doxygen block to
release_caches.
release_cachesis new public API and its two boolean parameters carry non-obvious semantics.persistselects flush-versus-drop andforceoverrides the "schema not cached" no-op path inRemoteAtomDBPeer::release. A caller cannot infer that from the signature. The class doc and the dependency-injection constructor in this header already use/** ... */; follow the same pattern here.As per coding guidelines: "Use brief Doxygen
/** ... */blocks above public API methods in C++ header files".🤖 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.h` around lines 87 - 88, Add a brief Doxygen block immediately above the public RemoteAtomDB::release_caches declaration, documenting the link_schema argument and clarifying that persist selects flushing versus dropping caches while force overrides the no-op path when the schema is not cached. Follow the existing /** ... */ documentation style in the header.Source: Coding guidelines
src/atomdb/remotedb/RemoteAtomDB.cc (1)
320-385: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winThe write fan-out ignores
writable_peers_and copies the handle vector.Two points.
First, the comment on line 321 states that writes only land on writable peers, but every one of these six methods iterates
remote_db_. Each readonly peer is still called and each one returns an empty result from its internalis_readonly()gate.finalize_peer_listsbuildswritable_peers_exactly so hot paths avoid this check, and no write path uses it. Iteratewritable_peers_here.Second,
handles = peer_handles;copies the whole vector for every peer that returns a non-empty result. Move it instead.♻️ Proposed change, shown for `add_atoms`; apply the same shape to the other five
vector<string> RemoteAtomDB::add_atoms(const vector<atoms::Atom*>& atoms, bool is_transactional, const atoms::Merger* merger) { vector<string> handles; - for (auto& [uid, peer] : remote_db_) { + for (auto& [uid, peer] : writable_peers_) { LOG_DEBUG("add_atoms(" << atoms.size() << ") to peer [" << uid << "]"); auto peer_handles = peer->add_atoms(atoms, is_transactional, merger); - if (!peer_handles.empty()) handles = peer_handles; + if (!peer_handles.empty()) handles = std::move(peer_handles); } return 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/remotedb/RemoteAtomDB.cc` around lines 320 - 385, Update add_atom, add_node, add_link, add_atoms, add_nodes, and add_links to iterate writable_peers_ instead of remote_db_, preserving the existing logging and write calls while avoiding readonly peers. In the vector-returning methods, replace the non-empty result assignment with a move from peer_handles so the final handle vector is transferred rather than copied.Source: Path instructions
src/tests/cpp/remote_atomdb_test.cc (1)
1091-1112: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe failed-flush test only covers the node path.
FailedFlushRestagesDirtyAtomsstages one node.RemoteAtomDBPeer::persist_atoms_to_localhandles links through a separate path: dependency discovery throughatomdb_->get_atom, thecomposite_type_enabled()branch, and the batch loop that logs "dropping N links with unresolved targets" and abandons those writes. None of that runs in this test.The link drop branch loses data silently. Add a case that stages a link whose targets exist on no reachable backend and assert the resulting state. Add a case where the flush of links throws and assert that the link is re-staged.
I can write both cases if you want them. Do you want me to open an issue to track this?
🤖 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 1091 - 1112, Extend FailedFlushRestagesDirtyAtoms with coverage for links: add a case staging a link whose targets are absent from every reachable backend and assert the expected resulting state, including the unresolved-target batch path; add another case using a failing local backend, assert the link remains cached after release_cache throws, then restore the backend and verify the re-staged link persists. Exercise RemoteAtomDBPeer::persist_atoms_to_local and preserve the existing node assertions.Source: Path instructions
src/atomdb/remotedb/RemoteAtomDBPeer.cc (1)
705-710: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winThe quiescence wait is an unbounded spin loop.
while (old_write_buffer.use_count() > 1) this_thread::yield();burns a full core while any writer still holds the retired buffer.InMemoryDB::add_atomstakeswrite_mutex_and can hold the snapshot for the whole batch, so a large bulk insert makes this spin for the duration of that batch.auto_cleanup()callsrelease_cache()from the cleanup thread, so the spin can run on a background thread under memory pressure, which is the worst moment to consume CPU.The ownership reasoning is sound. Add a backoff so the wait yields the core instead of spinning hot.
♻️ Proposed backoff
- while (old_write_buffer.use_count() > 1) { - this_thread::yield(); - } + unsigned int spins = 0; + while (old_write_buffer.use_count() > 1) { + if (++spins < 64) { + this_thread::yield(); + } else { + this_thread::sleep_for(chrono::microseconds(100)); + } + }This needs
#include <chrono>in this file.🤖 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/RemoteAtomDBPeer.cc` around lines 705 - 710, Update the quiescence loop around old_write_buffer in the cleanup path to add a timed backoff between use_count() checks, using the proposed chrono-based sleep and adding the required <chrono> include. Preserve the existing ownership condition and flush behavior once the buffer becomes solely owned.
🤖 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 `@config/das.json`:
- Line 3: Document the breaking atomdb.type change from redismongodb to remotedb
and the required peer2/peer3 database endpoints in the migration notes, or add
provisioning for ports 40030–40032 and 40040–40042. Preserve the existing config
keys and JSON structure, and ensure a fresh deployment can start against the new
federation topology.
In `@src/atomdb/inmemorydb/InMemoryDB.cc`:
- Around line 609-613: Update InMemoryDB::match_pattern_index_schema() to
acquire write_mutex_ before delegating to match_pattern_index_schema_unlocked(),
ensuring pattern_index_schema_map is protected while being iterated. Preserve
the unlocked variant for callers that already hold the mutation lock, and revise
the stale safety comment accordingly.
- Around line 560-571: Prevent use-after-free during trie replacement by
updating src/atomdb/inmemorydb/InMemoryDB.cc:560-571 in drop_all() and
re_index_patterns() to use a shared_mutex model with unique locking for
reset_trie operations, or clear trie contents in place while preserving trie
object lifetimes; update src/atomdb/inmemorydb/InMemoryDB.h:23-30 to document
the implemented reader/writer locking behavior and the effects of drop_all() and
re_index_patterns().
In `@src/atomdb/remotedb/RemoteAtomDB.cc`:
- Around line 115-124: Change the log statement in
RemoteAtomDB::composite_type_enabled() so this normal derivation message is not
emitted at ERROR level; remove it or use the project’s appropriate non-error
logging level while preserving the peer iteration and return behavior.
In `@src/atomdb/remotedb/RemoteAtomDBPeer.cc`:
- Around line 463-497: In src/atomdb/remotedb/RemoteAtomDBPeer.cc:463-497,
update delete_atoms, delete_nodes, and delete_links to return the number of
distinct handles removed from at least one layer rather than summing overlapping
per-layer counts. In src/atomdb/remotedb/RemoteAtomDBPeer.cc:509-531, update
node_count, link_count, and atom_count to use one documented layer as the peer
population source instead of adding read-cache, write-buffer, and
local-persistence counts. Extend AtomsCount to perform a read that warms the
cache and verify the count remains correct.
- Around line 68-80: Update get_atom so local_persistence_ lookups do not
redundantly warm read_cache_ on every hit. Preserve the
NonStagedPrefersLocalOverStaleCache lookup order and local result behavior, but
skip rc->add_atom(atom.get()) when the cache already contains the handle, or
remove this local-to-cache warming entirely.
In `@src/atomdb/remotedb/RemoteAtomDBPeer.h`:
- Around line 140-142: Update the comment above peer_mutex_ to remove the claim
that it is never held across InMemoryDB calls. Document that release_cache() and
release() may hold peer_mutex_ while calling atom_count() on write_buffer_ or
read_cache_, while preserving the invariant that InMemoryDB does not acquire
peer_mutex_ and avoiding claims about local_persistence_ or atomdb_ I/O unless
they remain accurate.
In `@src/tests/cpp/remote_atomdb_test.cc`:
- Around line 1128-1139: The writer worker launched in the test should not call
ASSERT_FALSE directly. Record failures from peer->add_node in an atomic failure
counter accessible to each worker, allow all threads to finish, then assert the
counter is zero after joining the writer and releaser threads.
In `@src/tests/scripts/run_toy.sh`:
- Around line 19-32: Restore the database setup and knowledge-base loading steps
in run_toy.sh, including das-cli db start and make run-db-loader using the $KB
argument, so the script provisions the remotedb peers defined by
config/das.json. If setup must remain external, remove the disabled block and
add an explicit comment documenting the required running, preloaded database
precondition; otherwise ensure $KB is validated or consumed.
---
Outside diff comments:
In `@src/atomdb/inmemorydb/InMemoryDB.cc`:
- Around line 375-420: Add regression tests in inmemorydb_test.cc covering
re-adding a deleted link and verifying its incoming-set and pattern entries are
restored, plus calling re_index_patterns(false) followed by add_links for an
existing handle and verifying required index entries remain present. Use the
existing InMemoryDB test helpers and assert both scenarios preserve indexing
behavior.
In `@src/atomdb/remotedb/RemoteAtomDB.cc`:
- Around line 165-179: Update RemoteAtomDB::get_matching_atoms to iterate
writable_peers_ before readonly_peers_, preserving the existing handle-based
deduplication so writable-peer atoms are retained and readonly duplicates are
ignored. Align its peer precedence with get_atom without changing the matching
or result behavior.
---
Nitpick comments:
In `@src/atomdb/inmemorydb/InMemoryDB.cc`:
- Around line 573-576: Remove the discarded-approach text from the comment near
re_index_visitor. Keep only that the visitor must call unlocked helpers because
write_mutex_ is non-recursive.
In `@src/atomdb/remotedb/RemoteAtomDB.cc`:
- Around line 320-385: Update add_atom, add_node, add_link, add_atoms,
add_nodes, and add_links to iterate writable_peers_ instead of remote_db_,
preserving the existing logging and write calls while avoiding readonly peers.
In the vector-returning methods, replace the non-empty result assignment with a
move from peer_handles so the final handle vector is transferred rather than
copied.
In `@src/atomdb/remotedb/RemoteAtomDB.h`:
- Around line 87-88: Add a brief Doxygen block immediately above the public
RemoteAtomDB::release_caches declaration, documenting the link_schema argument
and clarifying that persist selects flushing versus dropping caches while force
overrides the no-op path when the schema is not cached. Follow the existing /**
... */ documentation style in the header.
In `@src/atomdb/remotedb/RemoteAtomDBPeer.cc`:
- Around line 705-710: Update the quiescence loop around old_write_buffer in the
cleanup path to add a timed backoff between use_count() checks, using the
proposed chrono-based sleep and adding the required <chrono> include. Preserve
the existing ownership condition and flush behavior once the buffer becomes
solely owned.
In `@src/tests/cpp/redis_mongodb_test.cc`:
- Around line 1171-1177: Update the test setup around RedisMongoDB construction
to reuse the existing test_atomdb_json_config() helper instead of assembling the
nlohmann::json configuration inline. Preserve the disabled
composite_type_enabled setting and existing test behavior while relying on the
shared fixture for type, endpoints, and credentials.
In `@src/tests/cpp/remote_atomdb_test.cc`:
- Around line 1091-1112: Extend FailedFlushRestagesDirtyAtoms with coverage for
links: add a case staging a link whose targets are absent from every reachable
backend and assert the expected resulting state, including the unresolved-target
batch path; add another case using a failing local backend, assert the link
remains cached after release_cache throws, then restore the backend and verify
the re-staged link persists. Exercise RemoteAtomDBPeer::persist_atoms_to_local
and preserve the existing node assertions.
🪄 Autofix
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: 42eac09d-4f50-4b94-ba3b-d99c5b2994d4
📒 Files selected for processing (15)
config/das.jsonsrc/atomdb/inmemorydb/InMemoryDB.ccsrc/atomdb/inmemorydb/InMemoryDB.hsrc/atomdb/redis_mongodb/RedisMongoDB.ccsrc/atomdb/remotedb/RemoteAtomDB.ccsrc/atomdb/remotedb/RemoteAtomDB.hsrc/atomdb/remotedb/RemoteAtomDBPeer.ccsrc/atomdb/remotedb/RemoteAtomDBPeer.hsrc/main/bus_client.ccsrc/tests/cpp/BUILDsrc/tests/cpp/inmemorydb_test.ccsrc/tests/cpp/redis_mongodb_test.ccsrc/tests/cpp/remote_atomdb_test.ccsrc/tests/main/evaluation_evolution.ccsrc/tests/scripts/run_toy.sh
🚧 Files skipped from review as they are similar to previous changes (4)
- src/tests/cpp/BUILD
- src/tests/main/evaluation_evolution.cc
- src/atomdb/redis_mongodb/RedisMongoDB.cc
- src/tests/cpp/inmemorydb_test.cc
Summary
Makes RemoteAtomDB safe under concurrent pattern queries (e.g.
run_toy.shAndNot / multiple LinkTemplates), which previously segfaulted (exit 139) due to races on the peer cache.InMemoryDB thread-safety
recursive_mutexand take it on every public API method.delete_link→delete_atom, re-index visitor →add_pattern).InMemoryDBacross threads without external locking.RemoteAtomDBPeer locking (kept simple)
InMemoryDBowns that.peer_mutex_only guards peer bookkeeping:cache_pointer swap,fetched_link_templates_, andstaged_handles_.cache()snapshot (shared_ptr); writes holdpeer_mutex_so staged handles stay consistent with the cache generation.release_cachenever drops staged writes, even on a “clear without persistence” path.Test plan
//tests/cpp:inmemorydb_test//tests/cpp:remote_atomdb_testsrc/tests/scripts/run_toy.shwith RemoteDB peers (concurrent LinkTemplates)