diff --git a/src/atomdb/AtomDB.h b/src/atomdb/AtomDB.h index babcc9135..2485d45fe 100644 --- a/src/atomdb/AtomDB.h +++ b/src/atomdb/AtomDB.h @@ -16,11 +16,39 @@ using namespace atoms; namespace atomdb { +enum class AtomDBType { RedisMongoDB, MorkDB, InMemoryDB, RemoteAtomDB, AdapterDB }; + class AtomDB : public HandleDecoder { public: AtomDB() = default; virtual ~AtomDB() = default; + static AtomDBType string_to_type(const string& type) { + if (type == "redismongodb") return AtomDBType::RedisMongoDB; + if (type == "morkdb") return AtomDBType::MorkDB; + if (type == "inmemorydb") return AtomDBType::InMemoryDB; + if (type == "remotedb") return AtomDBType::RemoteAtomDB; + if (type == "adapterdb") return AtomDBType::AdapterDB; + RAISE_ERROR("Unsupported atomdb.type: " + type); + } + + static string type_to_string(AtomDBType type) { + switch (type) { + case AtomDBType::RedisMongoDB: + return "redismongodb"; + case AtomDBType::MorkDB: + return "morkdb"; + case AtomDBType::InMemoryDB: + return "inmemorydb"; + case AtomDBType::RemoteAtomDB: + return "remotedb"; + case AtomDBType::AdapterDB: + return "adapterdb"; + default: + RAISE_ERROR("Unsupported AtomDBType"); + } + } + virtual bool allow_nested_indexing() = 0; virtual bool composite_type_enabled() const = 0; diff --git a/src/atomdb/AtomDBFactory.cc b/src/atomdb/AtomDBFactory.cc new file mode 100644 index 000000000..a4cbb03e6 --- /dev/null +++ b/src/atomdb/AtomDBFactory.cc @@ -0,0 +1,116 @@ +#include "AtomDBFactory.h" + +#include "AdapterDB.h" +#include "InMemoryDB.h" +#include "MorkDB.h" +#include "RedisMongoDB.h" +#include "RemoteAtomDB.h" +#include "Utils.h" + +using namespace atomdb; +using namespace commons; + +// -------------------------------------------------------------------------------- +// Public methods + +shared_ptr AtomDBFactory::create(const JsonConfig& config, const string& context) { + auto atomdb_type = config.at_path("type").get_or(""); + + AtomDBType type = AtomDB::string_to_type(atomdb_type); + + shared_ptr atomdb; + + if (type == AtomDBType::RedisMongoDB || type == AtomDBType::MorkDB || + type == AtomDBType::InMemoryDB) { + atomdb = create_basic_atomdb(config, context); + } else if (type == AtomDBType::RemoteAtomDB || type == AtomDBType::AdapterDB) { + atomdb = create_composite_atomdb(config, context); + } else { + RAISE_ERROR("AtomDBFactory: unsupported AtomDB type: " + atomdb_type); + } + + return wrap_if_protected(atomdb); +} + +// -------------------------------------------------------------------------------- +// Private methods + +shared_ptr AtomDBFactory::create_basic_atomdb(const JsonConfig& config, const string& context) { + auto atomdb_type = config.at_path("type").get_or(""); + + AtomDBType type = AtomDB::string_to_type(atomdb_type); + + shared_ptr atomdb; + + if (type == AtomDBType::RedisMongoDB) { + // make_shared cannot access RedisMongoDB's private ctor; friend can via new. + atomdb = shared_ptr(new RedisMongoDB(context, false, config)); + } else if (type == AtomDBType::MorkDB) { + atomdb = make_shared(context, config); + } else if (type == AtomDBType::InMemoryDB) { + atomdb = make_shared(context.empty() ? "inmemorydb_" : context); + } else { + RAISE_ERROR("AtomDBFactory: '" + atomdb_type + "' is not a basic AtomDB type"); + } + + return atomdb; +} + +shared_ptr AtomDBFactory::create_composite_atomdb(const JsonConfig& config, + const string& context) { + auto atomdb_type = config.at_path("type").get_or(""); + + AtomDBType type = AtomDB::string_to_type(atomdb_type); + + shared_ptr atomdb; + + if (type == AtomDBType::RemoteAtomDB) { + auto remote_peers_config = config.at_path("remote_peers").get_or(JsonConfig()); + + map> remote_peers; + + for (auto& entry : remote_peers_config) { + auto peer_config = JsonConfig(entry); + string uid = peer_config.at_path("uid").get_or(""); + if (uid.empty()) { + RAISE_ERROR("AtomDBFactory: remote peer is missing a non-empty uid"); + } + + string peer_context = peer_config.at_path("context").get_or(""); + if (peer_context.empty()) { + peer_context = "remotedb_" + uid; + } + + shared_ptr local_persistence = nullptr; + auto local_persistence_config = + peer_config.at_path("local_persistence").get_or(JsonConfig()); + if (!local_persistence_config.empty()) { + string local_context = + local_persistence_config.at_path("context").get_or(peer_context); + if (local_context.empty()) { + local_context = peer_context; + } + local_persistence = create_basic_atomdb(local_persistence_config, local_context); + } + remote_peers[uid] = make_shared( + create_basic_atomdb(peer_config, peer_context), local_persistence, uid); + } + + atomdb = make_shared(remote_peers); + } else if (type == AtomDBType::AdapterDB) { + // The backend AtomDB in AdapterDB could be RemoteAtomDB ? + auto atomdb_backend_config = + config.at_path("adapterdb.atomdb_backend").get_or(JsonConfig()); + auto basic_atomdb = create_basic_atomdb(atomdb_backend_config, context); + atomdb = make_shared(config, basic_atomdb); + } else { + RAISE_ERROR("AtomDBFactory: '" + atomdb_type + "' is not a composite AtomDB type"); + } + + return atomdb; +} + +shared_ptr AtomDBFactory::wrap_if_protected(shared_ptr atomdb) { + // AtomDBFactory::wrap_if_protected() is not implemented yet. + return atomdb; +} \ No newline at end of file diff --git a/src/atomdb/AtomDBFactory.h b/src/atomdb/AtomDBFactory.h new file mode 100644 index 000000000..7f3b663b1 --- /dev/null +++ b/src/atomdb/AtomDBFactory.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include "AtomDB.h" +#include "JsonConfig.h" + +using namespace std; +using namespace commons; + +namespace atomdb { + +/** + * @brief Factory that builds AtomDB instances from a JsonConfig. + * + * This is the preferred way to obtain an AtomDB. Callers should not construct + * RedisMongoDB, MorkDB, InMemoryDB, RemoteAtomDB, or AdapterDB directly; instead + * pass a config whose "type" field selects the concrete implementation. + * + * Two kinds of AtomDB are supported: + * - Basic: RedisMongoDB, MorkDB, InMemoryDB — constructed from their own config. + * - Composite: RemoteAtomDB and AdapterDB — built by composing one or more basic + * AtomDBs (remote peers for RemoteAtomDB; a wrapped AtomDB for AdapterDB). + * + */ +class AtomDBFactory { + public: + /** + * @brief Creates a AtomDB and wraps it with ProtectedAtomDB when is applyable. + */ + static shared_ptr create(const JsonConfig& config, const string& context = ""); + + private: + // Supported types: redismongodb, morkdb, inmemorydb. + static shared_ptr create_basic_atomdb(const JsonConfig& config, const string& context = ""); + + // Supported types: remotedb, adapterdb. + static shared_ptr create_composite_atomdb(const JsonConfig& config, + const string& context = ""); + + /** + * @brief Wraps an AtomDB with ProtectedAtomDB when protected and not already wrapped. + */ + static shared_ptr wrap_if_protected(shared_ptr atomdb); +}; + +} // namespace atomdb diff --git a/src/atomdb/AtomDBSingleton.cc b/src/atomdb/AtomDBSingleton.cc index 37c85a08a..4f83b22ed 100644 --- a/src/atomdb/AtomDBSingleton.cc +++ b/src/atomdb/AtomDBSingleton.cc @@ -1,9 +1,6 @@ #include "AtomDBSingleton.h" -#include "AdapterDB.h" -#include "MorkDB.h" -#include "RedisMongoDB.h" -#include "RemoteAtomDB.h" +#include "AtomDBFactory.h" #include "Utils.h" using namespace atomdb; @@ -19,24 +16,9 @@ void AtomDBSingleton::init(const JsonConfig& atomdb_config) { if (AtomDBSingleton::initialized) { RAISE_ERROR( "AtomDBSingleton already initialized. AtomDBSingleton::init() should be called only once."); - } else { - auto atomdb_type = atomdb_config.at_path("type").get_or(""); - if (atomdb_type == "morkdb") { - AtomDBSingleton::atom_db = shared_ptr(new MorkDB("", atomdb_config)); - } else if (atomdb_type == "redismongodb") { - AtomDBSingleton::atom_db = shared_ptr(new RedisMongoDB("", false, atomdb_config)); - } else if (atomdb_type == "remotedb") { - auto remote_peers_config = - atomdb_config.at_path("remote_peers").get_or(JsonConfig()); - AtomDBSingleton::atom_db = shared_ptr(new RemoteAtomDB(remote_peers_config)); - } else if (atomdb_type == "adapterdb") { - AtomDBSingleton::atom_db = shared_ptr(new AdapterDB(atomdb_config)); - } else { - RAISE_ERROR("Invalid AtomDB type: " + atomdb_type); - } - - AtomDBSingleton::initialized = true; } + AtomDBSingleton::atom_db = AtomDBFactory::create(atomdb_config); + AtomDBSingleton::initialized = true; } shared_ptr AtomDBSingleton::get_instance() { diff --git a/src/atomdb/BUILD b/src/atomdb/BUILD index 68b602e48..ed66ba36c 100644 --- a/src/atomdb/BUILD +++ b/src/atomdb/BUILD @@ -8,13 +8,25 @@ cc_library( deps = [ ":atomdb", ":atomdb_api_types", + ":atomdb_factory", ":atomdb_singleton", ":atomdbutils", + ], +) + +cc_library( + name = "atomdb_factory", + srcs = ["AtomDBFactory.cc"], + hdrs = ["AtomDBFactory.h"], + includes = ["."], + deps = [ + ":atomdb", "//atomdb/adapterdb:adapterdb_lib", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/morkdb:morkdb_lib", "//atomdb/redis_mongodb:redis_mongodb_lib", "//atomdb/remotedb:remotedb_lib", + "//commons:commons_lib", ], ) @@ -56,11 +68,8 @@ cc_library( hdrs = ["AtomDBSingleton.h"], includes = ["."], deps = [ + ":atomdb_factory", "//atomdb:atomdb_api_types", - "//atomdb/adapterdb", - "//atomdb/morkdb", - "//atomdb/redis_mongodb", - "//atomdb/remotedb:remotedb_lib", "//commons:commons_lib", ], ) diff --git a/src/atomdb/adapterdb/AdapterDB.cc b/src/atomdb/adapterdb/AdapterDB.cc index 8b8d4916e..ded690ed0 100644 --- a/src/atomdb/adapterdb/AdapterDB.cc +++ b/src/atomdb/adapterdb/AdapterDB.cc @@ -8,13 +8,10 @@ #include "DatabaseOrchestrator.h" #include "DedicatedThread.h" #include "MongoInitializer.h" -#include "MorkDB.h" #include "MorkMappingStrategy.h" #include "PostgresMappingStrategy.h" #include "PostgresWrapper.h" #include "Processor.h" -#include "RedisMongoDB.h" -#include "RemoteAtomDB.h" #include "Utils.h" #include "expression_hasher.h" #include "processor/ThreadPool.h" @@ -33,11 +30,6 @@ string AdapterDB::MONGODB_ADAPTER_COLLECTION_NAME = "adapterdb"; // Construction / destruction // ============================== -AdapterDB::AdapterDB(const JsonConfig& config) : config(config) { - this->atomdb_backend_setup(); - this->initialize(); -} - atomdb::AdapterDB::AdapterDB(const JsonConfig& config, std::shared_ptr backend) : config(config), atomdb_backend(backend) { this->initialize(true); @@ -310,20 +302,20 @@ void AdapterDB::persistence_setup() { } } -void AdapterDB::atomdb_backend_setup() { - auto atomdb_backend_config = - this->config.at_path("adapterdb.atomdb_backend").get_or(JsonConfig()); - string atomdb_backend_type = atomdb_backend_config.at_path("type").get_or(""); - if (atomdb_backend_type == "morkdb") { - this->atomdb_backend = shared_ptr(new MorkDB("", atomdb_backend_config)); - } else if (atomdb_backend_type == "redismongodb") { - this->atomdb_backend = shared_ptr(new RedisMongoDB("", false, atomdb_backend_config)); - } else if (atomdb_backend_type == "remotedb") { - this->atomdb_backend = shared_ptr(new RemoteAtomDB(atomdb_backend_config)); - } else { - RAISE_ERROR("Invalid AtomDB type: " + atomdb_backend_type); - } -} +// void AdapterDB::atomdb_backend_setup() { +// auto atomdb_backend_config = +// this->config.at_path("adapterdb.atomdb_backend").get_or(JsonConfig()); +// string atomdb_backend_type = atomdb_backend_config.at_path("type").get_or(""); +// if (atomdb_backend_type == "morkdb") { +// this->atomdb_backend = shared_ptr(new MorkDB("", atomdb_backend_config)); +// } else if (atomdb_backend_type == "redismongodb") { +// this->atomdb_backend = shared_ptr(new RedisMongoDB("", false, atomdb_backend_config)); +// } else if (atomdb_backend_type == "remotedb") { +// this->atomdb_backend = shared_ptr(new RemoteAtomDB(atomdb_backend_config)); +// } else { +// RAISE_ERROR("Invalid AtomDB type: " + atomdb_backend_type); +// } +// } bool AdapterDB::is_backend_ready() const { return this->backend_ready.load(); } diff --git a/src/atomdb/adapterdb/AdapterDB.h b/src/atomdb/adapterdb/AdapterDB.h index e5dc43104..7208072dd 100644 --- a/src/atomdb/adapterdb/AdapterDB.h +++ b/src/atomdb/adapterdb/AdapterDB.h @@ -34,8 +34,7 @@ inline AdapterDbType parse_adapter_db_type(const string& value) { class AdapterDB : public AtomDB { public: - explicit AdapterDB(const JsonConfig& config); - AdapterDB(const JsonConfig& config, shared_ptr backend); // for testing + AdapterDB(const JsonConfig& config, shared_ptr backend); ~AdapterDB() override; static string MONGODB_ADAPTER_COLLECTION_NAME; @@ -128,11 +127,6 @@ class AdapterDB : public AtomDB { */ void persistence_setup(); - /** - * @brief Initializes the AtomDB backend according to the configuration. - */ - void atomdb_backend_setup(); - bool is_backend_ready() const; void ensure_backend_ready() const; diff --git a/src/atomdb/adapterdb/BUILD b/src/atomdb/adapterdb/BUILD index 39534f411..f32b0d279 100644 --- a/src/atomdb/adapterdb/BUILD +++ b/src/atomdb/adapterdb/BUILD @@ -17,9 +17,6 @@ cc_library( includes = ["."], deps = [ "//atomdb", - "//atomdb/morkdb", - "//atomdb/redis_mongodb", - "//atomdb/remotedb:remotedb_lib", "//commons:commons_lib", "//commons/atoms:atoms_lib", "//db_adapter:db_adapter_lib", diff --git a/src/atomdb/redis_mongodb/RedisMongoDB.h b/src/atomdb/redis_mongodb/RedisMongoDB.h index b90664bf3..deb41cedc 100644 --- a/src/atomdb/redis_mongodb/RedisMongoDB.h +++ b/src/atomdb/redis_mongodb/RedisMongoDB.h @@ -28,7 +28,6 @@ enum MONGODB_FIELD { ID = 0, NAME, TARGETS, NAMED_TYPE, size }; class RedisMongoDB : public AtomDB { public: - RedisMongoDB(const string& context, bool skip_redis, const JsonConfig& config); ~RedisMongoDB(); bool allow_nested_indexing() override; @@ -145,6 +144,10 @@ class RedisMongoDB : public AtomDB { void build_composite_type_entries_map(const vector& links, map>& composite_type_entries_map); + protected: + friend class AtomDBFactory; + RedisMongoDB(const string& context, bool skip_redis, const JsonConfig& config); + private: string context; bool skip_redis_; diff --git a/src/atomdb/remotedb/RemoteAtomDB.cc b/src/atomdb/remotedb/RemoteAtomDB.cc index 1e4806613..53482a6be 100644 --- a/src/atomdb/remotedb/RemoteAtomDB.cc +++ b/src/atomdb/remotedb/RemoteAtomDB.cc @@ -20,54 +20,6 @@ using namespace commons; using json = nlohmann::json; -namespace { - -shared_ptr create_atomdb_from_config(const JsonConfig& config) { - string uid = config.at_path("uid").get_or(""); - string type = config.at_path("type").get_or(""); - string context = config.at_path("context").get_or(""); - - if (type == "inmemorydb") { - return make_shared(context.empty() ? "remotedb_" : context); - } - - if (type == "redismongodb") { - RedisMongoDB::initialize_statics(context); - auto atomdb = make_shared(context, false, config); - return atomdb; - } - - if (type == "morkdb") { - auto atomdb = make_shared(context, config); - return atomdb; - } - - RAISE_ERROR("Unknown AtomDB type for peer " + uid + ": " + type); - return nullptr; -} - -} // namespace - -RemoteAtomDB::RemoteAtomDB(const JsonConfig& peers_config) { - for (auto& entry : peers_config) { - auto peer_config = JsonConfig(entry); - string uid = peer_config.at_path("uid").get_or(""); - if (uid.empty()) continue; - - shared_ptr local_persistence = nullptr; - auto local_persistence_config = - peer_config.at_path("local_persistence").get_or(JsonConfig()); - if (!local_persistence_config.empty()) { - local_persistence = create_atomdb_from_config(local_persistence_config); - } - remote_db_[uid] = make_shared( - create_atomdb_from_config(peer_config), local_persistence, uid); - } - - LOG_INFO("RemoteAtomDB initialized with " << remote_db_.size() << " remote peers"); - derive_nested_indexing(); -} - RemoteAtomDB::RemoteAtomDB(map> peers) : remote_db_(std::move(peers)) { LOG_INFO("RemoteAtomDB initialized with " << remote_db_.size() << " pre-built peers"); diff --git a/src/atomdb/remotedb/RemoteAtomDB.h b/src/atomdb/remotedb/RemoteAtomDB.h index d1de2218f..a567ba7bd 100644 --- a/src/atomdb/remotedb/RemoteAtomDB.h +++ b/src/atomdb/remotedb/RemoteAtomDB.h @@ -15,11 +15,9 @@ namespace atomdb { /** * RemoteAtomDB connects to multiple remote AtomDBs via RemoteAtomDBPeer instances. * Each peer maintains its own cache, remote connection, and local persistence. - * The constructor expects a JSON config with connection info for each remote peer. */ class RemoteAtomDB : public AtomDB { public: - explicit RemoteAtomDB(const JsonConfig& peers_config); /** * Dependency-injection constructor for pre-built peers. * Primarily used by tests to federate controllable backends without live config/connection. diff --git a/src/main/BUILD b/src/main/BUILD index 028948672..be5d985b8 100644 --- a/src/main/BUILD +++ b/src/main/BUILD @@ -58,11 +58,8 @@ cc_library( srcs = ["db_loader.cc"], deps = [ "//atomdb:atomdb_api_types", + "//atomdb:atomdb_factory", "//atomdb:atomdb_singleton", - "//atomdb/adapterdb:adapterdb_lib", - "//atomdb/morkdb:morkdb_lib", - "//atomdb/redis_mongodb:redis_mongodb_lib", - "//atomdb/remotedb:remotedb_lib", "//commons:commons_lib", "//metta:metta_lib", ], diff --git a/src/main/db_loader.cc b/src/main/db_loader.cc index 761cc3135..ce2879f80 100644 --- a/src/main/db_loader.cc +++ b/src/main/db_loader.cc @@ -7,15 +7,12 @@ #include #include -#include "AdapterDB.h" -#include "AtomDBSingleton.h" +#include "AtomDBFactory.h" #include "JsonConfig.h" #include "JsonConfigParser.h" #include "MettaParser.h" #include "MettaParserActions.h" -#include "MorkDB.h" #include "RedisMongoDB.h" -#include "RemoteAtomDB.h" #include "Utils.h" #define LOG_LEVEL INFO_LEVEL @@ -72,20 +69,7 @@ int main(int argc, char* argv[]) { JsonConfig json_config = JsonConfigParser::load(config_path); auto atomdb_config = json_config.at_path("atomdb").get_or(JsonConfig()); - auto atomdb_type = atomdb_config.at_path("type").get_or(""); - if (atomdb_type == "redismongodb") { - AtomDBSingleton::provide(make_shared(context, false, atomdb_config)); - } else if (atomdb_type == "morkdb") { - AtomDBSingleton::provide(make_shared(context, atomdb_config)); - } else if (atomdb_type == "remotedb") { - auto remote_peers_config = - atomdb_config.at_path("remote_peers").get_or(JsonConfig()); - AtomDBSingleton::provide(make_shared(remote_peers_config)); - } else if (atomdb_type == "adapterdb") { - AtomDBSingleton::provide(make_shared(atomdb_config)); - } else { - RAISE_ERROR("Invalid AtomDB type: " + atomdb_type); - } + auto atomdb = AtomDBFactory::create(atomdb_config, context); signal(SIGINT, &ctrl_c_handler); signal(SIGTERM, &ctrl_c_handler); @@ -134,7 +118,6 @@ int main(int argc, char* argv[]) { (static_cast(i + 1) * lines.size()) / static_cast(num_threads); threads.emplace_back([&, start_line, end_line, i]() -> void { - auto thread_atomdb = AtomDBSingleton::get_instance(); vector batch_atoms; vector> parser_actions_list; size_t thread_atoms_count = 0; @@ -175,7 +158,7 @@ int main(int argc, char* argv[]) { thread_atoms_count++; if (batch_atoms.size() >= static_cast(chunk_size)) { - thread_atomdb->add_atoms(batch_atoms, true); + atomdb->add_atoms(batch_atoms, true); batch_atoms.clear(); if (parser_actions_list.size() > 10) { parser_actions_list.erase(parser_actions_list.begin(), @@ -190,7 +173,7 @@ int main(int argc, char* argv[]) { } if (!batch_atoms.empty()) { - thread_atomdb->add_atoms(batch_atoms, true); + atomdb->add_atoms(batch_atoms, true); } total_atoms_processed += thread_atoms_count; @@ -213,7 +196,6 @@ int main(int argc, char* argv[]) { STOP_WATCH_FINISH(db_loader_from_file, "DBLoaderFromFile"); } else { - auto atomdb = AtomDBSingleton::get_instance(); auto db = dynamic_pointer_cast(atomdb); if (db != nullptr) { try { @@ -234,8 +216,6 @@ int main(int argc, char* argv[]) { for (int i = 0; i < num_threads; i++) { threads.emplace_back([&, thread_id = i, links_per_thread, remainder]() -> void { - auto thread_db = AtomDBSingleton::get_instance(); - vector links; vector nodes; @@ -280,8 +260,8 @@ int main(int argc, char* argv[]) { links.push_back(link_with_nested); if (j % chunk_size == 0) { - thread_db->add_nodes(nodes, true); - thread_db->add_links(links, true); + atomdb->add_nodes(nodes, true); + atomdb->add_links(links, true); nodes.clear(); links.clear(); } @@ -290,12 +270,12 @@ int main(int argc, char* argv[]) { if (!nodes.empty()) { LOG_INFO("[" + to_string(thread_id) + "] Final - Adding " + to_string(nodes.size()) + " nodes"); - thread_db->add_nodes(nodes, true); + atomdb->add_nodes(nodes, true); } if (!links.empty()) { LOG_INFO("[" + to_string(thread_id) + "] Final - Adding " + to_string(links.size()) + " links"); - thread_db->add_links(links, true); + atomdb->add_links(links, true); } // clang-format off @@ -307,7 +287,7 @@ int main(int argc, char* argv[]) { }); // clang-format on - auto result = thread_db->query_for_pattern(link_schema); + auto result = atomdb->query_for_pattern(link_schema); if (result->size() != 2) { RAISE_ERROR("[" + to_string(thread_id) + "] Expected 2 results, got " + to_string(result->size())); diff --git a/src/tests/benchmark/atomdb/atomdb_main.cc b/src/tests/benchmark/atomdb/atomdb_main.cc index 4d5de5a86..dc020c7a4 100644 --- a/src/tests/benchmark/atomdb/atomdb_main.cc +++ b/src/tests/benchmark/atomdb/atomdb_main.cc @@ -13,7 +13,6 @@ #include "AtomDB.h" #include "JsonConfig.h" -#include "MorkDB.h" #include "RedisMongoDB.h" #include "Utils.h" #include "atomdb_operations.h" @@ -52,13 +51,9 @@ map global_metrics; void setup() { RedisMongoDB::initialize_statics(); } shared_ptr factory_create_atomdb(string type, const JsonConfig& atomdb_config) { - if (type == "redismongodb") { - return make_shared("", false, atomdb_config); - } else if (type == "morkdb") { - return make_shared("", atomdb_config); - } else { - RAISE_ERROR("Unknown AtomDB type: " + type); - } + JsonConfig config = atomdb_config; + config["type"] = type; + return AtomDBFactory::create(config); } int main(int argc, char** argv) { diff --git a/src/tests/cpp/BUILD b/src/tests/cpp/BUILD index 392c2b3a4..533d414bb 100644 --- a/src/tests/cpp/BUILD +++ b/src/tests/cpp/BUILD @@ -850,6 +850,34 @@ cc_test( ], ) +cc_test( + name = "atomdb_factory_test", + size = "medium", + srcs = ["atomdb_factory_test.cc"], + copts = [ + "-Iexternal/gtest/googletest/include", + "-Iexternal/gtest/googletest", + ], + linkopts = [ + "-L/usr/local/lib", + "-lhiredis_cluster", + "-lhiredis", + "-lmongocxx", + "-lbsoncxx", + ], + linkstatic = 1, + deps = [ + "//atomdb:atomdb_factory", + "//atomdb/adapterdb:adapterdb_lib", + "//atomdb/inmemorydb:inmemorydb_lib", + "//atomdb/morkdb:morkdb_lib", + "//atomdb/remotedb:remotedb_lib", + "//tests/cpp/test_commons:test_atomdb_json_config", + "@com_github_google_googletest//:gtest_main", + "@mbedtls", + ], +) + cc_test( name = "atomdbutils_test", size = "small", @@ -895,6 +923,7 @@ cc_test( ], linkstatic = 1, deps = [ + "//atomdb:atomdb_factory", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/remotedb:remotedb_lib", "//commons/atoms:atoms_lib", diff --git a/src/tests/cpp/adapterdb_test.cc b/src/tests/cpp/adapterdb_test.cc index c1c1f97e1..b7867fd5b 100644 --- a/src/tests/cpp/adapterdb_test.cc +++ b/src/tests/cpp/adapterdb_test.cc @@ -10,6 +10,7 @@ #include #include +#include "AtomDBFactory.h" #include "AtomDBSingleton.h" #include "Link.h" #include "Merger.h" @@ -56,7 +57,9 @@ class AdapterDBTestBase : public ::testing::Test { shared_ptr backend; void SetUpBackend() { - backend = make_shared("adapter_test", false, test_atomdb_json_config()); + auto atomdb = AtomDBFactory::create(test_atomdb_json_config(), "adapter_test"); + backend = dynamic_pointer_cast(atomdb); + ASSERT_NE(backend, nullptr); } JsonConfig build_adapter_config(const string& mapping_path, diff --git a/src/tests/cpp/atomdb_factory_test.cc b/src/tests/cpp/atomdb_factory_test.cc new file mode 100644 index 000000000..4906e3a9e --- /dev/null +++ b/src/tests/cpp/atomdb_factory_test.cc @@ -0,0 +1,156 @@ +#include + +#include +#include +#include +#include + +#include "AdapterDB.h" +#include "AtomDBFactory.h" +#include "InMemoryDB.h" +#include "JsonConfig.h" +#include "MorkDB.h" +#include "Node.h" +#include "RemoteAtomDB.h" +#include "TestAtomDBJsonConfig.h" +#include "Utils.h" +#include "expression_hasher.h" + +using namespace atomdb; +using namespace atoms; +using namespace commons; +using namespace std; + +namespace { + +JsonConfig config_with_type(const string& type) { + JsonConfig config; + config["type"] = type; + return config; +} + +JsonConfig remotedb_config_with_inmemory_peers() { + nlohmann::json json; + json["type"] = "remotedb"; + json["remote_peers"] = nlohmann::json::array( + {{{"uid", "peer1"}, {"type", "inmemorydb"}, {"context", "factory_remote_peer1_"}}, + {{"uid", "peer2"}, + {"type", "inmemorydb"}, + {"context", "factory_remote_peer2_"}, + {"local_persistence", {{"type", "inmemorydb"}, {"context", "factory_remote_peer2_local_"}}}}}); + return JsonConfig(json); +} + +} // namespace + +TEST(AtomDBFactoryTest, CreateInMemoryDB) { + auto db = AtomDBFactory::create(config_with_type("inmemorydb"), "factory_test_"); + ASSERT_NE(db, nullptr); + EXPECT_NE(dynamic_pointer_cast(db), nullptr); +} + +TEST(AtomDBFactoryTest, CreateMorkDB) { + auto db = AtomDBFactory::create(test_atomdb_json_config("morkdb"), "factory_mork_create_"); + ASSERT_NE(db, nullptr); + EXPECT_NE(dynamic_pointer_cast(db), nullptr); +} + +TEST(AtomDBFactoryTest, CreateRejectsMissingAndUnknownTypes) { + EXPECT_THROW(AtomDBFactory::create(JsonConfig()), runtime_error); + EXPECT_THROW(AtomDBFactory::create(config_with_type("")), runtime_error); + EXPECT_THROW(AtomDBFactory::create(config_with_type("unknown")), runtime_error); +} + +TEST(AtomDBFactoryTest, CreateRemoteAtomDBAssemblesPeers) { + auto db = AtomDBFactory::create(remotedb_config_with_inmemory_peers(), ""); + ASSERT_NE(db, nullptr); + + auto remote_db = dynamic_pointer_cast(db); + ASSERT_NE(remote_db, nullptr); + + const auto& peers = remote_db->get_remote_dbs(); + EXPECT_EQ(peers.size(), 2u); + EXPECT_NE(peers.find("peer1"), peers.end()); + EXPECT_NE(peers.find("peer2"), peers.end()); + // peer1 has no local_persistence; peer2 does. + EXPECT_TRUE(peers.at("peer1")->is_readonly()); + EXPECT_FALSE(peers.at("peer2")->is_readonly()); +} + +TEST(AtomDBFactoryTest, CreateRemoteAtomDBWithEmptyPeers) { + JsonConfig config; + config["type"] = "remotedb"; + config["remote_peers"] = nlohmann::json::array(); + + auto db = AtomDBFactory::create(config, ""); + ASSERT_NE(db, nullptr); + + auto remote_db = dynamic_pointer_cast(db); + ASSERT_NE(remote_db, nullptr); + EXPECT_TRUE(remote_db->get_remote_dbs().empty()); +} + +TEST(AtomDBFactoryTest, CreateRemoteAtomDBRejectsPeerWithoutUid) { + nlohmann::json json; + json["type"] = "remotedb"; + json["remote_peers"] = nlohmann::json::array( + {{{"type", "inmemorydb"}, {"context", "factory_remote_missing_uid_"}}, + {{"uid", "peer_ok"}, {"type", "inmemorydb"}, {"context", "factory_remote_ok_"}}}); + + EXPECT_THROW(AtomDBFactory::create(JsonConfig(json), ""), runtime_error); +} + +TEST(AtomDBFactoryTest, CreateAdapterDBRequiresBackendType) { + JsonConfig missing_backend; + missing_backend["type"] = "adapterdb"; + missing_backend["adapterdb"] = nlohmann::json::object(); + + // Missing adapterdb.atomdb_backend.type makes create_basic_atomdb fail via AtomDB::string_to_type. + EXPECT_THROW(AtomDBFactory::create(missing_backend, ""), runtime_error); + + // Valid adapterdb.atomdb_backend: factory constructs AdapterDB and delegates AtomDB ops. + string mapping_path = "/tmp/atomdb_factory_adapterdb_mapping.metta"; + string unique_marker = "factory_adapter_" + to_string(Utils::get_current_time_millis()) + "_" + + compute_hash(const_cast("atomdb_factory_adapterdb")); + { + ofstream mapping_file(mapping_path); + mapping_file << "; " << unique_marker << "\n"; + mapping_file << "(Similarity \"ent\" $h)\n"; + mapping_file << "(Inheritance \"human\" $m)\n"; + } + + auto mork_client = make_shared("localhost:40032"); + const string similarity_seed = "(Similarity \"ent\" \"human\")"; + const string inheritance_seed = "(Inheritance \"human\" \"mammal\")"; + if (mork_client->get(similarity_seed, similarity_seed).empty()) { + mork_client->post(similarity_seed); + } + if (mork_client->get(inheritance_seed, inheritance_seed).empty()) { + mork_client->post(inheritance_seed); + } + + nlohmann::json json; + json["type"] = "adapterdb"; + json["adapterdb"] = { + {"type", "mork"}, + {"context_mapping_paths", nlohmann::json::array({mapping_path})}, + {"database_credentials", {{"host", "localhost"}, {"port", 40032}}}, + {"persistence", {{"reuse_mongodb", true}}}, + {"export_metta_on_mapping", {{"enabled", false}, {"output_dir", "/tmp"}}}, + {"atomdb_backend", test_atomdb_json_config("morkdb").get_json()}, + }; + + auto db = AtomDBFactory::create(JsonConfig(json), "factory_adapterdb_"); + ASSERT_NE(db, nullptr); + + auto adapter_db = dynamic_pointer_cast(db); + ASSERT_NE(adapter_db, nullptr); + + Node node("Symbol", "FactoryAdapterDBDelegationNode"); + string handle = adapter_db->add_node(&node); + EXPECT_FALSE(handle.empty()); + EXPECT_TRUE(adapter_db->node_exists(handle)); + ASSERT_NE(adapter_db->get_node(handle), nullptr); + + remove(mapping_path.c_str()); +} diff --git a/src/tests/cpp/redis_mongodb_test.cc b/src/tests/cpp/redis_mongodb_test.cc index dc6170bb3..1b57ae8dd 100644 --- a/src/tests/cpp/redis_mongodb_test.cc +++ b/src/tests/cpp/redis_mongodb_test.cc @@ -1,6 +1,8 @@ #include #include +#include +#include #include #include #include @@ -8,6 +10,7 @@ #include #include "Atom.h" +#include "AtomDBFactory.h" #include "AtomDBSingleton.h" #include "Hasher.h" #include "Link.h" @@ -37,8 +40,9 @@ class MockDecoder : public HandleDecoder { class RedisMongoDBTestEnvironment : public ::testing::Environment { public: void SetUp() override { - auto atomdb = new RedisMongoDB("test_", false, test_atomdb_json_config()); - AtomDBSingleton::provide(shared_ptr(atomdb)); + auto atomdb = AtomDBFactory::create(test_atomdb_json_config(), "test_"); + ASSERT_NE(dynamic_pointer_cast(atomdb), nullptr); + AtomDBSingleton::provide(atomdb); load_animals_data(); } @@ -1190,7 +1194,8 @@ TEST_F(RedisMongoDBTest, CompositeTypeEnabledFlag) { auto config_default = test_atomdb_json_config(); config_default.erase("composite_type_enabled"); - auto db_default = make_shared("test_", false, config_default); + auto db_default = dynamic_pointer_cast(AtomDBFactory::create(config_default, "test_")); + ASSERT_NE(db_default, nullptr); EXPECT_TRUE(db_default->composite_type_enabled()); vector enabled_nodes = {new Node("Symbol", "CompositeTypeEnabled-A"), @@ -1212,7 +1217,9 @@ TEST_F(RedisMongoDBTest, CompositeTypeEnabledFlag) { auto config_disabled = test_atomdb_json_config(); config_disabled["composite_type_enabled"] = false; - auto db_disabled = make_shared("test_", false, config_disabled); + auto db_disabled = + dynamic_pointer_cast(AtomDBFactory::create(config_disabled, "test_")); + ASSERT_NE(db_disabled, nullptr); EXPECT_FALSE(db_disabled->composite_type_enabled()); vector disabled_nodes = {new Node("Symbol", "CompositeTypeDisabled-A"), diff --git a/src/tests/cpp/redis_mongodb_test_2.cc b/src/tests/cpp/redis_mongodb_test_2.cc index 75f5abee4..5b7ec1aea 100644 --- a/src/tests/cpp/redis_mongodb_test_2.cc +++ b/src/tests/cpp/redis_mongodb_test_2.cc @@ -7,6 +7,7 @@ #include #include +#include "AtomDBFactory.h" #include "AtomDBSingleton.h" #include "Hasher.h" #include "Link.h" @@ -22,9 +23,9 @@ using namespace std; class RedisMongoDBTestEnvironment : public ::testing::Environment { public: void SetUp() override { - auto atomdb = new RedisMongoDB("test2_", false, test_atomdb_json_config()); - atomdb->drop_all(); - AtomDBSingleton::provide(shared_ptr(atomdb)); + auto atomdb = AtomDBFactory::create(test_atomdb_json_config(), "test2_"); + ASSERT_NE(dynamic_pointer_cast(atomdb), nullptr); + AtomDBSingleton::provide(atomdb); } void TearDown() override { diff --git a/src/tests/cpp/remote_atomdb_test.cc b/src/tests/cpp/remote_atomdb_test.cc index 454ee8c15..574ff23ad 100644 --- a/src/tests/cpp/remote_atomdb_test.cc +++ b/src/tests/cpp/remote_atomdb_test.cc @@ -9,6 +9,7 @@ #include #include "Assignment.h" +#include "AtomDBFactory.h" #include "InMemoryDB.h" #include "InMemoryDBAPITypes.h" #include "JsonConfig.h" @@ -381,8 +382,10 @@ class RemoteAtomDBTest : public ::testing::Test { << "Could not find tests/assets/remotedb_config.json (TEST_SRCDIR=" << (std::getenv("TEST_SRCDIR") ? std::getenv("TEST_SRCDIR") : "unset") << ")"; auto json_config = load_config(config_path_); - auto remote_peers_val = json_config.at_path("remote_peers").get_or(JsonConfig()); - db_ = make_shared(remote_peers_val); + json_config["type"] = "remotedb"; + auto db = AtomDBFactory::create(json_config, ""); + db_ = dynamic_pointer_cast(db); + ASSERT_NE(db_, nullptr); } void TearDown() override {} @@ -477,8 +480,10 @@ class RemoteAtomDBConfigTest : public ::testing::Test { config_path_ = resolve_config_path("remotedb_config_single.json"); ASSERT_FALSE(config_path_.empty()) << "Could not find tests/assets/remotedb_config_single.json"; auto json_config = load_config(config_path_); - auto remote_peers_val = json_config.at_path("remote_peers").get_or(JsonConfig()); - db_ = make_shared(remote_peers_val); + json_config["type"] = "remotedb"; + auto db = AtomDBFactory::create(json_config, ""); + db_ = dynamic_pointer_cast(db); + ASSERT_NE(db_, nullptr); } void TearDown() override {}