#pragma once #include "data/LedgerCacheInterface.hpp" #include "etl/SystemState.hpp" #include "util/prometheus/Bool.hpp" #include "util/prometheus/Label.hpp" #include "util/prometheus/Prometheus.hpp" #include #include namespace etl { /** * @brief Interface for managing writer state in the ETL subsystem. * * This interface provides methods to query and control whether the ETL process * is actively writing to the database. Implementations should coordinate with * the ETL system state to manage write responsibilities. */ class WriterStateInterface { public: virtual ~WriterStateInterface() = default; /** * @brief Check if the ETL process is in strict read-only mode. * @return true if the process is in strict read-only mode, false otherwise */ [[nodiscard]] virtual bool isReadOnly() const = 0; /** * @brief Check if the ETL process is currently writing to the database. * @return true if the process is writing, false otherwise */ [[nodiscard]] virtual bool isWriting() const = 0; /** * @brief Request to start writing to the database. * * This method signals that the process should take over writing responsibilities. * The actual transition to writing state may not be immediate. */ virtual void startWriting() = 0; /** * @brief Request to stop writing to the database. * * This method signals that the process should give up writing responsibilities. * The actual transition from writing state may not be immediate. */ virtual void giveUpWriting() = 0; /** * @brief Check if the cluster is using the fallback writer decision mechanism. * * @return true if the cluster has switched to fallback mode, false otherwise */ [[nodiscard]] virtual bool isFallback() const = 0; /** * @brief Check if this node is in fallback recovery mode. * * Fallback recovery is an intermediate state entered when the node has been in * fallback mode long enough to attempt returning to election-based writer selection. * In this state the node continues participating in the fallback write-race while * coordinating with other nodes to exit fallback together. * * @return true if the node is in fallback recovery mode, false otherwise */ [[nodiscard]] virtual bool isFallbackRecovery() const = 0; /** * @brief Set or clear the fallback recovery flag. * * When @p newValue is true, the node enters fallback recovery mode: * - @ref isFallbackRecovery returns true * - The plain fallback flag (@ref isFallback) is cleared so the node no longer * publishes @c DbRole::Fallback; it publishes @c DbRole::FallbackRecovery instead. * * When @p newValue is false, the recovery flag is cleared without touching the * plain fallback flag. This is used when the recovery coordination completes and * the node transitions back to election mode. * * @param newValue true to enter recovery mode, false to leave it */ virtual void setFallbackRecovery(bool newValue) = 0; /** * @brief Switch the cluster to the fallback writer decision mechanism. * * This method is called when the cluster needs to transition from the cluster * communication mechanism to the slower but more reliable fallback mechanism. * Once set, this flag propagates to all nodes in the cluster through the * ClioNode DbRole::Fallback state. * * Also clears the fallback recovery flag (@ref isFallbackRecovery) because entering * a fresh fallback period cancels any in-progress recovery attempt. */ virtual void setWriterDecidingFallback() = 0; /** * @brief Whether the ETL monitor has started and the node is ready to become a writer. * * @return true if ETL has started the monitor loop, false otherwise. */ [[nodiscard]] virtual bool isEtlStarted() const = 0; /** * @brief Whether the ledger cache is fully loaded. * * @return true if the cache is full, false otherwise. */ [[nodiscard]] virtual bool isCacheFull() const = 0; /** * @brief Create a clone of this writer state. * * Creates a new instance of the writer state with the same underlying system state. * This is used when spawning operations that need their own writer state instance * while sharing the same system state. * * @return A unique pointer to the cloned writer state. */ [[nodiscard]] virtual std::unique_ptr clone() const = 0; }; /** * @brief Implementation of WriterStateInterface that manages ETL writer state. * * This class coordinates with SystemState to manage whether the ETL process * is actively writing to the database. It provides methods to query the current * writing state and request transitions between writing and non-writing states. */ class WriterState : public WriterStateInterface { private: std::shared_ptr systemState_; /**< @brief Shared system state for ETL coordination */ std::reference_wrapper cache_; /** * @brief Prometheus metric tracking whether this node is in fallback recovery mode. * * @note Because @c prometheus::Bool holds a @c std::reference_wrapper to the underlying * gauge, copies of @c WriterState (including clones) share the same metric value. * Mutations made through a clone are therefore immediately visible on the original * instance and vice-versa. */ util::prometheus::Bool isFallbackRecovery_ = PrometheusService::boolMetric( "etl_writing_deciding_fallback_recovery", util::prometheus::Labels{}, "Whether clio is in recovery from the fallback writer decision mechanism" ); public: /** * @brief Construct a WriterState with the given system state and cache. * * @param state Shared pointer to the system state for coordination * @param cache The ledger cache used to report cache fullness */ WriterState(std::shared_ptr state, data::LedgerCacheInterface const& cache); [[nodiscard]] bool isReadOnly() const override; /** * @brief Check if the ETL process is currently writing to the database. * @return true if the process is writing, false otherwise */ [[nodiscard]] bool isWriting() const override; /** * @brief Request to start writing to the database. * * If already writing, this method does nothing. Otherwise, it sets the * shouldTakeoverWriting flag in the system state to signal the request. */ void startWriting() override; /** * @brief Request to stop writing to the database. * * If not currently writing, this method does nothing. Otherwise, it sets the * shouldGiveUpWriter flag in the system state to signal the request. */ void giveUpWriting() override; /** * @brief Switch the cluster to the fallback writer decision mechanism. * * Sets the isWriterDecidingFallback flag in the system state, which will be * propagated to other nodes in the cluster through the ClioNode DbRole::Fallback state. */ void setWriterDecidingFallback() override; /** * @brief Check if the cluster is using the fallback writer decision mechanism. * * @return true if the cluster has switched to fallback mode, false otherwise */ [[nodiscard]] bool isFallback() const override; /** @copydoc WriterStateInterface::isFallbackRecovery */ [[nodiscard]] bool isFallbackRecovery() const override; /** @copydoc WriterStateInterface::setFallbackRecovery */ void setFallbackRecovery(bool newValue) override; /** @copydoc WriterStateInterface::isEtlStarted */ [[nodiscard]] bool isEtlStarted() const override; /** @copydoc WriterStateInterface::isCacheFull */ [[nodiscard]] bool isCacheFull() const override; /** * @brief Create a clone of this writer state. * * Creates a new WriterState instance sharing the same system state. * * @return A unique pointer to the cloned writer state. */ [[nodiscard]] std::unique_ptr clone() const override; }; } // namespace etl