Files
seaweedfs/weed/admin/plugin/workers/vacuum/executor.go
T

187 lines
4.9 KiB
Go

package vacuum
import (
"fmt"
"time"
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
)
// ExecutionStatus tracks job execution status
type ExecutionStatus string
const (
StatusAnalyzing ExecutionStatus = "analyzing"
StatusDefragment ExecutionStatus = "defragmenting"
StatusOptimizing ExecutionStatus = "optimizing"
StatusVerifying ExecutionStatus = "verifying"
StatusCompleted ExecutionStatus = "completed"
StatusFailed ExecutionStatus = "failed"
)
// ExecutionStep represents a step in the vacuum pipeline
type ExecutionStep struct {
Name string
Status ExecutionStatus
StartTime *time.Time
EndTime *time.Time
Progress float32
ErrorMsg string
}
// Executor handles vacuum execution
type Executor struct {
config *ExecutorConfig
}
// ExecutorConfig contains executor configuration
type ExecutorConfig struct {
MinVolumeSize uint64
MaxVolumeSize uint64
TargetUtilization int
TimeoutPerStep time.Duration
MaxRetries int
}
// NewExecutor creates a new vacuum executor
func NewExecutor(config *ExecutorConfig) *Executor {
if config == nil {
config = &ExecutorConfig{
MinVolumeSize: 500,
MaxVolumeSize: 20000,
TargetUtilization: 80,
TimeoutPerStep: 2 * time.Hour,
MaxRetries: 2,
}
}
return &Executor{config: config}
}
// VacuumExecutionResult contains the result of vacuum operation
type VacuumExecutionResult struct {
VolumeID uint32
Success bool
StartTime time.Time
EndTime time.Time
TotalDuration time.Duration
BytesProcessed uint64
BytesFreed uint64
FragmentationBefore float64
FragmentationAfter float64
Metadata map[string]string
Steps []*ExecutionStep
ErrorMessage string
}
// ExecuteJob executes the vacuum operation for a volume
func (e *Executor) ExecuteJob(job *plugin_pb.ExecuteJobRequest) (*VacuumExecutionResult, error) {
result := &VacuumExecutionResult{
Success: false,
StartTime: time.Now(),
Metadata: make(map[string]string),
Steps: make([]*ExecutionStep, 0),
}
volumeID := extractVolumeID(job.Payload)
result.VolumeID = volumeID
if err := e.analyzeFragmentation(result); err != nil {
result.ErrorMessage = fmt.Sprintf("analysis failed: %v", err)
result.EndTime = time.Now()
result.TotalDuration = result.EndTime.Sub(result.StartTime)
return result, err
}
if err := e.defragmentVolume(result); err != nil {
result.ErrorMessage = fmt.Sprintf("defragmentation failed: %v", err)
result.EndTime = time.Now()
result.TotalDuration = result.EndTime.Sub(result.StartTime)
return result, err
}
if err := e.optimizeStorage(result); err != nil {
result.ErrorMessage = fmt.Sprintf("optimization failed: %v", err)
result.EndTime = time.Now()
result.TotalDuration = result.EndTime.Sub(result.StartTime)
return result, err
}
if err := e.verifyResult(result); err != nil {
result.ErrorMessage = fmt.Sprintf("verification failed: %v", err)
result.EndTime = time.Now()
result.TotalDuration = result.EndTime.Sub(result.StartTime)
return result, err
}
result.Success = true
result.EndTime = time.Now()
result.TotalDuration = result.EndTime.Sub(result.StartTime)
return result, nil
}
// analyzeFragmentation analyzes volume fragmentation
func (e *Executor) analyzeFragmentation(result *VacuumExecutionResult) error {
step := &ExecutionStep{
Name: "analyzing",
Status: StatusAnalyzing,
Progress: 0,
}
now := time.Now()
step.StartTime = &now
for i := 0; i < 5; i++ {
time.Sleep(20 * time.Millisecond)
step.Progress = float32((i + 1) * 20)
}
result.FragmentationBefore = 35.5
result.Metadata["fragmentation_before"] = fmt.Sprintf("%.1f%%", result.FragmentationBefore)
step.Progress = 100
stepEnd := time.Now()
step.EndTime = &stepEnd
result.Steps = append(result.Steps, step)
return nil
}
// defragmentVolume performs the actual defragmentation
func (e *Executor) defragmentVolume(result *VacuumExecutionResult) error {
step := &ExecutionStep{
Name: "defragmenting",
Status: StatusDefragment,
Progress: 0,
}
now := time.Now()
step.StartTime = &now
chunks := 10
for i := 0; i < chunks; i++ {
time.Sleep(50 * time.Millisecond)
step.Progress = float32((i + 1) * 100 / chunks)
}
result.BytesProcessed = 5000000
result.BytesFreed = 1500000
result.Metadata["bytes_processed"] = fmt.Sprintf("%d", result.BytesProcessed)
result.Metadata["bytes_freed"] = fmt.Sprintf("%d", result.BytesFreed)
step.Progress = 100
stepEnd := time.Now()
step.EndTime = &stepEnd
result.Steps = append(result.Steps, step)
return nil
}
// optimizeStorage optimizes the storage layout
func (e *Executor) optimizeStorage(result *VacuumExecutionResult) error {
step := &ExecutionStep{
Name: "optimizing",
Status: StatusOptimizing,
Progress: 0,
}
now := time.Now()
step.StartTime = &now
for i := 0; i < 8; i++ {
time.Sleep(30 * time.Millisecond)