database_service.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #pragma once
  2. #include "document.hpp"
  3. #include "memory_store.hpp"
  4. #include "database_grpc_impl.hpp"
  5. #include "persistence/persistence_manager.hpp"
  6. #include "persistence/history_store.hpp"
  7. #include "encryption/encryption_manager.hpp"
  8. #include "events/event_manager.hpp"
  9. #include "files/file_manager.hpp"
  10. #include "replication/replication_manager.hpp"
  11. #include "migrations/migration_runner.hpp"
  12. #include "views/view_manager.hpp"
  13. #include "config/collection_config_manager.hpp"
  14. // v2.0 storage substrate (Stage 4) — DocumentStore + LmdbEnv live alongside
  15. // the v1.x MemoryStore during the Phase C transition. Stage 4 opens both;
  16. // later stages migrate handlers one group at a time, deleting v1.x paths
  17. // once they go cold.
  18. namespace smartbotic::db::storage {
  19. class LmdbEnv;
  20. class DocumentStore;
  21. class ProjectStoreRegistry;
  22. }
  23. #include <nlohmann/json.hpp>
  24. #include <atomic>
  25. #include <filesystem>
  26. #include <memory>
  27. #include <mutex>
  28. #include <string>
  29. #include <string_view>
  30. #include <thread>
  31. #include <vector>
  32. namespace grpc {
  33. class Server;
  34. }
  35. namespace smartbotic::database {
  36. /**
  37. * Main storage service application.
  38. * Coordinates all components and manages the gRPC server.
  39. */
  40. class DatabaseService {
  41. public:
  42. struct Config {
  43. std::string nodeId = "storage-1";
  44. std::string bindAddress = "0.0.0.0";
  45. uint16_t rpcPort = 9004;
  46. // Bootstrap/service registration
  47. std::string webapiUrl;
  48. std::string serviceKey;
  49. uint32_t heartbeatIntervalSec = 30;
  50. std::filesystem::path dataDirectory;
  51. std::filesystem::path keyFilePath;
  52. // Memory eviction settings
  53. uint64_t maxMemoryMb = 800; // Max memory before eviction
  54. uint32_t evictionThresholdPercent = 80; // Start evicting at this %
  55. uint32_t evictionTargetPercent = 60; // Evict down to this %
  56. uint32_t evictionCheckIntervalMs = 5000; // How often to check
  57. // NEW v1.7.0 eviction tuning — mirrors MemoryStore::Config fields.
  58. // Plumbed here so the config loader can set them and setupComponents()
  59. // can copy them into the MemoryStore. Schema-only in T2; behavior lands
  60. // in T3/T4.
  61. uint32_t evictionChunkSize = 1000;
  62. uint32_t evictionChunkPauseMs = 50;
  63. uint32_t maxEvictionPassesPerTrigger = 20;
  64. uint32_t hotWriteFloorMs = 30000;
  65. uint32_t memorySoftPercent = 70;
  66. uint32_t memoryHardPercent = 85;
  67. uint32_t memoryEmergencyPercent = 95;
  68. // v1.7.0 T10 — eviction burst event threshold (docs per tick).
  69. uint32_t evictionBurstThreshold = 10000;
  70. // Persistence settings
  71. uint32_t walSyncIntervalMs = 100;
  72. uint32_t snapshotIntervalSec = 3600;
  73. bool compressionEnabled = true;
  74. // Full persistence manager config (recovery mode, etc.).
  75. // DatabaseService::setupComponents() overlays the simpler fields above
  76. // onto this struct before constructing PersistenceManager.
  77. PersistenceManager::Config persistenceConfig;
  78. // File storage settings
  79. uint64_t maxFileSizeMb = 500;
  80. std::vector<std::string> allowedFileTypes;
  81. uint32_t fileCleanupIntervalSec = 3600;
  82. // Encryption settings
  83. bool encryptionEnabled = true;
  84. bool autoGenerateKey = true;
  85. // Replication settings
  86. bool replicationEnabled = true;
  87. std::vector<std::string> peerAddresses;
  88. std::string conflictResolution = "last_writer_wins";
  89. // Migration settings
  90. struct MigrationsConfig {
  91. bool enabled = true;
  92. std::filesystem::path directory;
  93. bool autoApply = true;
  94. bool failOnError = true;
  95. } migrations;
  96. // gRPC server settings (v1.6.2 — concurrency cap + configurable sizes)
  97. struct GrpcConfig {
  98. uint32_t maxReceiveMessageSizeMb = 100;
  99. uint32_t maxSendMessageSizeMb = 100;
  100. uint32_t resourceQuotaMemoryMb = 256;
  101. uint32_t maxConcurrentSubscribeStreams = 50;
  102. uint32_t maxConcurrentFileStreams = 10;
  103. };
  104. GrpcConfig grpc;
  105. };
  106. explicit DatabaseService(Config config);
  107. ~DatabaseService();
  108. /**
  109. * Initialize the service.
  110. * Loads configuration, initializes components, and prepares for startup.
  111. */
  112. bool initialize();
  113. /**
  114. * Start the service.
  115. * Starts the gRPC server and all background threads.
  116. */
  117. void start();
  118. /**
  119. * Stop the service.
  120. * Gracefully shuts down all components.
  121. */
  122. void stop();
  123. /**
  124. * Wait for the service to stop.
  125. */
  126. void wait();
  127. /**
  128. * Check if the service is running.
  129. */
  130. [[nodiscard]] bool isRunning() const { return running_; }
  131. /**
  132. * Signal the service to stop (for signal handlers).
  133. */
  134. void signalStop();
  135. /**
  136. * Get service statistics.
  137. */
  138. [[nodiscard]] nlohmann::json getStats() const;
  139. /**
  140. * Access the view manager (for RPC handlers, migrations, etc.).
  141. */
  142. ViewManager& viewManager() { return *view_manager_; }
  143. /** Current read-only state (atomic, lock-free read). */
  144. bool isReadOnly() const { return read_only_.load(std::memory_order_acquire); }
  145. /** Human-readable reason the DB is read-only (empty when writable). */
  146. std::string readOnlyReason() const;
  147. /** Toggle read-only state. Called by SetReadOnly RPC and startup logic. */
  148. void setReadOnly(bool value, const std::string& reason);
  149. /** Recovery outcome from the last recover() call. Used by GetReadOnlyStatus RPC. */
  150. const RecoveryOutcome& recoveryOutcome() const { return recovery_outcome_; }
  151. /** Set the force-readwrite flag (from --force-readwrite CLI arg). */
  152. void setForceReadwrite(bool value) { force_readwrite_ = value; }
  153. /**
  154. * Load configuration from a JSON file.
  155. */
  156. static Config loadConfig(const std::filesystem::path& configPath);
  157. /**
  158. * Parse configuration from an already-loaded JSON object. Used by the
  159. * conf.d drop-in loader (see config/config_loader.hpp) after it has
  160. * merged /etc/smartbotic-database/config.json with every *.json in
  161. * /etc/smartbotic-database/conf.d/.
  162. */
  163. static Config parseConfig(const nlohmann::json& json);
  164. // v2.3 — project-aware read accessors. `docStore(project)` returns
  165. // the per-project LmdbDocumentStore or nullptr if the project isn't
  166. // open (rare unless someone dropped it concurrently). The no-arg
  167. // `docStore()` defaults to the "default" project for callers still
  168. // on the v2.0-v2.2 API shape (Stage D will retire it). Health +
  169. // drift atomics gate any LMDB-first read path.
  170. smartbotic::db::storage::DocumentStore* docStore() noexcept;
  171. smartbotic::db::storage::DocumentStore* docStore(std::string_view project) noexcept;
  172. smartbotic::db::storage::ProjectStoreRegistry* projects() noexcept {
  173. return projects_.get();
  174. }
  175. bool mirrorHealthy() const noexcept {
  176. return mirror_healthy_.load(std::memory_order_acquire);
  177. }
  178. uint64_t mirrorDriftCount() const noexcept {
  179. return mirror_drift_count_.load(std::memory_order_relaxed);
  180. }
  181. // v2.3 Stage F — project CRUD entry points (delegate to registry).
  182. // The registry is the source of truth for listing / creating / dropping.
  183. // CreateProject is idempotent. DropProject refuses "default". The
  184. // gRPC handlers in database_grpc_impl.cpp call these.
  185. std::vector<std::string> listProjects() const;
  186. bool createProject(const std::string& name, std::string& error);
  187. bool dropProject(const std::string& name, std::string& error);
  188. private:
  189. void setupComponents();
  190. void startGrpcServer();
  191. void stopGrpcServer();
  192. void applyReplicatedEntry(const databasepb::ReplicationEntry& entry);
  193. bool runMigrations();
  194. // v2.0 Stage 4 — synchronous backfill of MemoryStore into doc_store_.
  195. // Runs once at boot, after recovery + migrations, before gRPC accepts
  196. // traffic. Skips when _meta.schema_version=2 marker exists (the env
  197. // is already populated by migrate_v1_to_v2). Returns false only on
  198. // catastrophic failure; per-doc errors flip mirror_healthy_=false but
  199. // do not abort boot (the read-flip downstream is the gate).
  200. bool backfillIntoDocStore();
  201. // v2.3 Stage C — atomic rename of <dataDir>/env/ into
  202. // <dataDir>/projects/default/env/ when the v2.2 layout is detected
  203. // and the new layout doesn't yet exist. Idempotent. Refuses to start
  204. // if both exist (likely operator hand-mess); the operator must
  205. // resolve manually.
  206. void migrateLegacyEnvToDefaultProject();
  207. Config config_;
  208. std::atomic<bool> running_{false};
  209. std::atomic<bool> stopRequested_{false};
  210. // Components
  211. std::unique_ptr<MemoryStore> store_;
  212. // v2.3 — multi-project storage substrate. Each project is one
  213. // LmdbEnv at `<dataDir>/projects/<name>/env/`. The registry holds
  214. // them in a map keyed by project name, lazy-opens new projects on
  215. // first write, and is the single source of truth for "which projects
  216. // exist." Old v2.0-v2.2 installs auto-migrate the legacy
  217. // `<dataDir>/env/` into `<dataDir>/projects/default/env/` at first
  218. // boot (see migrateLegacyEnvToDefaultProject).
  219. std::unique_ptr<smartbotic::db::storage::ProjectStoreRegistry> projects_;
  220. // v2.0 dual-write health. The persist callback mirrors every user-
  221. // collection write into doc_store_; on failure we log ERROR, bump the
  222. // drift counter, and flip mirror_healthy_ to false. Downstream Stage 4
  223. // tasks that flip reads onto doc_store_ MUST check mirror_healthy_
  224. // before engaging — a degraded mirror means LMDB is missing writes.
  225. std::atomic<bool> mirror_healthy_{true};
  226. std::atomic<uint64_t> mirror_drift_count_{0};
  227. std::unique_ptr<PersistenceManager> persistence_;
  228. std::unique_ptr<HistoryStore> history_store_; // v1.9.0 — disk-resident history
  229. std::unique_ptr<EncryptionManager> encryption_;
  230. std::unique_ptr<EventManager> events_;
  231. std::unique_ptr<FileManager> files_;
  232. std::unique_ptr<ReplicationManager> replication_;
  233. std::unique_ptr<ViewManager> view_manager_;
  234. std::unique_ptr<CollectionConfigManager> config_manager_;
  235. // gRPC
  236. std::unique_ptr<DatabaseGrpcImpl> storageImpl_;
  237. std::unique_ptr<DatabaseReplicationGrpcImpl> replicationImpl_;
  238. std::unique_ptr<grpc::Server> grpcServer_;
  239. std::thread serverThread_;
  240. // Migration runner
  241. std::unique_ptr<MigrationRunner> migrationRunner_;
  242. // Read-only runtime state (v1.6.1 — auto-readonly on non-trivial recovery)
  243. std::atomic<bool> read_only_{false};
  244. mutable std::mutex reason_mutex_;
  245. std::string read_only_reason_;
  246. RecoveryOutcome recovery_outcome_;
  247. bool force_readwrite_ = false;
  248. };
  249. } // namespace smartbotic::database