Преглед изворни кода

fix(investment): gate unsupported cache fallback

y9522 пре 2 недеља
родитељ
комит
8d0903f765

+ 2 - 3
Sunrise/src/state/build_data/runtime.h

@@ -588,9 +588,8 @@ publish_vendor_catalog(std::span<const vendors::IndexEntry> index,
                                           vendors::Definition& definition) noexcept;
 
 /**
- * @return True when required domains are ready and either the full cache write succeeds or the
-
- * * optional catalyst catalog rejects an unsupported build without writing an incomplete cache.
+ * An unsupported catalyst build finishes without writing an incomplete cache.
+ * @return True when required domains are ready and cache persistence safely finishes.
  */
 [[nodiscard]] bool persist() noexcept;
 

+ 8 - 3
Sunrise/src/state/build_data/runtime/persistence/build_data_persistence.cpp

@@ -360,16 +360,21 @@ bool persist() noexcept {
     AcquireSRWLockExclusive(&state.lock);
     const bool requiredReady = runtime::persistence::required_domains_ready();
     bool result = false;
-    if (requiredReady && !exotic_catalysts_ready()
-        && state.catalystError == items::catalysts::Error::unsupportedBuild) {
+    switch (runtime::persistence::cache_action(
+        requiredReady, exotic_catalysts_ready(), state.catalystError)) {
+    case runtime::persistence::CacheAction::waitForDomains:
+        break;
+    case runtime::persistence::CacheAction::skipUnsupportedCatalog:
         // Catalyst facts are build-pinned. A rejected build must not keep the refresh worker
         // active or put an incomplete catalog on disk. Other extracted domains stay usable.
         runtime::persistence::release_scratch_locked(state);
         state.enabled = false;
         state.replaceStaleCache = false;
         result = true;
-    } else if (requiredReady) {
+        break;
+    case runtime::persistence::CacheAction::writeCompleteCache:
         result = runtime::persistence::persist_if_complete_locked(state);
+        break;
     }
     ReleaseSRWLockExclusive(&state.lock);
     return result;

+ 28 - 0
Sunrise/src/state/build_data/runtime/persistence/build_data_persistence.h

@@ -27,6 +27,34 @@
 
 namespace sunrise::state::build_data::runtime::persistence {
 
+/** Cache action after one extraction pass. */
+enum class CacheAction {
+    waitForDomains,
+    skipUnsupportedCatalog,
+    writeCompleteCache,
+};
+
+/**
+ * Selects the only safe persistence action for the current extraction state.
+ * @param requiredReady True when all required non-catalyst domains are ready.
+ * @param catalystReady True when the complete catalyst catalog is published.
+ * @param catalystError Exact result of the last catalyst derivation.
+ * @return Wait, skip an unsupported catalog, or write a complete cache.
+ */
+[[nodiscard]] constexpr CacheAction cache_action(bool requiredReady,
+                                                 bool catalystReady,
+                                                 items::catalysts::Error catalystError) noexcept {
+    if (!requiredReady) {
+        return CacheAction::waitForDomains;
+    }
+    if (catalystReady) {
+        return CacheAction::writeCompleteCache;
+    }
+    return catalystError == items::catalysts::Error::unsupportedBuild
+               ? CacheAction::skipUnsupportedCatalog
+               : CacheAction::waitForDomains;
+}
+
 /** Fixed cache paths, identity, and canonical snapshot storage guarded by one State lock. */
 struct Context {
     SRWLOCK lock{SRWLOCK_INIT};

+ 27 - 0
tests/catalyst_regression_tests.cpp

@@ -10,6 +10,7 @@
 #include "state/build_data/cache/records/codec.h"
 #include "state/build_data/items/catalysts/exotic_catalyst_builder.h"
 #include "state/build_data/items/catalysts/exotic_catalyst_catalog.h"
+#include "state/build_data/runtime/persistence/build_data_persistence.h"
 
 extern int failures;
 
@@ -19,6 +20,7 @@ namespace cache_records = sunrise::state::build_data::cache::records;
 namespace catalysts = sunrise::state::build_data::items::catalysts;
 namespace details = sunrise::state::build_data::items::details;
 namespace items = sunrise::state::build_data::items;
+namespace persistence = sunrise::state::build_data::runtime::persistence;
 namespace socket_plugs = sunrise::state::build_data::items::socket_plugs;
 
 constexpr std::uint32_t kTimestamp = 0x12345678U;
@@ -343,6 +345,30 @@ void test_cache_record() noexcept {
            "catalyst records use one cache bump over upstream version 44");
 }
 
+void test_persistence_action() noexcept {
+    using enum persistence::CacheAction;
+    expect(persistence::cache_action(false, false, catalysts::Error::unsupportedBuild)
+               == waitForDomains,
+           "persistence waits for required domains");
+    expect(persistence::cache_action(true, false, catalysts::Error::unsupportedBuild)
+               == skipUnsupportedCatalog,
+           "unsupported builds finish without an incomplete cache");
+    constexpr std::array rejectedErrors{
+        catalysts::Error::none,
+        catalysts::Error::noCatalyst,
+        catalysts::Error::placeholderOnly,
+        catalysts::Error::missingReleased,
+        catalysts::Error::ambiguousLifecycle,
+        catalysts::Error::invalidSocket,
+    };
+    for (const catalysts::Error error : rejectedErrors) {
+        expect(persistence::cache_action(true, false, error) == waitForDomains,
+               "non-build catalyst failures stay failed closed");
+    }
+    expect(persistence::cache_action(true, true, catalysts::Error::none) == writeCompleteCache,
+           "a complete catalog permits one cache write");
+}
+
 } // namespace
 
 void test_exotic_catalysts() noexcept {
@@ -350,5 +376,6 @@ void test_exotic_catalysts() noexcept {
     test_safe_derivation_failures();
     test_catalog_application();
     test_cache_record();
+    test_persistence_action();
     catalysts::clear();
 }