/* * Copyright 2026 Safronov Grigorii * * Licensed under the CDDL, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * https://opensource.org/licenses/CDDL-1.0 */ // Файл: internal/cluster/types.go // Назначение: Общие типы данных для кластерных операций с поддержкой временных меток package cluster import ( "fmt" "time" ) // NodeInfo представляет информацию об узле для координатора type NodeInfo struct { ID string `json:"id"` IP string `json:"ip"` Port int `json:"port"` Status string `json:"status"` LastSeen int64 `json:"last_seen"` JoinedAt int64 `json:"joined_at"` UpdatedAt int64 `json:"updated_at"` Version uint64 `json:"version"` } // NOTE: ClusterStatus structure is defined in raft_coordinator.go // to avoid duplication and ensure consistency. // ClusterHealth представляет информацию о здоровье кластера type ClusterHealth struct { Nodes map[string]*NodeHealth `json:"nodes"` OverallScore float64 `json:"overall_score"` Recommendations string `json:"recommendations"` CheckedAt int64 `json:"checked_at"` } // NodeHealth представляет здоровье отдельного узла type NodeHealth struct { Status string `json:"status"` LatencyMs int64 `json:"latency_ms"` LastCheck int64 `json:"last_check"` LastSuccess int64 `json:"last_success"` LastFailure int64 `json:"last_failure"` FailureCount int `json:"failure_count"` } // NodeRequest представляет запрос от одного узла к другому type NodeRequest struct { Type string `json:"type"` Data []byte `json:"data"` FromNode string `json:"from_node"` RequestID string `json:"request_id"` Timestamp int64 `json:"timestamp"` } // ShardInfo представляет информацию о шарде type ShardInfo struct { ID string `json:"id"` Name string `json:"name"` Nodes []string `json:"nodes"` LeaderNode string `json:"leader_node"` Status string `json:"status"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` LastRebalanced int64 `json:"last_rebalanced"` DocumentCount int64 `json:"document_count"` SizeBytes int64 `json:"size_bytes"` } // ReplicationLogEntry представляет запись в журнале репликации type ReplicationLogEntry struct { ID string `json:"id"` Timestamp int64 `json:"timestamp"` SourceNode string `json:"source_node"` TargetNode string `json:"target_node"` Operation string `json:"operation"` Database string `json:"database"` Collection string `json:"collection"` DocumentID string `json:"document_id"` Status string `json:"status"` DurationMs int64 `json:"duration_ms"` Error string `json:"error,omitempty"` Details map[string]interface{} `json:"details,omitempty"` } // ========== СТРУКТУРЫ ДЛЯ PRODUCTION-READY РЕАЛИЗАЦИИ ========== // ConnectionInfo содержит информацию о соединении с узлом type ConnectionInfo struct { NodeID string `json:"node_id"` Address string `json:"address"` IsHealthy bool `json:"is_healthy"` LastUsed int64 `json:"last_used"` Failures uint32 `json:"failures"` ConnectedAt int64 `json:"connected_at"` } // ClusterOperation представляет операцию в кластере type ClusterOperation struct { ID string `json:"id"` Type string `json:"type"` // "add_node", "remove_node", "rebalance", "reshard" Status string `json:"status"` // "pending", "in_progress", "completed", "failed" CreatedAt int64 `json:"created_at"` StartedAt int64 `json:"started_at"` CompletedAt int64 `json:"completed_at"` Details map[string]interface{} `json:"details"` Error string `json:"error,omitempty"` } // WorkerPoolStats содержит статистику пула воркеров type WorkerPoolStats struct { MaxWorkers int `json:"max_workers"` ActiveTasks int32 `json:"active_tasks"` TotalTasks uint64 `json:"total_tasks"` FailedTasks uint64 `json:"failed_tasks"` QueueSize int `json:"queue_size"` QueueCapacity int `json:"queue_capacity"` } // ========== МЕТОДЫ ДЛЯ NodeInfo ========== // NodeJoinedAt возвращает человекочитаемое время присоединения узла func (n *NodeInfo) NodeJoinedAt() string { if n.JoinedAt == 0 { return "not joined" } return time.UnixMilli(n.JoinedAt).Format("2006-01-02 15:04:05.000") } // LastSeenAt возвращает человекочитаемое время последнего контакта func (n *NodeInfo) LastSeenAt() string { if n.LastSeen == 0 { return "never" } return time.UnixMilli(n.LastSeen).Format("2006-01-02 15:04:05.000") } // GetUptime возвращает время жизни узла в кластере func (n *NodeInfo) GetUptime() time.Duration { if n.JoinedAt == 0 { return 0 } return time.Duration(time.Now().UnixMilli()-n.JoinedAt) * time.Millisecond } // IsHealthy проверяет, здоров ли узел func (n *NodeInfo) IsHealthy() bool { return n.Status == "active" && time.Now().UnixMilli()-n.LastSeen < 30000 } // GetAddress возвращает адрес узла func (n *NodeInfo) GetAddress() string { return fmt.Sprintf("%s:%d", n.IP, n.Port) } // ========== МЕТОДЫ ДЛЯ ShardInfo ========== // GetUptime возвращает время жизни шарда func (s *ShardInfo) GetUptime() time.Duration { if s.CreatedAt == 0 { return 0 } return time.Duration(time.Now().UnixMilli()-s.CreatedAt) * time.Millisecond } // IsHealthy проверяет, здоров ли шард func (s *ShardInfo) IsHealthy() bool { return s.Status == "active" } // GetLastRebalancedStr возвращает человекочитаемое время последней ребалансировки func (s *ShardInfo) GetLastRebalancedStr() string { if s.LastRebalanced == 0 { return "never" } return time.UnixMilli(s.LastRebalanced).Format("2006-01-02 15:04:05.000") } // GetCreatedAtStr возвращает человекочитаемое время создания func (s *ShardInfo) GetCreatedAtStr() string { if s.CreatedAt == 0 { return "unknown" } return time.UnixMilli(s.CreatedAt).Format("2006-01-02 15:04:05.000") } // GetUpdatedAtStr возвращает человекочитаемое время обновления func (s *ShardInfo) GetUpdatedAtStr() string { if s.UpdatedAt == 0 { return "unknown" } return time.UnixMilli(s.UpdatedAt).Format("2006-01-02 15:04:05.000") } // GetLeaderNode возвращает лидера шарда func (s *ShardInfo) GetLeaderNode() string { return s.LeaderNode } // GetNodeCount возвращает количество узлов в шарде func (s *ShardInfo) GetNodeCount() int { return len(s.Nodes) } // GetReplicationFactor возвращает фактор репликации шарда func (s *ShardInfo) GetReplicationFactor() int { return len(s.Nodes) } // ========== МЕТОДЫ ДЛЯ ReplicationLogEntry ========== // GetTimestampStr возвращает человекочитаемое время записи репликации func (r *ReplicationLogEntry) GetTimestampStr() string { if r.Timestamp == 0 { return "unknown" } return time.UnixMilli(r.Timestamp).Format("2006-01-02 15:04:05.000") } // IsSuccess проверяет, успешна ли операция репликации func (r *ReplicationLogEntry) IsSuccess() bool { return r.Status == "success" } // GetDuration возвращает длительность операции func (r *ReplicationLogEntry) GetDuration() time.Duration { return time.Duration(r.DurationMs) * time.Millisecond } // ========== МЕТОДЫ ДЛЯ ClusterHealth ========== // IsHealthy проверяет, здоров ли кластер в целом func (ch *ClusterHealth) IsHealthy() bool { return ch.OverallScore >= 80 } // IsDegraded проверяет, деградирован ли кластер func (ch *ClusterHealth) IsDegraded() bool { return ch.OverallScore >= 50 && ch.OverallScore < 80 } // IsCritical проверяет, находится ли кластер в критическом состоянии func (ch *ClusterHealth) IsCritical() bool { return ch.OverallScore < 50 } // GetHealthyNodes возвращает количество здоровых узлов func (ch *ClusterHealth) GetHealthyNodes() int { count := 0 for _, node := range ch.Nodes { if node.Status == "active" { count++ } } return count } // ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ========== // GetTimestamp возвращает текущий timestamp в миллисекундах func GetTimestamp() int64 { return time.Now().UnixMilli() } // FormatTimestamp форматирует timestamp в человекочитаемый вид func FormatTimestamp(ts int64) string { if ts == 0 { return "never" } return time.UnixMilli(ts).Format("2006-01-02 15:04:05.000") } // IsTimestampExpired проверяет, истёк ли timestamp func IsTimestampExpired(ts int64, ttl time.Duration) bool { if ts == 0 { return true } return time.Now().UnixMilli()-ts > ttl.Milliseconds() }