Procházet zdrojové kódy

fix(registry): re-open all projects' collection registries at startup (#1)

On restart the per-project vectorapi_collections index was never opened, so
GET /collections returned empty and document ops 404'd even though the data
survived on disk. Startup only warmed the default project; named projects were
never ensure()'d until a management call lazily ran it. KeyStore::bootstrap
already re-opens vectorapi_keys every boot, which is why keys survived but the
registry didn't.

Add CollectionRegistry::bootstrap(projects) and call it for every project from
db.listProjects() at startup. Regression guard in test_registry.cpp.

Fixes #1
fszontagh před 1 měsícem
rodič
revize
7e22bbb

+ 4 - 0
src/collection_registry.cpp

@@ -26,6 +26,10 @@ void CollectionRegistry::ensure(const std::string& project) {
         db_.client().createCollection(c, 0, false, 0, 0);
 }
 
+void CollectionRegistry::bootstrap(const std::vector<std::string>& projects) {
+    for (const auto& p : projects) ensure(p);
+}
+
 bool CollectionRegistry::add(const std::string& project, const CollectionMeta& meta) {
     CollectionMeta m = meta;
     if (m.createdAt == 0) m.createdAt = static_cast<uint64_t>(std::time(nullptr));

+ 8 - 0
src/collection_registry.hpp

@@ -28,6 +28,14 @@ public:
     /// Ensure the registry collection for `project` exists (idempotent).
     void ensure(const std::string& project);
 
+    /// Re-open the registry collection for every given project. The DB only
+    /// surfaces a collection through find()/list() once it has been opened in
+    /// the current server process; without this, a restart leaves every
+    /// per-project registry index invisible (collections appear empty / 404)
+    /// even though the data survives. Call once at startup with all known
+    /// projects — mirrors KeyStore::bootstrap re-opening vectorapi_keys. (issue #1)
+    void bootstrap(const std::vector<std::string>& projects);
+
     /// Add (or overwrite) a collection entry.  Returns true on success.
     bool add(const std::string& project, const CollectionMeta& meta);
 

+ 11 - 1
src/main.cpp

@@ -64,7 +64,17 @@ int main(int argc, char** argv) {
     svapi::CollectionRegistry registry(db);
     const std::string defProj = settings.snapshot()->defaultProject;
     db.ensureProject(defProj);
-    registry.ensure(defProj);
+    // Re-open EVERY project's collection registry at startup. The DB only
+    // surfaces a collection via find()/list() once it has been opened in the
+    // current process, so without this a restart makes all collections vanish
+    // from the API (GET /collections empty, document ops 404) even though the
+    // data survives on disk. KeyStore::bootstrap already does this for
+    // vectorapi_keys, which is why keys survive but the registry didn't. (issue #1)
+    {
+        auto projects = db.listProjects();  // always includes the default project
+        registry.bootstrap(projects);
+        spdlog::info("opened collection registry for {} project(s) at startup", projects.size());
+    }
 
     svapi::SessionStore sessions(std::chrono::minutes(settings.snapshot()->sessionTtlMinutes));
 

+ 24 - 0
tests/test_registry.cpp

@@ -26,3 +26,27 @@ TEST(CollectionRegistry, IsolatedPerProject) {
 
     db->dropProject(pa); db->dropProject(pb);
 }
+
+// Regression guard for issue #1: a freshly-constructed registry (as on a new
+// server process) must surface previously-stored entries for every project once
+// bootstrap() has re-opened them. The startup path warmed only the default
+// project before the fix, so named projects' collections went missing on restart.
+TEST(CollectionRegistry, BootstrapWarmsAllProjects) {
+    SVAPI_REQUIRE_DB(db);
+    const std::string pa = testutil::tmpName("bootA"), pb = testutil::tmpName("bootB");
+    db->ensureProject(pa); db->ensureProject(pb);
+    {
+        CollectionRegistry seed(*db);
+        seed.ensure(pa); seed.ensure(pb);
+        EXPECT_TRUE(seed.add(pa, {"alpha", "json", 0, "", 0}));
+        EXPECT_TRUE(seed.add(pb, {"beta", "vector", 8, "m", 0}));
+    }
+    CollectionRegistry fresh(*db);
+    fresh.bootstrap({pa, pb});            // the startup warm-up that fixes issue #1
+    EXPECT_EQ(fresh.list(pa).size(), 1u);
+    EXPECT_EQ(fresh.list(pb).size(), 1u);
+    EXPECT_TRUE(fresh.get(pa, "alpha").has_value());
+    EXPECT_TRUE(fresh.get(pb, "beta").has_value());
+
+    db->dropProject(pa); db->dropProject(pb);
+}