Files
futriix/internal/cluster/types.go

287 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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()
}