mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-19 13:00:45 +02:00
147 lines
3.7 KiB
Go
147 lines
3.7 KiB
Go
package vacuum
|
|
|
|
import (
|
|
"sort"
|
|
)
|
|
|
|
// VacuumCandidate represents a volume eligible for vacuum
|
|
type VacuumCandidate struct {
|
|
VolumeID uint32
|
|
DataNodeID string
|
|
Size uint64
|
|
UsedSpace uint64
|
|
DeadSpace uint64
|
|
DeadSpacePercent float64
|
|
ReplicaCount int
|
|
RackID string
|
|
DataCenterID string
|
|
FileCount int64
|
|
LastModified int64
|
|
CanVacuum bool
|
|
FragmentationScore float64
|
|
Reason string
|
|
}
|
|
|
|
// DetectionOptions contains options for detection
|
|
type DetectionOptions struct {
|
|
MinVolumeSize uint64
|
|
MaxVolumeSize uint64
|
|
DeadSpaceThreshold int
|
|
TargetUtilization int
|
|
ExcludeNodes []string
|
|
PreferredNodes []string
|
|
}
|
|
|
|
// Detector scans for vacuum candidates
|
|
type Detector struct {
|
|
config DetectionOptions
|
|
}
|
|
|
|
// NewDetector creates a new vacuum detector
|
|
func NewDetector(opts DetectionOptions) *Detector {
|
|
return &Detector{
|
|
config: opts,
|
|
}
|
|
}
|
|
|
|
// DetectJobs scans volumes for vacuum candidates
|
|
func (d *Detector) DetectJobs(volumeMetrics map[uint32]*VolumeMetric) ([]*VacuumCandidate, error) {
|
|
candidates := make([]*VacuumCandidate, 0)
|
|
|
|
for volumeID, metric := range volumeMetrics {
|
|
candidate, shouldInclude := d.evaluateVolume(volumeID, metric)
|
|
if shouldInclude {
|
|
candidates = append(candidates, candidate)
|
|
}
|
|
}
|
|
|
|
d.SortByFragmentation(candidates)
|
|
return candidates, nil
|
|
}
|
|
|
|
// evaluateVolume checks if a volume should be vacuumed
|
|
func (d *Detector) evaluateVolume(volumeID uint32, metric *VolumeMetric) (*VacuumCandidate, bool) {
|
|
deadSpace := metric.Size - metric.UsedSpace
|
|
deadSpacePercent := 0.0
|
|
if metric.Size > 0 {
|
|
deadSpacePercent = float64(deadSpace) * 100.0 / float64(metric.Size)
|
|
}
|
|
|
|
candidate := &VacuumCandidate{
|
|
VolumeID: volumeID,
|
|
DataNodeID: metric.DataNodeID,
|
|
Size: metric.Size,
|
|
UsedSpace: metric.UsedSpace,
|
|
DeadSpace: deadSpace,
|
|
DeadSpacePercent: deadSpacePercent,
|
|
ReplicaCount: metric.ReplicaCount,
|
|
RackID: metric.RackID,
|
|
DataCenterID: metric.DataCenterID,
|
|
FileCount: metric.FileCount,
|
|
LastModified: metric.LastModified,
|
|
}
|
|
|
|
if metric.Size < d.config.MinVolumeSize {
|
|
candidate.CanVacuum = false
|
|
candidate.Reason = "volume too small"
|
|
return candidate, false
|
|
}
|
|
|
|
if metric.Size > d.config.MaxVolumeSize {
|
|
candidate.CanVacuum = false
|
|
candidate.Reason = "volume too large"
|
|
return candidate, false
|
|
}
|
|
|
|
if int(deadSpacePercent) < d.config.DeadSpaceThreshold {
|
|
candidate.CanVacuum = false
|
|
candidate.Reason = "insufficient dead space"
|
|
return candidate, false
|
|
}
|
|
|
|
if d.isNodeExcluded(metric.DataNodeID) {
|
|
candidate.CanVacuum = false
|
|
candidate.Reason = "node excluded"
|
|
return candidate, false
|
|
}
|
|
|
|
if len(d.config.PreferredNodes) > 0 && !d.isPreferredNode(metric.DataNodeID) {
|
|
candidate.CanVacuum = false
|
|
candidate.Reason = "node not preferred"
|
|
return candidate, false
|
|
}
|
|
|
|
if metric.IsRebalancing {
|
|
candidate.CanVacuum = false
|
|
candidate.Reason = "volume rebalancing"
|
|
return candidate, false
|
|
}
|
|
|
|
utilization := 0
|
|
if metric.Size > 0 {
|
|
utilization = int(float64(metric.UsedSpace) * 100.0 / float64(metric.Size))
|
|
}
|
|
|
|
candidate.CanVacuum = true
|
|
candidate.FragmentationScore = calculateFragmentationScore(deadSpacePercent, float64(utilization))
|
|
return candidate, true
|
|
}
|
|
|
|
// isNodeExcluded checks if a node is excluded
|
|
func (d *Detector) isNodeExcluded(nodeID string) bool {
|
|
for _, excluded := range d.config.ExcludeNodes {
|
|
if excluded == nodeID {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isPreferredNode checks if a node is preferred
|
|
func (d *Detector) isPreferredNode(nodeID string) bool {
|
|
for _, preferred := range d.config.PreferredNodes {
|
|
if preferred == nodeID {
|
|
return true
|
|
}
|
|
}
|