You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							114 lines
						
					
					
						
							3.0 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							114 lines
						
					
					
						
							3.0 KiB
						
					
					
				
								package topology
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"fmt"
							 | 
						|
									"time"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// reassignTaskStates assigns tasks to the appropriate disks
							 | 
						|
								func (at *ActiveTopology) reassignTaskStates() {
							 | 
						|
									// Clear existing task assignments
							 | 
						|
									for _, disk := range at.disks {
							 | 
						|
										disk.pendingTasks = nil
							 | 
						|
										disk.assignedTasks = nil
							 | 
						|
										disk.recentTasks = nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Reassign pending tasks
							 | 
						|
									for _, task := range at.pendingTasks {
							 | 
						|
										at.assignTaskToDisk(task)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Reassign assigned tasks
							 | 
						|
									for _, task := range at.assignedTasks {
							 | 
						|
										at.assignTaskToDisk(task)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Reassign recent tasks
							 | 
						|
									for _, task := range at.recentTasks {
							 | 
						|
										at.assignTaskToDisk(task)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// assignTaskToDisk assigns a task to the appropriate disk(s)
							 | 
						|
								func (at *ActiveTopology) assignTaskToDisk(task *taskState) {
							 | 
						|
									addedDisks := make(map[string]bool)
							 | 
						|
								
							 | 
						|
									// Local helper function to assign task to a disk and avoid code duplication
							 | 
						|
									assign := func(server string, diskID uint32) {
							 | 
						|
										key := fmt.Sprintf("%s:%d", server, diskID)
							 | 
						|
										if server == "" || addedDisks[key] {
							 | 
						|
											return
							 | 
						|
										}
							 | 
						|
										if disk, exists := at.disks[key]; exists {
							 | 
						|
											switch task.Status {
							 | 
						|
											case TaskStatusPending:
							 | 
						|
												disk.pendingTasks = append(disk.pendingTasks, task)
							 | 
						|
											case TaskStatusInProgress:
							 | 
						|
												disk.assignedTasks = append(disk.assignedTasks, task)
							 | 
						|
											case TaskStatusCompleted:
							 | 
						|
												disk.recentTasks = append(disk.recentTasks, task)
							 | 
						|
											}
							 | 
						|
											addedDisks[key] = true
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Assign to all source disks
							 | 
						|
									for _, source := range task.Sources {
							 | 
						|
										assign(source.SourceServer, source.SourceDisk)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Assign to all destination disks (duplicates automatically avoided by helper)
							 | 
						|
									for _, dest := range task.Destinations {
							 | 
						|
										assign(dest.TargetServer, dest.TargetDisk)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// isDiskAvailable checks if a disk can accept new tasks
							 | 
						|
								func (at *ActiveTopology) isDiskAvailable(disk *activeDisk, taskType TaskType) bool {
							 | 
						|
									// Check if disk has too many pending and active tasks
							 | 
						|
									activeLoad := len(disk.pendingTasks) + len(disk.assignedTasks)
							 | 
						|
									if activeLoad >= MaxConcurrentTasksPerDisk {
							 | 
						|
										return false
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Check for conflicting task types
							 | 
						|
									for _, task := range disk.assignedTasks {
							 | 
						|
										if at.areTaskTypesConflicting(task.TaskType, taskType) {
							 | 
						|
											return false
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return true
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// areTaskTypesConflicting checks if two task types conflict
							 | 
						|
								func (at *ActiveTopology) areTaskTypesConflicting(existing, new TaskType) bool {
							 | 
						|
									// Examples of conflicting task types
							 | 
						|
									conflictMap := map[TaskType][]TaskType{
							 | 
						|
										TaskTypeVacuum:        {TaskTypeBalance, TaskTypeErasureCoding},
							 | 
						|
										TaskTypeBalance:       {TaskTypeVacuum, TaskTypeErasureCoding},
							 | 
						|
										TaskTypeErasureCoding: {TaskTypeVacuum, TaskTypeBalance},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if conflicts, exists := conflictMap[existing]; exists {
							 | 
						|
										for _, conflictType := range conflicts {
							 | 
						|
											if conflictType == new {
							 | 
						|
												return true
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return false
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// cleanupRecentTasks removes old recent tasks
							 | 
						|
								func (at *ActiveTopology) cleanupRecentTasks() {
							 | 
						|
									cutoff := time.Now().Add(-time.Duration(at.recentTaskWindowSeconds) * time.Second)
							 | 
						|
								
							 | 
						|
									for taskID, task := range at.recentTasks {
							 | 
						|
										if task.CompletedAt.Before(cutoff) {
							 | 
						|
											delete(at.recentTasks, taskID)
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								}
							 |