فهرست منبع

feat(schema): config + proto additions for eviction v2 (v1.7.0 T2)

Adds config surface for v1.7.0's chunked eviction + memory pressure work:

Config (storage.memory):
  eviction_chunk_size (default 1000)
  eviction_chunk_pause_ms (default 50)
  max_eviction_passes_per_trigger (default 20)
  hot_write_floor_ms (default 30000)
  memory_soft_percent (default 70)
  memory_hard_percent (default 85)
  memory_emergency_percent (default 95)

CollectionOptions.memory_priority (LOW/NORMAL/HIGH, default NORMAL) —
selection weight for eviction. Existing collections keep behavior.

Proto:
  CollectionOptions gets memory_priority field
  New GetMemoryStats RPC + CollectionMemoryStats/GetMemoryStatsResponse messages
  New event types: MEMORY_PRESSURE_HIGH, MEMORY_EVICTION_BURST

Behavior change in later tasks; this one is schema-only, no runtime change.
fszontagh 3 ماه پیش
والد
کامیت
25887e490f

+ 8 - 1
packaging/deb/config/config.json

@@ -9,7 +9,14 @@
       "max_memory_mb": 512,
       "eviction_threshold_percent": 80,
       "eviction_target_percent": 60,
-      "eviction_check_interval_ms": 5000
+      "eviction_check_interval_ms": 5000,
+      "eviction_chunk_size": 1000,
+      "eviction_chunk_pause_ms": 50,
+      "max_eviction_passes_per_trigger": 20,
+      "hot_write_floor_ms": 30000,
+      "memory_soft_percent": 70,
+      "memory_hard_percent": 85,
+      "memory_emergency_percent": 95
     },
     "persistence": {
       "wal_sync_interval_ms": 100,

+ 42 - 0
proto/database.proto

@@ -82,6 +82,7 @@ service DatabaseService {
     // Health and stats
     rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
     rpc GetStats(GetStatsRequest) returns (GetStatsResponse);
+    rpc GetMemoryStats(GetMemoryStatsRequest) returns (GetMemoryStatsResponse);
 
     // Read-only runtime state control
     rpc SetReadOnly(SetReadOnlyRequest) returns (SetReadOnlyResponse);
@@ -434,6 +435,17 @@ message CollectionOptions {
     repeated string sensitive_fields = 4;
     uint32 max_versions = 5;          // Max version history per document (0 = unlimited)
     uint32 vector_dimension = 6;      // Dimension of vectors stored in this collection (0 = not a vector collection)
+
+    // NEW in v1.7.0 — eviction priority for the collection.
+    // UNSPECIFIED is treated as NORMAL for backward compatibility.
+    MemoryPriority memory_priority = 7;
+}
+
+enum MemoryPriority {
+    MEMORY_PRIORITY_UNSPECIFIED = 0;   // treated as NORMAL
+    MEMORY_PRIORITY_LOW = 1;
+    MEMORY_PRIORITY_NORMAL = 2;
+    MEMORY_PRIORITY_HIGH = 3;
 }
 
 message CreateCollectionResponse {
@@ -570,6 +582,10 @@ enum EventType {
     EVENT_DELETE = 3;
     EVENT_EXPIRE = 4;
     EVENT_INVALIDATE = 5;
+
+    // NEW in v1.7.0 — memory-pressure observability events.
+    EVENT_MEMORY_PRESSURE_HIGH = 6;   // Emitted when pressure reaches hard threshold
+    EVENT_MEMORY_EVICTION_BURST = 7;  // Emitted when eviction runs a large burst
 }
 
 // ===== Replication =====
@@ -816,3 +832,29 @@ message GetReadOnlyStatusResponse {
     uint64 wal_entries_replayed = 7;
     uint32 snapshots_attempted = 8;
 }
+
+// ===== Memory Stats =====
+// Added in v1.7.0. Wire-compatible — server-side implementation lands in T9.
+
+message GetMemoryStatsRequest {}
+
+message CollectionMemoryStats {
+    string collection = 1;
+    uint64 document_count = 2;
+    uint64 estimated_bytes = 3;
+    uint64 evicted_stub_count = 4;
+    MemoryPriority priority = 5;
+}
+
+message GetMemoryStatsResponse {
+    uint64 total_memory_bytes = 1;
+    uint64 max_memory_bytes = 2;
+    uint32 pressure_percent = 3;         // current usage / max × 100
+    string pressure_level = 4;           // "normal" | "soft" | "hard" | "emergency"
+    repeated CollectionMemoryStats collections = 5;
+
+    // Most recent eviction
+    uint64 last_eviction_timestamp = 6;  // ms since epoch, 0 if none
+    uint64 last_eviction_docs = 7;
+    uint64 last_eviction_bytes_freed = 8;
+}

+ 17 - 0
service/src/database_service.cpp

@@ -336,6 +336,15 @@ DatabaseService::Config DatabaseService::parseConfig(const nlohmann::json& json)
             config.evictionThresholdPercent = memory.value("eviction_threshold_percent", config.evictionThresholdPercent);
             config.evictionTargetPercent = memory.value("eviction_target_percent", config.evictionTargetPercent);
             config.evictionCheckIntervalMs = memory.value("eviction_check_interval_ms", config.evictionCheckIntervalMs);
+
+            // NEW v1.7.0 eviction tuning (schema-only in T2; runtime use lands in T3/T4)
+            config.evictionChunkSize = memory.value("eviction_chunk_size", config.evictionChunkSize);
+            config.evictionChunkPauseMs = memory.value("eviction_chunk_pause_ms", config.evictionChunkPauseMs);
+            config.maxEvictionPassesPerTrigger = memory.value("max_eviction_passes_per_trigger", config.maxEvictionPassesPerTrigger);
+            config.hotWriteFloorMs = memory.value("hot_write_floor_ms", config.hotWriteFloorMs);
+            config.memorySoftPercent = memory.value("memory_soft_percent", config.memorySoftPercent);
+            config.memoryHardPercent = memory.value("memory_hard_percent", config.memoryHardPercent);
+            config.memoryEmergencyPercent = memory.value("memory_emergency_percent", config.memoryEmergencyPercent);
         }
 
         // Persistence settings
@@ -436,6 +445,14 @@ void DatabaseService::setupComponents() {
     storeConfig.evictionThresholdPercent = config_.evictionThresholdPercent;
     storeConfig.evictionTargetPercent = config_.evictionTargetPercent;
     storeConfig.evictionCheckIntervalMs = config_.evictionCheckIntervalMs;
+    // NEW v1.7.0 eviction tuning (wired in T2; consumed in T3/T4).
+    storeConfig.evictionChunkSize = config_.evictionChunkSize;
+    storeConfig.evictionChunkPauseMs = config_.evictionChunkPauseMs;
+    storeConfig.maxEvictionPassesPerTrigger = config_.maxEvictionPassesPerTrigger;
+    storeConfig.hotWriteFloorMs = config_.hotWriteFloorMs;
+    storeConfig.memorySoftPercent = config_.memorySoftPercent;
+    storeConfig.memoryHardPercent = config_.memoryHardPercent;
+    storeConfig.memoryEmergencyPercent = config_.memoryEmergencyPercent;
     store_ = std::make_unique<MemoryStore>(storeConfig);
 
     // Create view manager (cache loaded in initialize() after persistence recovery)

+ 12 - 0
service/src/database_service.hpp

@@ -52,6 +52,18 @@ public:
         uint32_t evictionTargetPercent = 60;     // Evict down to this %
         uint32_t evictionCheckIntervalMs = 5000; // How often to check
 
+        // NEW v1.7.0 eviction tuning — mirrors MemoryStore::Config fields.
+        // Plumbed here so the config loader can set them and setupComponents()
+        // can copy them into the MemoryStore. Schema-only in T2; behavior lands
+        // in T3/T4.
+        uint32_t evictionChunkSize = 1000;
+        uint32_t evictionChunkPauseMs = 50;
+        uint32_t maxEvictionPassesPerTrigger = 20;
+        uint32_t hotWriteFloorMs = 30000;
+        uint32_t memorySoftPercent = 70;
+        uint32_t memoryHardPercent = 85;
+        uint32_t memoryEmergencyPercent = 95;
+
         // Persistence settings
         uint32_t walSyncIntervalMs = 100;
         uint32_t snapshotIntervalSec = 3600;

+ 30 - 0
service/src/document.hpp

@@ -144,6 +144,32 @@ struct DocumentVersion {
     }
 };
 
+/**
+ * Per-collection eviction priority. Added in v1.7.0 to let operators
+ * bias the eviction selector: Low collections are evicted first, High
+ * collections last. Normal is the default and preserves v1.6.x behavior.
+ */
+enum class MemoryPriority {
+    Low = 0,
+    Normal = 1,   // default — preserves existing behavior
+    High = 2
+};
+
+inline std::string memoryPriorityToString(MemoryPriority p) {
+    switch (p) {
+        case MemoryPriority::Low:    return "low";
+        case MemoryPriority::Normal: return "normal";
+        case MemoryPriority::High:   return "high";
+    }
+    return "normal";
+}
+
+inline MemoryPriority memoryPriorityFromString(const std::string& s) {
+    if (s == "low")  return MemoryPriority::Low;
+    if (s == "high") return MemoryPriority::High;
+    return MemoryPriority::Normal;  // default for unknown/"normal"
+}
+
 /**
  * Options for creating a collection.
  * Named CollectionOptions to avoid conflict with proto-generated type.
@@ -156,6 +182,7 @@ struct CollectionOptions {
     uint32_t maxVersions = 0;                    // Max version history per document (0 = unlimited)
     bool pinned = false;                         // If true, never evict documents from this collection
     uint32_t vectorDimension = 0;                // Vector dimension for vector search (0 = disabled)
+    MemoryPriority memoryPriority = MemoryPriority::Normal;  // v1.7.0 eviction bias
 
     [[nodiscard]] nlohmann::json toJson() const {
         nlohmann::json j;
@@ -166,6 +193,7 @@ struct CollectionOptions {
         j["maxVersions"] = maxVersions;
         j["pinned"] = pinned;
         j["vector_dimension"] = vectorDimension;
+        j["memory_priority"] = memoryPriorityToString(memoryPriority);
         return j;
     }
 
@@ -179,6 +207,8 @@ struct CollectionOptions {
         opts.pinned = j.value("pinned", false);  // Backward-compatible default
         if (j.contains("vector_dimension") && j["vector_dimension"].is_number())
             opts.vectorDimension = j["vector_dimension"].get<uint32_t>();
+        if (j.contains("memory_priority") && j["memory_priority"].is_string())
+            opts.memoryPriority = memoryPriorityFromString(j["memory_priority"].get<std::string>());
         return opts;
     }
 };

+ 11 - 0
service/src/memory_store.hpp

@@ -57,6 +57,17 @@ public:
         uint32_t evictionTargetPercent;      // Evict down to this % of maxMemoryBytes
         uint32_t evictionCheckIntervalMs;    // How often to check memory usage
 
+        // NEW v1.7.0 eviction tuning — chunked eviction, memory pressure thresholds,
+        // and hot-write protection. Wired through config in T2; runtime behavior
+        // lands in later tasks (T3 state machine, T4 chunked eviction, etc.).
+        uint32_t evictionChunkSize = 1000;           // Docs evicted per chunk before yielding
+        uint32_t evictionChunkPauseMs = 50;          // Pause between chunks to let writers in
+        uint32_t maxEvictionPassesPerTrigger = 20;   // Cap passes per pressure trigger
+        uint32_t hotWriteFloorMs = 30000;            // Skip eviction of docs written within this window
+        uint32_t memorySoftPercent = 70;             // Begin trickle eviction above this %
+        uint32_t memoryHardPercent = 85;             // Aggressive eviction + emit pressure event
+        uint32_t memoryEmergencyPercent = 95;        // Refuse writes above this %
+
         Config()
             : maxMemoryBytes(800ULL * 1024 * 1024)   // 800 MB default
             , expirationCheckIntervalMs(1000)         // Check TTL every second