Loading src/backends/cluster.cpp +25 −24 Original line number Diff line number Diff line Loading @@ -298,7 +298,7 @@ namespace authdb { // Single retrieve with timeout: manifest contains header + magic + entity keys + full record data std::vector<uint8_t> manifest; // Check if a previous timed-out fetch completed in the background // Check if a previous async fetch completed in the background if (cache->pending_retrieve.valid()) { auto st = cache->pending_retrieve.wait_for(std::chrono::milliseconds(0)); if (st == std::future_status::ready) { Loading @@ -309,9 +309,11 @@ namespace authdb { } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: background retrieve failed: " << e.what() << std::endl; cache->last_fetch = now; // prevent immediate retry _ClusterDataExists.store(true); return; } catch (...) { cache->last_fetch = now; _ClusterDataExists.store(true); return; } Loading @@ -324,39 +326,28 @@ namespace authdb { return; } } else { // No pending fetch — start a new async retrieve with timeout // No pending fetch — launch async retrieve (fire-and-forget). // Use cached data for this request; result is picked up on next call. try { auto *raw_client = client.get(); cache->pending_retrieve = std::async(std::launch::async, [raw_client, dgid]() { return raw_client->retrieve(dgid); }); } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: async launch failed: " << e.what() << std::endl; } static constexpr auto RETRIEVE_TIMEOUT = std::chrono::seconds(5); if (cache->pending_retrieve.wait_for(RETRIEVE_TIMEOUT) == std::future_status::ready) { manifest = cache->pending_retrieve.get(); // Don't wait — always return cached data immediately. // Update last_fetch so we don't re-launch for at least 10s. cache->last_fetch = now; cache->recovery_epoch = cur_epoch; } else { // Timed out — future stays in cache, will be checked next call std::cerr << "[CLUSTER-BE] domain=" << _Domain << " retrieve timed out after 5s, using cached data" << std::endl; if (!cache->buffer.empty()) { _Buffer = cache->buffer; _ClusterDataExists.store(_Buffer.size() > sizeof(AuthHeader)); } return; } } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: retrieve failed: " << e.what() << std::endl; _ClusterDataExists.store(true); return; } catch (...) { _ClusterDataExists.store(true); return; } } if (manifest.size() < sizeof(AuthHeader) + sizeof(uint32_t) + sizeof(uint32_t)) { if (!manifest.empty()) Loading @@ -368,6 +359,16 @@ namespace authdb { AuthHeader head; std::memcpy(&head, manifest.data(), sizeof(AuthHeader)); // Revision guard: never replace local cache with older data. // This prevents data loss when a recovering node returns stale manifests. size_t cached_rev = bufferRevision(cache->buffer.empty() ? _Buffer : cache->buffer); if (head.Revesion < cached_rev) { DBG_LOG("[CLUSTER-BE] domain=" << _Domain << " retrieved rev=" << head.Revesion << " < cached rev=" << cached_rev << ", discarding stale manifest\n"); return; } size_t moff = sizeof(AuthHeader); uint32_t magic = 0; std::memcpy(&magic, manifest.data() + moff, sizeof(uint32_t)); Loading Loading
src/backends/cluster.cpp +25 −24 Original line number Diff line number Diff line Loading @@ -298,7 +298,7 @@ namespace authdb { // Single retrieve with timeout: manifest contains header + magic + entity keys + full record data std::vector<uint8_t> manifest; // Check if a previous timed-out fetch completed in the background // Check if a previous async fetch completed in the background if (cache->pending_retrieve.valid()) { auto st = cache->pending_retrieve.wait_for(std::chrono::milliseconds(0)); if (st == std::future_status::ready) { Loading @@ -309,9 +309,11 @@ namespace authdb { } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: background retrieve failed: " << e.what() << std::endl; cache->last_fetch = now; // prevent immediate retry _ClusterDataExists.store(true); return; } catch (...) { cache->last_fetch = now; _ClusterDataExists.store(true); return; } Loading @@ -324,39 +326,28 @@ namespace authdb { return; } } else { // No pending fetch — start a new async retrieve with timeout // No pending fetch — launch async retrieve (fire-and-forget). // Use cached data for this request; result is picked up on next call. try { auto *raw_client = client.get(); cache->pending_retrieve = std::async(std::launch::async, [raw_client, dgid]() { return raw_client->retrieve(dgid); }); } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: async launch failed: " << e.what() << std::endl; } static constexpr auto RETRIEVE_TIMEOUT = std::chrono::seconds(5); if (cache->pending_retrieve.wait_for(RETRIEVE_TIMEOUT) == std::future_status::ready) { manifest = cache->pending_retrieve.get(); // Don't wait — always return cached data immediately. // Update last_fetch so we don't re-launch for at least 10s. cache->last_fetch = now; cache->recovery_epoch = cur_epoch; } else { // Timed out — future stays in cache, will be checked next call std::cerr << "[CLUSTER-BE] domain=" << _Domain << " retrieve timed out after 5s, using cached data" << std::endl; if (!cache->buffer.empty()) { _Buffer = cache->buffer; _ClusterDataExists.store(_Buffer.size() > sizeof(AuthHeader)); } return; } } catch (const std::exception &e) { std::cerr << "ClusterBackend::fetchFromCluster: retrieve failed: " << e.what() << std::endl; _ClusterDataExists.store(true); return; } catch (...) { _ClusterDataExists.store(true); return; } } if (manifest.size() < sizeof(AuthHeader) + sizeof(uint32_t) + sizeof(uint32_t)) { if (!manifest.empty()) Loading @@ -368,6 +359,16 @@ namespace authdb { AuthHeader head; std::memcpy(&head, manifest.data(), sizeof(AuthHeader)); // Revision guard: never replace local cache with older data. // This prevents data loss when a recovering node returns stale manifests. size_t cached_rev = bufferRevision(cache->buffer.empty() ? _Buffer : cache->buffer); if (head.Revesion < cached_rev) { DBG_LOG("[CLUSTER-BE] domain=" << _Domain << " retrieved rev=" << head.Revesion << " < cached rev=" << cached_rev << ", discarding stale manifest\n"); return; } size_t moff = sizeof(AuthHeader); uint32_t magic = 0; std::memcpy(&magic, manifest.data() + moff, sizeof(uint32_t)); Loading