From c78c57d4369f169b558ce3dac8d7b82afb4d1183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9=20=D0=A1?= =?UTF-8?q?=D0=B0=D1=84=D1=80=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Thu, 11 Jun 2026 17:37:51 +0000 Subject: [PATCH] Upload files to "internal/cluster" --- internal/cluster/types.go | 286 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 internal/cluster/types.go diff --git a/internal/cluster/types.go b/internal/cluster/types.go new file mode 100644 index 0000000..de98ac8 --- /dev/null +++ b/internal/cluster/types.go @@ -0,0 +1,286 @@ +/* + * 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() +}