mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-13 09:38:36 +02:00
* feat: throughput limits for replicate, EC shard, and worker-driven moves VolumeCopy was the only rate-limitable transfer; EC shard copies, replica creation, and worker-driven moves all ran at whatever the receiving server's maintenance rate allowed, with no per-operation control. - proto: VolumeEcShardsCopyRequest and the balance / ec_balance task params and configs gain io_byte_per_second; 0 keeps today's behavior (the volume server's own maintenance rate governs). - volume server: VolumeEcShardsCopy throttles with one WriteThrottler per request, shared across the shard, .ecx, .ecj, .vif, and .ecsum copies so the limit caps the transfer as a whole - the same shape as VolumeCopy. - volume_move: ReplicateVolume accepts the limit; EcMoveOptions carries it through MoveEcShards/CopyAndMountEcShards into the copy request, with fake-client tests asserting propagation. - shell: ec.balance gains -ioBytePerSecond; volume.tier.move's replication top-up honors the command's existing -ioBytePerSecond instead of running unthrottled. - worker: balance and ec_balance configs gain io_byte_per_second (surfaced in the admin config schema), carried through detection and plugin job parameters into task params and handed to the shared mover; batch balance jobs inherit the limit from their detection results. The limit is per copy stream, so maxParallelization multiplies the aggregate ceiling. * worker plugins: expose io_byte_per_second in the plugin config and derive it The plugin-driven detection path derives its task Config from the plugin configuration values, and both balance and ec_balance left IoBytePerSecond at zero there - a configured limit silently reverted to the server maintenance rate. Both derive functions now read the field (clamped at zero), and the plugin descriptors expose it with defaults so the configuration form carries it.
192 lines
6.3 KiB
Go
192 lines
6.3 KiB
Go
package balance
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/admin/config"
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/worker_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/worker/tasks/base"
|
|
)
|
|
|
|
// Config extends BaseConfig with balance-specific settings
|
|
type Config struct {
|
|
base.BaseConfig
|
|
ImbalanceThreshold float64 `json:"imbalance_threshold"`
|
|
MinServerCount int `json:"min_server_count"`
|
|
IoBytePerSecond int64 `json:"io_byte_per_second"`
|
|
DataCenterFilter string `json:"-"` // per-detection-run, not persisted
|
|
RackFilter string `json:"-"` // per-detection-run, not persisted
|
|
NodeFilter string `json:"-"` // per-detection-run, not persisted
|
|
}
|
|
|
|
// NewDefaultConfig creates a new default balance configuration
|
|
func NewDefaultConfig() *Config {
|
|
return &Config{
|
|
BaseConfig: base.BaseConfig{
|
|
Enabled: true,
|
|
ScanIntervalSeconds: 30 * 60, // 30 minutes
|
|
MaxConcurrent: 1,
|
|
},
|
|
ImbalanceThreshold: 0.2, // 20%
|
|
MinServerCount: 2,
|
|
}
|
|
}
|
|
|
|
// GetConfigSpec returns the configuration schema for balance tasks
|
|
func GetConfigSpec() base.ConfigSpec {
|
|
return base.ConfigSpec{
|
|
Fields: []*config.Field{
|
|
{
|
|
Name: "enabled",
|
|
JSONName: "enabled",
|
|
Type: config.FieldTypeBool,
|
|
DefaultValue: true,
|
|
Required: false,
|
|
DisplayName: "Enable Balance Tasks",
|
|
Description: "Whether balance tasks should be automatically created",
|
|
HelpText: "Toggle this to enable or disable automatic balance task generation",
|
|
InputType: "checkbox",
|
|
CSSClasses: "form-check-input",
|
|
},
|
|
{
|
|
Name: "scan_interval_seconds",
|
|
JSONName: "scan_interval_seconds",
|
|
Type: config.FieldTypeInterval,
|
|
DefaultValue: 30 * 60,
|
|
MinValue: 5 * 60,
|
|
MaxValue: 2 * 60 * 60,
|
|
Required: true,
|
|
DisplayName: "Scan Interval",
|
|
Description: "How often to scan for volume distribution imbalances",
|
|
HelpText: "The system will check for volume distribution imbalances at this interval",
|
|
Placeholder: "30",
|
|
Unit: config.UnitMinutes,
|
|
InputType: "interval",
|
|
CSSClasses: "form-control",
|
|
},
|
|
{
|
|
Name: "max_concurrent",
|
|
JSONName: "max_concurrent",
|
|
Type: config.FieldTypeInt,
|
|
DefaultValue: 1,
|
|
MinValue: 1,
|
|
MaxValue: 3,
|
|
Required: true,
|
|
DisplayName: "Max Concurrent Tasks",
|
|
Description: "Maximum number of balance tasks that can run simultaneously",
|
|
HelpText: "Limits the number of balance operations running at the same time",
|
|
Placeholder: "1 (default)",
|
|
Unit: config.UnitCount,
|
|
InputType: "number",
|
|
CSSClasses: "form-control",
|
|
},
|
|
{
|
|
Name: "imbalance_threshold",
|
|
JSONName: "imbalance_threshold",
|
|
Type: config.FieldTypeFloat,
|
|
DefaultValue: 0.2,
|
|
MinValue: 0.05,
|
|
MaxValue: 0.5,
|
|
Required: true,
|
|
DisplayName: "Imbalance Threshold",
|
|
Description: "Minimum imbalance ratio to trigger balancing",
|
|
HelpText: "Volume distribution imbalances above this threshold will trigger balancing",
|
|
Placeholder: "0.20 (20%)",
|
|
Unit: config.UnitNone,
|
|
InputType: "number",
|
|
CSSClasses: "form-control",
|
|
},
|
|
{
|
|
Name: "io_byte_per_second",
|
|
JSONName: "io_byte_per_second",
|
|
Type: config.FieldTypeInt,
|
|
DefaultValue: 0,
|
|
MinValue: 0,
|
|
Required: false,
|
|
DisplayName: "Move IO Limit (bytes/sec)",
|
|
Description: "Limit each volume move's copy rate",
|
|
HelpText: "0 falls back to each volume server's own maintenance rate (-maintenanceBytePerSecond)",
|
|
Placeholder: "0 (server maintenance rate)",
|
|
Unit: config.UnitNone,
|
|
InputType: "number",
|
|
CSSClasses: "form-control",
|
|
},
|
|
{
|
|
Name: "min_server_count",
|
|
JSONName: "min_server_count",
|
|
Type: config.FieldTypeInt,
|
|
DefaultValue: 2,
|
|
MinValue: 2,
|
|
MaxValue: 10,
|
|
Required: true,
|
|
DisplayName: "Minimum Server Count",
|
|
Description: "Minimum number of servers required for balancing",
|
|
HelpText: "Balancing will only occur if there are at least this many servers",
|
|
Placeholder: "2 (default)",
|
|
Unit: config.UnitCount,
|
|
InputType: "number",
|
|
CSSClasses: "form-control",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// ToTaskPolicy converts configuration to a TaskPolicy protobuf message
|
|
func (c *Config) ToTaskPolicy() *worker_pb.TaskPolicy {
|
|
return &worker_pb.TaskPolicy{
|
|
Enabled: c.Enabled,
|
|
MaxConcurrent: int32(c.MaxConcurrent),
|
|
RepeatIntervalSeconds: int32(c.ScanIntervalSeconds),
|
|
CheckIntervalSeconds: int32(c.ScanIntervalSeconds),
|
|
TaskConfig: &worker_pb.TaskPolicy_BalanceConfig{
|
|
BalanceConfig: &worker_pb.BalanceTaskConfig{
|
|
ImbalanceThreshold: float64(c.ImbalanceThreshold),
|
|
MinServerCount: int32(c.MinServerCount),
|
|
IoBytePerSecond: c.IoBytePerSecond,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// FromTaskPolicy loads configuration from a TaskPolicy protobuf message
|
|
func (c *Config) FromTaskPolicy(policy *worker_pb.TaskPolicy) error {
|
|
if policy == nil {
|
|
return fmt.Errorf("policy is nil")
|
|
}
|
|
|
|
// Set general TaskPolicy fields
|
|
c.Enabled = policy.Enabled
|
|
c.MaxConcurrent = int(policy.MaxConcurrent)
|
|
c.ScanIntervalSeconds = int(policy.RepeatIntervalSeconds) // Direct seconds-to-seconds mapping
|
|
|
|
// Set balance-specific fields from the task config
|
|
if balanceConfig := policy.GetBalanceConfig(); balanceConfig != nil {
|
|
c.ImbalanceThreshold = float64(balanceConfig.ImbalanceThreshold)
|
|
c.MinServerCount = int(balanceConfig.MinServerCount)
|
|
c.IoBytePerSecond = balanceConfig.IoBytePerSecond
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadConfigFromPersistence loads configuration from the persistence layer if available
|
|
func LoadConfigFromPersistence(configPersistence interface{}) *Config {
|
|
config := NewDefaultConfig()
|
|
|
|
// Try to load from persistence if available
|
|
if persistence, ok := configPersistence.(interface {
|
|
LoadBalanceTaskPolicy() (*worker_pb.TaskPolicy, error)
|
|
}); ok {
|
|
if policy, err := persistence.LoadBalanceTaskPolicy(); err == nil && policy != nil {
|
|
if err := config.FromTaskPolicy(policy); err == nil {
|
|
glog.V(1).Infof("Loaded balance configuration from persistence")
|
|
return config
|
|
}
|
|
}
|
|
}
|
|
|
|
glog.V(1).Infof("Using default balance configuration")
|
|
return config
|
|
}
|