yams/include/yams/daemon/daemon.h

235 lines
9.7 KiB
C++

#pragma once
#include <yams/core/types.h>
#include <yams/daemon/components/DaemonLifecycleFsm.h>
#include <yams/daemon/components/StateComponent.h>
#include <yams/daemon/components/TuningConfig.h>
#include <yams/daemon/resource/onnx_model_pool.h> // For DaemonConfig
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <cstdio>
#include <filesystem>
#include <functional>
#include <future>
#include <map>
#include <memory>
#include <mutex>
#include <thread>
#include <yams/compat/thread_stop_compat.h>
//
namespace yams::daemon {
// Forward declarations for components
class LifecycleComponent;
class DaemonLifecycleAdapter;
class ServiceManager;
class RequestDispatcher;
class DaemonMetrics;
class SocketServer;
class IOCoordinator;
class TuningManager; // centralized tuner (moved from ServiceManager)
// Forward decls for GTEST-only accessors are below guarded by YAMS_TESTING
struct DaemonConfig {
std::filesystem::path dataDir;
std::filesystem::path socketPath;
std::filesystem::path pidFile;
std::filesystem::path logFile;
size_t maxMemoryGb = 4;
std::chrono::milliseconds requestTimeout{5000};
bool healthMonitoring = true;
std::string logLevel = "info";
size_t maxLogFiles = 5;
size_t maxLogSizeMb = 10;
bool enableModelProvider = false;
bool modelProviderRequired = false;
ModelPoolConfig modelPoolConfig;
std::filesystem::path pluginDir;
std::string pluginNamePolicy{"relaxed"};
bool pluginDirStrict{false};
std::vector<std::filesystem::path> trustedPluginPaths;
// Default to disabled to avoid unintended plugin loading in plain daemon
// invocations and tests that don't explicitly opt in. Tests that rely on
// plugins (e.g., ONNX embeddings) set this to true in their config.
bool autoLoadPlugins = false;
bool enableAutoRepair = true;
bool useMockModelProvider = false;
size_t autoRepairBatchSize = 32;
bool autoRebuildOnDimMismatch = false;
size_t maxPendingRepairs = 1000;
// Embedded one-shot clients (sandboxed CLIs, Codex) should avoid startup maintenance that can
// outlive the command and make teardown expensive. Request handlers may still perform explicit
// writes, but opportunistic reconcile/rebuild work is suppressed.
bool embeddedOneShot = false;
// Typed runtime instrumentation profile. The "memory" profile suppresses
// opportunistic startup maintenance that creates large transient allocator
// churn, which is especially expensive under macOS MallocStackLogging.
struct InstrumentationPolicy {
// "auto" enables the memory profile when MallocStackLogging is present;
// "memory" forces it; "normal"/"off" disables it.
std::string profile{"auto"};
bool memoryProfileActive{false};
bool suppressAutoRepair{false};
bool suppressSimeonLexicalBuild{false};
bool suppressVectorIndexBuild{false};
std::uint64_t mslStackLogWarnBytes{2ULL * 1024ULL * 1024ULL * 1024ULL};
} instrumentation;
// Per-plugin configuration: plugin name -> JSON config string
// Parsed from [plugins.<name>] sections in config.toml
std::map<std::string, std::string> pluginConfigs;
// Streaming keepalive configuration
std::chrono::milliseconds heartbeatInterval{500}; // default 500ms
std::chrono::milliseconds heartbeatJitter{50}; // default +/-50ms applied per tick
// Adaptive tuning configuration (no envs)
TuningConfig tuning{};
// Path to loaded config file for reloads
std::filesystem::path configFilePath;
struct GraphPrunePolicy {
bool enabled{false};
std::size_t keepLatestPerCanonical{3};
std::chrono::minutes interval{60};
std::chrono::minutes initialDelay{10};
} graphPrune;
// Forward decls for GTEST-only accessors are below guarded by YAMS_TESTING
struct DownloadPolicy {
bool enable{false}; // feature gate
std::vector<std::string> allowedHosts{}; // exact or wildcard patterns
std::vector<std::string> allowedSchemes{"https"}; // schemes to allow (default https)
bool requireChecksum{true}; // require algo:hex on requests
bool storeOnly{true}; // only write into CAS (no arbitrary paths)
std::chrono::milliseconds timeout{60000}; // per-request timeout
std::uint64_t maxFileBytes{0}; // 0 = unlimited
size_t rateLimitRps{0}; // 0 = unlimited
std::string sandbox{"subprocess"}; // future: platform-specific isolation
} downloadPolicy;
};
class YamsDaemon {
public:
explicit YamsDaemon(const DaemonConfig& config = {});
~YamsDaemon();
// Lifecycle management
Result<void> start();
Result<void> stop();
bool waitForStopCompletion(std::chrono::milliseconds timeout);
/// Run the daemon main loop on the calling thread. Call after start().
/// Returns when stopRequested_ becomes true or requestStop() is called.
void runLoop();
/// Test helper: trigger deferred async initialization and the init waiter
/// without entering the long-lived runLoop wait loop.
void testingStartAsyncInitWithoutRunLoop();
void requestStop() {
stopRequested_.store(true, std::memory_order_release);
stop_cv_.notify_all();
}
bool isRunning() const { return running_.load(); }
bool isStopRequested() const { return stopRequested_.load(); }
const StateComponent& getState() const { return state_; }
// Lifecycle FSM accessor
const DaemonLifecycleFsm& getLifecycle() const { return lifecycleFsm_; }
// Mark or clear subsystem degradation (sticky until cleared by operator action)
void setSubsystemDegraded(const std::string& name, bool degraded,
const std::string& reason = std::string()) {
lifecycleFsm_.setSubsystemDegraded(name, degraded, reason);
}
// Socket server accessor (for testing)
SocketServer* getSocketServer() const { return socketServer_.get(); }
// Document event notifications - forwarded to RepairService via ServiceManager
void onDocumentAdded(const std::string& hash, const std::string& path);
void onDocumentRemoved(const std::string& hash);
// Path resolution helpers
enum class PathType { Socket, PidFile, LogFile };
static std::filesystem::path resolveSystemPath(PathType type);
static bool canWriteToDirectory(const std::filesystem::path& dir);
static std::filesystem::path getXDGRuntimeDir();
DaemonConfig config_;
StateComponent state_;
std::unique_ptr<LifecycleComponent> lifecycleManager_;
std::shared_ptr<ServiceManager> serviceManager_;
std::unique_ptr<DaemonLifecycleAdapter> lifecycleAdapter_;
std::unique_ptr<RequestDispatcher> requestDispatcher_;
// Accessed from multiple threads (runLoop + request handling/shutdown). Guard with mutex.
mutable std::mutex metricsMutex_;
// Protects mutable config fields updated after startup (currently tuning reload state).
mutable std::mutex configMutex_;
std::shared_ptr<DaemonMetrics> metrics_;
// Integrated socket server (replaces external yams-socket-server)
std::unique_ptr<SocketServer> socketServer_;
// Dedicated I/O thread pool for IPC sockets
std::unique_ptr<IOCoordinator> ioCoordinator_;
std::unique_ptr<TuningManager> tuningManager_;
// Lifecycle FSM (authoritative lifecycle state)
DaemonLifecycleFsm lifecycleFsm_{};
// Threading and state
std::atomic<bool> running_{false};
std::atomic<bool> stopRequested_{false};
std::atomic<bool> reloadRequested_{false};
std::atomic<bool> runLoopStarted_{false};
// Main loop runs on the calling thread via runLoop().
std::mutex stop_mutex_;
std::condition_variable stop_cv_;
bool stopCompleted_{true};
std::promise<void> asyncInitStartedPromise_;
std::shared_future<void> asyncInitStartedFuture_;
std::atomic<bool> asyncInitBarrierSet_{false};
std::thread initWaiterThread_;
std::atomic<bool> initHandled_{false};
std::mutex shutdownThreadMutex_;
std::thread shutdownThread_;
std::atomic<bool> shutdownThreadActive_{false};
bool modelPreloadSkipped_{false};
std::chrono::steady_clock::time_point repairBusySince_{};
std::chrono::steady_clock::time_point repairReadySince_{};
// Signal check hook for integration with main loop (avoids separate thread)
std::function<bool()> signalCheckHook_;
// External shutdown flag reference for CV predicate wake-up (yams-qe6r fix)
// When set, the CV predicate in runLoop() also checks this flag, enabling
// prompt response to SIGTERM even when blocked waiting on the CV.
// Atomic pointer to avoid data race between setter and runLoop reader.
std::atomic<std::atomic<bool>*> externalShutdownFlag_{nullptr};
// On-demand plugin autoload bridge
Result<size_t> autoloadPluginsNow();
ServiceManager* getServiceManager() const { return serviceManager_.get(); }
void requestReload() { reloadRequested_.store(true, std::memory_order_relaxed); }
void reloadTuningConfig();
// Set a hook that will be called each iteration of runLoop() to check for signals
// Returns true if shutdown was requested
void setSignalCheckHook(std::function<bool()> hook) { signalCheckHook_ = std::move(hook); }
// Set an external shutdown flag that the CV predicate will also check (yams-qe6r)
// This enables the runLoop to respond promptly to SIGTERM even when blocked on CV
void setExternalShutdownFlag(std::atomic<bool>* flag) {
externalShutdownFlag_.store(flag, std::memory_order_release);
}
void spawnShutdownThread(std::function<void()> shutdownFn);
void reapCompletedShutdownThread();
};
} // namespace yams::daemon