 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			682 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			682 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package distribution provides gossip protocol for metadata synchronization
 | |
| package distribution
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"math/rand"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"chorus/pkg/dht"
 | |
| 	"chorus/pkg/config"
 | |
| 	"chorus/pkg/ucxl"
 | |
| )
 | |
| 
 | |
| // GossipProtocolImpl implements GossipProtocol interface for metadata synchronization
 | |
| type GossipProtocolImpl struct {
 | |
| 	mu                 sync.RWMutex
 | |
| 	dht                *dht.DHT
 | |
| 	config             *config.Config
 | |
| 	running            bool
 | |
| 	gossipInterval     time.Duration
 | |
| 	maxGossipPeers     int
 | |
| 	compressionEnabled bool
 | |
| 	messageBuffer      chan *GossipMessage
 | |
| 	state              *GossipState
 | |
| 	stats              *GossipStatistics
 | |
| 	metadataCache      map[string]*ContextMetadata
 | |
| 	vectorClock        map[string]int64
 | |
| 	failureDetector    *FailureDetector
 | |
| }
 | |
| 
 | |
| // GossipMessage represents a message in the gossip protocol
 | |
| type GossipMessage struct {
 | |
| 	MessageID    string                     `json:"message_id"`
 | |
| 	MessageType  GossipMessageType          `json:"message_type"`
 | |
| 	SenderID     string                     `json:"sender_id"`
 | |
| 	Timestamp    time.Time                  `json:"timestamp"`
 | |
| 	TTL          int                        `json:"ttl"`
 | |
| 	VectorClock  map[string]int64           `json:"vector_clock"`
 | |
| 	Payload      map[string]interface{}     `json:"payload"`
 | |
| 	Metadata     *GossipMessageMetadata     `json:"metadata"`
 | |
| }
 | |
| 
 | |
| // GossipMessageType represents different types of gossip messages
 | |
| type GossipMessageType string
 | |
| 
 | |
| const (
 | |
| 	GossipMessageHeartbeat     GossipMessageType = "heartbeat"
 | |
| 	GossipMessageMetadataSync  GossipMessageType = "metadata_sync"
 | |
| 	GossipMessageContextUpdate GossipMessageType = "context_update"
 | |
| 	GossipMessagePeerDiscovery GossipMessageType = "peer_discovery"
 | |
| 	GossipMessageConflictAlert GossipMessageType = "conflict_alert"
 | |
| 	GossipMessageHealthCheck   GossipMessageType = "health_check"
 | |
| )
 | |
| 
 | |
| // GossipMessageMetadata contains metadata about gossip messages
 | |
| type GossipMessageMetadata struct {
 | |
| 	Priority        Priority  `json:"priority"`
 | |
| 	Reliability     bool      `json:"reliability"`
 | |
| 	Encrypted       bool      `json:"encrypted"`
 | |
| 	Compressed      bool      `json:"compressed"`
 | |
| 	OriginalSize    int       `json:"original_size"`
 | |
| 	CompressionType string    `json:"compression_type"`
 | |
| }
 | |
| 
 | |
| // ContextMetadata represents metadata about a distributed context
 | |
| type ContextMetadata struct {
 | |
| 	Address          ucxl.Address      `json:"address"`
 | |
| 	Version          int64             `json:"version"`
 | |
| 	LastUpdated      time.Time         `json:"last_updated"`
 | |
| 	UpdatedBy        string            `json:"updated_by"`
 | |
| 	Roles            []string          `json:"roles"`
 | |
| 	Size             int64             `json:"size"`
 | |
| 	Checksum         string            `json:"checksum"`
 | |
| 	ReplicationNodes []string          `json:"replication_nodes"`
 | |
| 	VectorClock      map[string]int64  `json:"vector_clock"`
 | |
| 	Status           MetadataStatus    `json:"status"`
 | |
| }
 | |
| 
 | |
| // MetadataStatus represents the status of context metadata
 | |
| type MetadataStatus string
 | |
| 
 | |
