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.
187 lines
6.0 KiB
187 lines
6.0 KiB
package table_maintenance
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/worker_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/worker/tasks/base"
|
|
"github.com/seaweedfs/seaweedfs/weed/worker/types"
|
|
)
|
|
|
|
// TableMaintenanceDetector implements types.TaskDetector for table maintenance
|
|
type TableMaintenanceDetector struct {
|
|
config *Config
|
|
}
|
|
|
|
// NewTableMaintenanceDetector creates a new table maintenance detector
|
|
func NewTableMaintenanceDetector(config *Config) *TableMaintenanceDetector {
|
|
return &TableMaintenanceDetector{
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// ScanInterval returns how often to scan for table maintenance needs
|
|
func (d *TableMaintenanceDetector) ScanInterval() time.Duration {
|
|
if d.config != nil && d.config.ScanIntervalMinutes > 0 {
|
|
return time.Duration(d.config.ScanIntervalMinutes) * time.Minute
|
|
}
|
|
return 30 * time.Minute // Default: scan every 30 minutes
|
|
}
|
|
|
|
// DetectTasks scans for tables that need maintenance
|
|
func (d *TableMaintenanceDetector) DetectTasks(metrics []*types.VolumeHealthMetrics) ([]*types.TaskDetectionResult, error) {
|
|
glog.V(2).Infof("Table maintenance detection starting")
|
|
|
|
// Table maintenance doesn't use volume metrics - it scans table buckets
|
|
// The actual scanning is done by the admin server's table scanner
|
|
// Workers pick up jobs from the admin server's queue
|
|
|
|
// This detector returns empty results because table maintenance jobs
|
|
// are created by the admin server's table scanner, not by volume metrics
|
|
return nil, nil
|
|
}
|
|
|
|
// Detection is the function signature required by the task registration system
|
|
func Detection(metrics []*types.VolumeHealthMetrics, clusterInfo *types.ClusterInfo, config base.TaskConfig) ([]*types.TaskDetectionResult, error) {
|
|
if !config.IsEnabled() {
|
|
return nil, nil
|
|
}
|
|
|
|
tableConfig, ok := config.(*Config)
|
|
if !ok {
|
|
tableConfig = NewDefaultConfig()
|
|
}
|
|
|
|
detector := NewTableMaintenanceDetector(tableConfig)
|
|
return detector.DetectTasks(metrics)
|
|
}
|
|
|
|
// TableMaintenanceScanner scans table buckets for maintenance needs
|
|
// This is called by the admin server to populate the maintenance queue
|
|
type TableMaintenanceScanner struct {
|
|
config *Config
|
|
}
|
|
|
|
// NewTableMaintenanceScanner creates a new table maintenance scanner
|
|
func NewTableMaintenanceScanner(config *Config) *TableMaintenanceScanner {
|
|
return &TableMaintenanceScanner{
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// ScanTableBucket scans a table bucket for tables needing maintenance
|
|
// Returns a list of maintenance jobs that should be queued
|
|
func (s *TableMaintenanceScanner) ScanTableBucket(bucketName string, tables []TableInfo) ([]*TableMaintenanceJob, error) {
|
|
glog.V(1).Infof("Scanning table bucket %s for maintenance needs", bucketName)
|
|
|
|
var jobs []*TableMaintenanceJob
|
|
|
|
for _, table := range tables {
|
|
// Check each table for maintenance needs
|
|
tableJobs := s.checkTableMaintenanceNeeds(bucketName, table)
|
|
jobs = append(jobs, tableJobs...)
|
|
}
|
|
|
|
glog.V(1).Infof("Found %d maintenance jobs for table bucket %s", len(jobs), bucketName)
|
|
return jobs, nil
|
|
}
|
|
|
|
// TableInfo represents basic table information for scanning
|
|
type TableInfo struct {
|
|
Namespace string
|
|
TableName string
|
|
TablePath string
|
|
LastCompaction time.Time
|
|
DataFileCount int
|
|
SnapshotCount int
|
|
OldestSnapshot time.Time
|
|
TotalSizeBytes int64
|
|
DeletedFileCount int
|
|
}
|
|
|
|
// checkTableMaintenanceNeeds checks if a table needs maintenance
|
|
func (s *TableMaintenanceScanner) checkTableMaintenanceNeeds(bucketName string, table TableInfo) []*TableMaintenanceJob {
|
|
var jobs []*TableMaintenanceJob
|
|
now := time.Now()
|
|
|
|
// Check for compaction needs
|
|
if s.needsCompaction(table) {
|
|
jobs = append(jobs, &TableMaintenanceJob{
|
|
JobType: JobTypeCompaction,
|
|
TableBucket: bucketName,
|
|
Namespace: table.Namespace,
|
|
TableName: table.TableName,
|
|
TablePath: table.TablePath,
|
|
Priority: types.TaskPriorityNormal,
|
|
Reason: "Table has many small files that can be compacted",
|
|
CreatedAt: now,
|
|
})
|
|
}
|
|
|
|
// Check for snapshot expiration needs
|
|
if s.needsSnapshotExpiration(table) {
|
|
jobs = append(jobs, &TableMaintenanceJob{
|
|
JobType: JobTypeSnapshotExpiration,
|
|
TableBucket: bucketName,
|
|
Namespace: table.Namespace,
|
|
TableName: table.TableName,
|
|
TablePath: table.TablePath,
|
|
Priority: types.TaskPriorityLow,
|
|
Reason: "Table has expired snapshots that can be removed",
|
|
CreatedAt: now,
|
|
})
|
|
}
|
|
|
|
// Check for orphan cleanup needs
|
|
if s.needsOrphanCleanup(table) {
|
|
jobs = append(jobs, &TableMaintenanceJob{
|
|
JobType: JobTypeOrphanCleanup,
|
|
TableBucket: bucketName,
|
|
Namespace: table.Namespace,
|
|
TableName: table.TableName,
|
|
TablePath: table.TablePath,
|
|
Priority: types.TaskPriorityLow,
|
|
Reason: "Table has orphaned files that can be removed",
|
|
CreatedAt: now,
|
|
})
|
|
}
|
|
|
|
return jobs
|
|
}
|
|
|
|
// needsCompaction checks if a table needs compaction
|
|
func (s *TableMaintenanceScanner) needsCompaction(table TableInfo) bool {
|
|
// Use config value directly - config is always set by NewTableMaintenanceScanner
|
|
return table.DataFileCount > s.config.CompactionFileThreshold
|
|
}
|
|
|
|
// needsSnapshotExpiration checks if a table has expired snapshots
|
|
func (s *TableMaintenanceScanner) needsSnapshotExpiration(table TableInfo) bool {
|
|
if table.SnapshotCount <= 1 {
|
|
return false // Keep at least one snapshot
|
|
}
|
|
|
|
// Use config value directly - config is always set by NewTableMaintenanceScanner
|
|
cutoff := time.Now().AddDate(0, 0, -s.config.SnapshotRetentionDays)
|
|
return table.OldestSnapshot.Before(cutoff)
|
|
}
|
|
|
|
// needsOrphanCleanup checks if a table might have orphaned files
|
|
func (s *TableMaintenanceScanner) needsOrphanCleanup(table TableInfo) bool {
|
|
// Tables with deleted files might have orphans
|
|
return table.DeletedFileCount > 0
|
|
}
|
|
|
|
// CreateTaskParams creates task parameters for a maintenance job
|
|
func CreateTaskParams(job *TableMaintenanceJob) *worker_pb.TaskParams {
|
|
return &worker_pb.TaskParams{
|
|
Sources: []*worker_pb.TaskSource{
|
|
{
|
|
Node: job.TablePath,
|
|
},
|
|
},
|
|
VolumeId: 0, // Not volume-specific
|
|
Collection: job.TableBucket,
|
|
}
|
|
}
|