Просмотр исходного кода

feat(client): setReadOnly/lock/unlock/getReadOnlyStatus client API

fszontagh 3 месяцев назад
Родитель
Сommit
5572a7e184
2 измененных файлов с 77 добавлено и 0 удалено
  1. 29 0
      client/include/smartbotic/database/client.hpp
  2. 48 0
      client/src/client.cpp

+ 29 - 0
client/include/smartbotic/database/client.hpp

@@ -462,6 +462,35 @@ public:
      */
     [[nodiscard]] std::optional<StatsInfo> getStats();
 
+    // ===== Read-Only Control =====
+
+    struct ReadOnlyStatus {
+        bool readOnly = false;
+        std::string reason;
+        std::string recoveryOutcome;    // "trivial_success" | "fresh_install" | "snapshot_fell_back" | "wal_only_replay" | "forced_empty" | "failed"
+        std::string expectedSnapshot;
+        std::string snapshotUsed;
+        std::string failureReason;
+        uint64_t walEntriesReplayed = 0;
+        uint32_t snapshotsAttempted = 0;
+    };
+
+    /**
+     * Toggle server-side read-only mode. When on, all writes are rejected
+     * with FAILED_PRECONDITION. Typically used to acknowledge a non-trivial
+     * recovery state, or to drain traffic for maintenance.
+     */
+    bool setReadOnly(bool readOnly);
+
+    /** Shortcut for setReadOnly(true). */
+    bool lock() { return setReadOnly(true); }
+
+    /** Shortcut for setReadOnly(false). Used after verifying a non-trivial recovery state. */
+    bool unlock() { return setReadOnly(false); }
+
+    /** Inspect the current read-only state and the recovery outcome. */
+    [[nodiscard]] ReadOnlyStatus getReadOnlyStatus();
+
     // ===== Health =====
 
     struct HealthInfo {

+ 48 - 0
client/src/client.cpp

@@ -1135,6 +1135,46 @@ public:
         return info;
     }
 
+    // ===== Read-Only Control =====
+
+    bool setReadOnly(bool readOnly) {
+        smartbotic::databasepb::SetReadOnlyRequest request;
+        request.set_read_only(readOnly);
+        smartbotic::databasepb::SetReadOnlyResponse response;
+        grpc::ClientContext context;
+        setDeadline(context);
+
+        auto status = stub_->SetReadOnly(&context, request, &response);
+        if (!status.ok()) {
+            spdlog::error("Client::setReadOnly failed: {}", status.error_message());
+            return false;
+        }
+        return response.success();
+    }
+
+    Client::ReadOnlyStatus getReadOnlyStatus() {
+        smartbotic::databasepb::GetReadOnlyStatusRequest request;
+        smartbotic::databasepb::GetReadOnlyStatusResponse response;
+        grpc::ClientContext context;
+        setDeadline(context);
+
+        Client::ReadOnlyStatus out;
+        auto status = stub_->GetReadOnlyStatus(&context, request, &response);
+        if (!status.ok()) {
+            spdlog::error("Client::getReadOnlyStatus failed: {}", status.error_message());
+            return out;
+        }
+        out.readOnly              = response.read_only();
+        out.reason                = response.reason();
+        out.recoveryOutcome       = response.recovery_outcome();
+        out.expectedSnapshot      = response.expected_snapshot();
+        out.snapshotUsed          = response.snapshot_used();
+        out.failureReason         = response.failure_reason();
+        out.walEntriesReplayed    = response.wal_entries_replayed();
+        out.snapshotsAttempted    = response.snapshots_attempted();
+        return out;
+    }
+
     // ===== File Operations =====
 
     Client::FileUploadResult uploadFile(const std::vector<uint8_t>& data, const Client::FileUploadMeta& meta) {
@@ -1508,6 +1548,14 @@ std::optional<Client::StatsInfo> Client::getStats() {
     return impl_->getStats();
 }
 
+bool Client::setReadOnly(bool readOnly) {
+    return impl_->setReadOnly(readOnly);
+}
+
+Client::ReadOnlyStatus Client::getReadOnlyStatus() {
+    return impl_->getReadOnlyStatus();
+}
+
 Client::FileUploadResult Client::uploadFile(const std::vector<uint8_t>& data, const FileUploadMeta& meta) {
     return impl_->uploadFile(data, meta);
 }