| const (
 | |
| 	MetadataStatusActive     MetadataStatus = "active"
 | |
| 	MetadataStatusDeprecated MetadataStatus = "deprecated" 
 | |
| 	MetadataStatusDeleted    MetadataStatus = "deleted"
 | |
| 	MetadataStatusConflicted MetadataStatus = "conflicted"
 | |
| )
 | |
| 
 | |
| // FailureDetector detects failed nodes in the network
 | |
| type FailureDetector struct {
 | |
| 	mu              sync.RWMutex
 | |
| 	suspectedNodes  map[string]time.Time
 | |
| 	failedNodes     map[string]time.Time
 | |
| 	heartbeatTimeout time.Duration
 | |
| 	failureThreshold time.Duration
 | |
| }
 | |
| 
 | |
| // NewGossipProtocolImpl creates a new gossip protocol implementation
 | |
| func NewGossipProtocolImpl(dht *dht.DHT, config *config.Config) (*GossipProtocolImpl, error) {
 | |
| 	if dht == nil {
 | |
| 		return nil, fmt.Errorf("DHT instance is required")
 | |
| 	}
 | |
| 	if config == nil {
 | |
| 		return nil, fmt.Errorf("config is required")
 | |
| 	}
 | |
| 
 | |
| 	gp := &GossipProtocolImpl{
 | |
| 		dht:                dht,
 | |
| 		config:             config,
 | |
| 		running:            false,
 | |
| 		gossipInterval:     30 * time.Second,
 | |
| 		maxGossipPeers:     5,
 | |
| 		compressionEnabled: true,
 | |
| 		messageBuffer:      make(chan *GossipMessage, 1000),
 | |
| 		state: &GossipState{
 | |
| 			Running:           false,
 | |
| 			CurrentRound:      0,
 | |
| 			RoundStartTime:    time.Now(),
 | |
| 			RoundDuration:     0,
 | |
| 			ActiveConnections: 0,
 | |
| 			PendingMessages:   0,
 | |
| 			NextRoundTime:     time.Now().Add(30 * time.Second),
 | |
| 			ProtocolVersion:   "v1.0",
 | |
| 			State:             "stopped",
 | |
| 		},
 | |
| 		stats: &GossipStatistics{
 | |
| 			LastUpdated: time.Now(),
 | |
| 		},
 | |
| 		metadataCache: make(map[string]*ContextMetadata),
 | |
| 		vectorClock:   make(map[string]int64),
 | |
| 		failureDetector: &FailureDetector{
 | |
| 			suspectedNodes:   make(map[string]time.Time),
 | |
| 			failedNodes:      make(map[string]time.Time),
 | |
| 			heartbeatTimeout: 60 * time.Second,
 | |
| 			failureThreshold: 120 * time.Second,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	return gp, nil
 | |
| }
 | |
| 
 | |
| // StartGossip begins gossip protocol for metadata synchronization
 | |
| func (gp *GossipProtocolImpl) StartGossip(ctx context.Context) error {
 | |
| 	gp.mu.Lock()
 | |
| 	if gp.running {
 | |
| 		gp.mu.Unlock()
 | |
| 		return fmt.Errorf("gossip protocol already running")
 | |
| 	}
 | |
| 	gp.running = true
 | |
| 	gp.state.Running = true
 | |
| 	gp.state.State = "running"
 | |
| 	gp.mu.Unlock()
 | |
| 
 | |
| 	// Start background workers
 | |
| 	go gp.gossipWorker(ctx)
 | |
| 	go gp.messageProcessor(ctx)
 | |
| 	go gp.heartbeatSender(ctx)
 | |
| 	go gp.failureDetectorWorker(ctx)
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // StopGossip stops gossip protocol
 | |
| func (gp *GossipProtocolImpl) StopGossip(ctx context.Context) error {
 | |
| 	gp.mu.Lock()
 | |
| 	defer gp.mu.Unlock()
 | |
| 
 | |
| 	if !gp.running {
 | |
| 		return fmt.Errorf("gossip protocol not running")
 | |
| 	}
 | |
| 
 | |
| 	gp.running = false
 | |
| 	gp.state.Running = false
 | |
| 	gp.state.State = "stopped"
 | |
| 	close(gp.messageBuffer)
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // GossipMetadata exchanges metadata with peer nodes
 | |
| func (gp *GossipProtocolImpl) GossipMetadata(ctx context.Context, peer string) error {
 | |
| 	if !gp.running {
 | |
| 		return fmt.Errorf("gossip protocol not running")
 | |
| 	}
 | |
| 
 | |
| 	// Create metadata sync message
 | |
| 	message := &GossipMessage{
 | |
| 		MessageID:   gp.generateMessageID(),
 | |
| 		MessageType: GossipMessageMetadataSync,
 | |
| 		SenderID:    gp.config.Agent.ID,
 | |
| 		Timestamp:   time.Now(),
 | |
| 		TTL:         3, // Max 3 hops
 | |
| 		VectorClock: gp.getVectorClock(),
 | |
| 		Payload: map[string]interface{}{
 | |
| 			"metadata_cache": gp.getMetadataCacheSnapshot(),
 | |
| 			"request_sync":   true,
 | |
| 		},
 | |
| 		Metadata: &GossipMessageMetadata{
 | |
| 			Priority:    PriorityNormal,
 | |
| 			Reliability: true,
 | |
| 			Encrypted:   false,
 | |
| 			Compressed:  gp.compressionEnabled,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// Send to specific peer
 | |
| 	return gp.sendMessage(ctx, message, peer)
 | |
| }
 | |
| 
 | |
| // GetGossipState returns current gossip protocol state
 | |
| func (gp *GossipProtocolImpl) GetGossipState() (*GossipState, error) {
 | |
| 	gp.mu.RLock()
 | |
| 	defer gp.mu.RUnlock()
 | |
| 
 | |
| 	// Update dynamic state
 | |
| 	gp.state.ActiveConnections = len(gp.dht.GetConnectedPeers())
 | |
| 	gp.state.PendingMessages = len(gp.messageBuffer)
 | |
| 
 | |
| 	return gp.state, nil
 | |
| }
 | |
| 
 | |
| // SetGossipInterval configures gossip frequency
 | |
| func (gp *GossipProtocolImpl) SetGossipInterval(interval time.Duration) error {
 | |
| 	if interval < time.Second {
 | |
| 		return fmt.Errorf("gossip interval too short (minimum 1 second)")
 | |
| 	}
 | |
| 	if interval > time.Hour {
 | |
| 		return fmt.Errorf("gossip interval too long (maximum 1 hour)")
 | |
| 	}
 | |
| 
 | |
| 	gp.mu.Lock()
 | |
| 	gp.gossipInterval = interval
 | |
| 	gp.state.NextRoundTime = time.Now().Add(interval)
 | |
| 	gp.mu.Unlock()
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // GetGossipStats returns gossip protocol statistics
 | |
| func (gp *GossipProtocolImpl) GetGossipStats() (*GossipStatistics, error) {
 | |
| 	gp.mu.RLock()
 | |
| 	defer gp.mu.RUnlock()
 | |
| 
 | |
| 	// Update real-time stats
 | |
| 	gp.stats.ActivePeers = len(gp.dht.GetConnectedPeers())
 | |
| 	gp.stats.LastGossipTime = time.Now()
 | |
| 	gp.stats.LastUpdated = time.Now()
 | |
| 
 | |
| 	return gp.stats, nil
 | |
| }
 | |
| 
 | |
| // Background workers
 | |
| 
 | |
| func (gp *GossipProtocolImpl) gossipWorker(ctx context.Context) {
 | |
| 	ticker := time.NewTicker(gp.gossipInterval)
 | |
| 	defer ticker.Stop()
 | |
| 
 | |
| 	for {
 | |
| 		select {
 | |
| 		case <-ctx.Done():
 | |
| 			return
 | |
| 		case <-ticker.C:
 | |
| 			if gp.running {
 | |
| 				gp.performGossipRound(ctx)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) messageProcessor(ctx context.Context) {
 | |
| 	for {
 | |
| 		select {
 | |
| 		case <-ctx.Done():
 | |
| 			return
 | |
| 		case message := <-gp.messageBuffer:
 | |
| 			if message == nil {
 | |
| 				return // Channel closed
 | |
| 			}
 | |
| 			gp.processIncomingMessage(ctx, message)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) heartbeatSender(ctx context.Context) {
 | |
| 	ticker := time.NewTicker(30 * time.Second)
 | |
| 	defer ticker.Stop()
 | |
| 
 | |
| 	for {
 | |
| 		select {
 | |
| 		case <-ctx.Done():
 | |
| 			return
 | |
| 		case <-ticker.C:
 | |
| 			if gp.running {
 | |
| 				gp.sendHeartbeat(ctx)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) failureDetectorWorker(ctx context.Context) {
 | |
| 	ticker := time.NewTicker(60 * time.Second)
 | |
| 	defer ticker.Stop()
 | |
| 
 | |
| 	for {
 | |
| 		select {
 | |
| 		case <-ctx.Done():
 | |
| 			return
 | |
| 		case <-ticker.C:
 | |
| 			if gp.running {
 | |
| 				gp.detectFailures()
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Core gossip operations
 | |
| 
 | |
| func (gp *GossipProtocolImpl) performGossipRound(ctx context.Context) {
 | |
| 	start := time.Now()
 | |
| 
 | |
| 	gp.mu.Lock()
 | |
| 	gp.state.CurrentRound++
 | |
| 	gp.state.RoundStartTime = start
 | |
| 	gp.stats.GossipRounds++
 | |
| 	gp.mu.Unlock()
 | |
| 
 | |
| 	// Select random peers for gossip
 | |
| 	peers := gp.selectGossipPeers()
 | |
| 
 | |
| 	// Perform gossip with selected peers
 | |
| 	for _, peer := range peers {
 | |
| 		go func(peerID string) {
 | |
| 			if err := gp.GossipMetadata(ctx, peerID); err != nil {
 | |
| 				gp.mu.Lock()
 | |
| 				gp.stats.NetworkErrors++
 | |
| 				gp.mu.Unlock()
 | |
| 			}
 | |
| 		}(peer)
 | |
| 	}
 | |
| 
 | |
| 	// Update round duration
 | |
| 	gp.mu.Lock()
 | |
| 	gp.state.RoundDuration = time.Since(start)
 | |
| 	gp.state.NextRoundTime = time.Now().Add(gp.gossipInterval)
 | |
| 	gp.stats.AverageRoundTime = (gp.stats.AverageRoundTime + gp.state.RoundDuration) / 2
 | |
| 	gp.mu.Unlock()
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) selectGossipPeers() []string {
 | |
| 	connectedPeers := gp.dht.GetConnectedPeers()
 | |
| 	if len(connectedPeers) == 0 {
 | |
| 		return []string{}
 | |
| 	}
 | |
| 
 | |
| 	// Randomly select up to maxGossipPeers
 | |
| 	selectedCount := min(len(connectedPeers), gp.maxGossipPeers)
 | |
| 	selected := make([]string, 0, selectedCount)
 | |
| 
 | |
| 	// Simple random selection
 | |
| 	perm := rand.Perm(len(connectedPeers))
 | |
| 	for i := 0; i < selectedCount; i++ {
 | |
| 		selected = append(selected, connectedPeers[perm[i]].String())
 | |
| 	}
 | |
| 
 | |
| 	return selected
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) processIncomingMessage(ctx context.Context, message *GossipMessage) {
 | |
| 	// Update vector clock
 | |
| 	gp.updateVectorClock(message.VectorClock)
 | |
| 
 | |
| 	// Process based on message type
 | |
| 	switch message.MessageType {
 | |
| 	case GossipMessageHeartbeat:
 | |
| 		gp.processHeartbeat(message)
 | |
| 	case GossipMessageMetadataSync:
 | |
| 		gp.processMetadataSync(ctx, message)
 | |
| 	case GossipMessageContextUpdate:
 | |
| 		gp.processContextUpdate(message)
 | |
| 	case GossipMessagePeerDiscovery:
 | |
| 		gp.processPeerDiscovery(message)
 | |
| 	case GossipMessageConflictAlert:
 | |
| 		gp.processConflictAlert(message)
 | |
| 	case GossipMessageHealthCheck:
 | |
| 		gp.processHealthCheck(message)
 | |
| 	default:
 | |
| 		gp.mu.Lock()
 | |
| 		gp.stats.ProtocolErrors++
 | |
| 		gp.mu.Unlock()
 | |
| 	}
 | |
| 
 | |
| 	// Update statistics
 | |
| 	gp.mu.Lock()
 | |
| 	gp.stats.MessagesReceived++
 | |
| 	gp.mu.Unlock()
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) sendMessage(ctx context.Context, message *GossipMessage, peer string) error {
 | |
| 	// Serialize message
 | |
| 	messageBytes, err := json.Marshal(message)
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("failed to serialize message: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	// Compress if enabled
 | |
| 	if gp.compressionEnabled && message.Metadata != nil {
 | |
| 		compressedBytes, err := gp.compressMessage(messageBytes)
 | |
| 		if err == nil {
 | |
| 			message.Metadata.Compressed = true
 | |
| 			message.Metadata.OriginalSize = len(messageBytes)
 | |
| 			message.Metadata.CompressionType = "gzip"
 | |
| 			messageBytes = compressedBytes
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Send via DHT (in a real implementation, this would use direct peer connections)
 | |
| 	key := fmt.Sprintf("gossip:%s:%s", peer, message.MessageID)
 | |
| 	if err := gp.dht.PutValue(ctx, key, messageBytes); err != nil {
 | |
| 		gp.mu.Lock()
 | |
| 		gp.stats.MessagesDropped++
 | |
| 		gp.mu.Unlock()
 | |
| 		return fmt.Errorf("failed to send gossip message: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	gp.mu.Lock()
 | |
| 	gp.stats.MessagesSent++
 | |
| 	gp.mu.Unlock()
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) sendHeartbeat(ctx context.Context) {
 | |
| 	message := &GossipMessage{
 | |
| 		MessageID:   gp.generateMessageID(),
 | |
| 		MessageType: GossipMessageHeartbeat,
 | |
| 		SenderID:    gp.config.Agent.ID,
 | |
| 		Timestamp:   time.Now(),
 | |
| 		TTL:         1, // Heartbeats don't propagate
 | |
| 		VectorClock: gp.getVectorClock(),
 | |
| 		Payload: map[string]interface{}{
 | |
| 			"status":     "alive",
 | |
| 			"load":       gp.calculateNodeLoad(),
 | |
| 			"version":    "1.0.0",
 | |
| 			"capabilities": []string{"context_distribution", "replication"},
 | |
| 		},
 | |
| 		Metadata: &GossipMessageMetadata{
 | |
| 			Priority:    PriorityHigh,
 | |
| 			Reliability: false, // Heartbeats can be lost
 | |
| 			Encrypted:   false,
 | |
| 			Compressed:  false,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// Send to all connected peers
 | |
| 	peers := gp.selectGossipPeers()
 | |
| 	for _, peer := range peers {
 | |
| 		go func(peerID string) {
 | |
| 			gp.sendMessage(ctx, message, peerID)
 | |
| 		}(peer)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) detectFailures() {
 | |
| 	now := time.Now()
 | |
| 	gp.failureDetector.mu.Lock()
 | |
| 	defer gp.failureDetector.mu.Unlock()
 | |
| 
 | |
| 	// Check for suspected nodes that haven't responded
 | |
| 	for nodeID, suspectedTime := range gp.failureDetector.suspectedNodes {
 | |
| 		if now.Sub(suspectedTime) > gp.failureDetector.failureThreshold {
 | |
| 			// Mark as failed
 | |
| 			gp.failureDetector.failedNodes[nodeID] = now
 | |
| 			delete(gp.failureDetector.suspectedNodes, nodeID)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Clean up old failure records
 | |
| 	for nodeID, failedTime := range gp.failureDetector.failedNodes {
 | |
| 		if now.Sub(failedTime) > 24*time.Hour {
 | |
| 			delete(gp.failureDetector.failedNodes, nodeID)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Message processing handlers
 | |
| 
 | |
| func (gp *GossipProtocolImpl) processHeartbeat(message *GossipMessage) {
 | |
| 	// Remove from suspected/failed lists if present
 | |
| 	gp.failureDetector.mu.Lock()
 | |
| 	delete(gp.failureDetector.suspectedNodes, message.SenderID)
 | |
| 	delete(gp.failureDetector.failedNodes, message.SenderID)
 | |
| 	gp.failureDetector.mu.Unlock()
 | |
| 
 | |
| 	// Update peer information
 | |
| 	if load, ok := message.Payload["load"].(float64); ok {
 | |
| 		// Store peer load information
 | |
| 		_ = load
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) processMetadataSync(ctx context.Context, message *GossipMessage) {
 | |
| 	// Extract metadata cache from payload
 | |
| 	if metadataCache, ok := message.Payload["metadata_cache"].(map[string]interface{}); ok {
 | |
| 		gp.mergeMetadataCache(metadataCache)
 | |
| 	}
 | |
| 
 | |
| 	// If this is a sync request, respond with our metadata
 | |
| 	if requestSync, ok := message.Payload["request_sync"].(bool); ok && requestSync {
 | |
| 		responseMessage := &GossipMessage{
 | |
| 			MessageID:   gp.generateMessageID(),
 | |
| 			MessageType: GossipMessageMetadataSync,
 | |
| 			SenderID:    gp.config.Agent.ID,
 | |
| 			Timestamp:   time.Now(),
 | |
| 			TTL:         1,
 | |
| 			VectorClock: gp.getVectorClock(),
 | |
| 			Payload: map[string]interface{}{
 | |
| 				"metadata_cache": gp.getMetadataCacheSnapshot(),
 | |
| 				"request_sync":   false,
 | |
| 			},
 | |
| 			Metadata: &GossipMessageMetadata{
 | |
| 				Priority:    PriorityNormal,
 | |
| 				Reliability: true,
 | |
| 				Encrypted:   false,
 | |
| 				Compressed:  gp.compressionEnabled,
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		go func() {
 | |
| 			gp.sendMessage(ctx, responseMessage, message.SenderID)
 | |
| 		}()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) processContextUpdate(message *GossipMessage) {
 | |
| 	// Handle context update notifications
 | |
| 	if address, ok := message.Payload["address"].(string); ok {
 | |
| 		if version, ok := message.Payload["version"].(float64); ok {
 | |
| 			gp.updateContextMetadata(address, int64(version), message.SenderID)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) processPeerDiscovery(message *GossipMessage) {
 | |
| 	// Handle peer discovery messages
 | |
| 	if peers, ok := message.Payload["peers"].([]interface{}); ok {
 | |
| 		for _, peerData := range peers {
 | |
| 			if peer, ok := peerData.(string); ok {
 | |
| 				// Add discovered peer to our peer list
 | |
| 				_ = peer
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) processConflictAlert(message *GossipMessage) {
 | |
| 	// Handle conflict alert messages
 | |
| 	if address, ok := message.Payload["address"].(string); ok {
 | |
| 		// Mark context as conflicted in our metadata cache
 | |
| 		gp.mu.Lock()
 | |
| 		if metadata, exists := gp.metadataCache[address]; exists {
 | |
| 			metadata.Status = MetadataStatusConflicted
 | |
| 		}
 | |
| 		gp.mu.Unlock()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) processHealthCheck(message *GossipMessage) {
 | |
| 	// Respond to health check with our status
 | |
| 	// Implementation would send back health information
 | |
| }
 | |
| 
 | |
| // Helper methods
 | |
| 
 | |
| func (gp *GossipProtocolImpl) generateMessageID() string {
 | |
| 	return fmt.Sprintf("%s-%d", gp.config.Agent.ID, time.Now().UnixNano())
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) getVectorClock() map[string]int64 {
 | |
| 	gp.mu.RLock()
 | |
| 	defer gp.mu.RUnlock()
 | |
| 
 | |
| 	clock := make(map[string]int64)
 | |
| 	for nodeID, timestamp := range gp.vectorClock {
 | |
| 		clock[nodeID] = timestamp
 | |
| 	}
 | |
| 	clock[gp.config.Agent.ID] = time.Now().Unix()
 | |
| 
 | |
| 	return clock
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) updateVectorClock(remoteClock map[string]int64) {
 | |
| 	gp.mu.Lock()
 | |
| 	defer gp.mu.Unlock()
 | |
| 
 | |
| 	for nodeID, timestamp := range remoteClock {
 | |
| 		if existingTimestamp, exists := gp.vectorClock[nodeID]; !exists || timestamp > existingTimestamp {
 | |
| 			gp.vectorClock[nodeID] = timestamp
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) getMetadataCacheSnapshot() map[string]*ContextMetadata {
 | |
| 	gp.mu.RLock()
 | |
| 	defer gp.mu.RUnlock()
 | |
| 
 | |
| 	snapshot := make(map[string]*ContextMetadata)
 | |
| 	for address, metadata := range gp.metadataCache {
 | |
| 		// Deep copy metadata
 | |
| 		snapshot[address] = &ContextMetadata{
 | |
| 			Address:          metadata.Address,
 | |
| 			Version:          metadata.Version,
 | |
| 			LastUpdated:      metadata.LastUpdated,
 | |
| 			UpdatedBy:        metadata.UpdatedBy,
 | |
| 			Roles:            append([]string{}, metadata.Roles...),
 | |
| 			Size:             metadata.Size,
 | |
| 			Checksum:         metadata.Checksum,
 | |
| 			ReplicationNodes: append([]string{}, metadata.ReplicationNodes...),
 | |
| 			VectorClock:      make(map[string]int64),
 | |
| 			Status:           metadata.Status,
 | |
| 		}
 | |
| 		for k, v := range metadata.VectorClock {
 | |
| 			snapshot[address].VectorClock[k] = v
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return snapshot
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) mergeMetadataCache(remoteCache map[string]interface{}) {
 | |
| 	gp.mu.Lock()
 | |
| 	defer gp.mu.Unlock()
 | |
| 
 | |
| 	// Simplified merge logic - in production would be more sophisticated
 | |
| 	for address, metadataInterface := range remoteCache {
 | |
| 		if metadataMap, ok := metadataInterface.(map[string]interface{}); ok {
 | |
| 			// Convert map to ContextMetadata struct
 | |
| 			// This is simplified - production code would use proper deserialization
 | |
| 			if version, ok := metadataMap["version"].(float64); ok {
 | |
| 				if existing, exists := gp.metadataCache[address]; !exists || int64(version) > existing.Version {
 | |
| 					// Update with newer version
 | |
| 					// Implementation would properly deserialize the metadata
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) updateContextMetadata(address string, version int64, updatedBy string) {
 | |
| 	gp.mu.Lock()
 | |
| 	defer gp.mu.Unlock()
 | |
| 
 | |
| 	if existing, exists := gp.metadataCache[address]; exists && version > existing.Version {
 | |
| 		existing.Version = version
 | |
| 		existing.LastUpdated = time.Now()
 | |
| 		existing.UpdatedBy = updatedBy
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) calculateNodeLoad() float64 {
 | |
| 	// Calculate current node load (simplified)
 | |
| 	gp.mu.RLock()
 | |
| 	metadataCount := len(gp.metadataCache)
 | |
| 	gp.mu.RUnlock()
 | |
| 
 | |
| 	return float64(metadataCount) / 100.0 // Normalize to [0,1] range
 | |
| }
 | |
| 
 | |
| func (gp *GossipProtocolImpl) compressMessage(data []byte) ([]byte, error) {
 | |
| 	// Simplified compression - would use actual compression in production
 | |
| 	return data, nil
 | |
| }
 | |
| 
 | |
| // min returns the minimum of two integers
 | |
| func min(a, b int) int {
 | |
| 	if a < b {
 | |
| 		return a
 | |
| 	}
 | |
| 	return b
 | |
| } |