Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
366 lines
12 KiB
Go
366 lines
12 KiB
Go
package topology
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
)
|
|
|
|
// AssignTask moves a task from pending to assigned and reserves capacity
|
|
func (at *ActiveTopology) AssignTask(taskID string) error {
|
|
at.mutex.Lock()
|
|
defer at.mutex.Unlock()
|
|
|
|
task, exists := at.pendingTasks[taskID]
|
|
if !exists {
|
|
return fmt.Errorf("pending task %s not found", taskID)
|
|
}
|
|
|
|
// Skip capacity check if topology hasn't been populated yet
|
|
if len(at.disks) == 0 {
|
|
glog.Warningf("AssignTask %s: topology has no disks yet, skipping capacity check", taskID)
|
|
} else {
|
|
// Check if all destination disks have sufficient capacity to reserve
|
|
for _, dest := range task.Destinations {
|
|
targetKey := fmt.Sprintf("%s:%d", dest.TargetServer, dest.TargetDisk)
|
|
if targetDisk, exists := at.disks[targetKey]; exists {
|
|
availableCapacity := at.getEffectiveAvailableCapacityUnsafe(targetDisk)
|
|
|
|
// Check if we have enough total capacity using the improved unified comparison
|
|
if !availableCapacity.CanAccommodate(dest.StorageChange) {
|
|
return fmt.Errorf("insufficient capacity on target disk %s:%d. Available: %+v, Required: %+v",
|
|
dest.TargetServer, dest.TargetDisk, availableCapacity, dest.StorageChange)
|
|
}
|
|
} else if dest.TargetServer != "" {
|
|
// Fail fast if destination disk is not found in topology
|
|
var existingKeys []string
|
|
for k := range at.disks {
|
|
existingKeys = append(existingKeys, k)
|
|
}
|
|
glog.Warningf("destination disk %s not found in topology. Existing disk keys: %v", targetKey, existingKeys)
|
|
return fmt.Errorf("destination disk %s not found in topology", targetKey)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Move task to assigned and reserve capacity
|
|
delete(at.pendingTasks, taskID)
|
|
task.Status = TaskStatusInProgress
|
|
at.assignedTasks[taskID] = task
|
|
at.reassignTaskStates()
|
|
|
|
// Log capacity reservation information for all sources and destinations
|
|
totalSourceImpact := StorageSlotChange{}
|
|
totalDestImpact := StorageSlotChange{}
|
|
for _, source := range task.Sources {
|
|
totalSourceImpact.AddInPlace(source.StorageChange)
|
|
}
|
|
for _, dest := range task.Destinations {
|
|
totalDestImpact.AddInPlace(dest.StorageChange)
|
|
}
|
|
|
|
glog.V(2).Infof("Task %s assigned and capacity reserved: %d sources (VolumeSlots:%d, ShardSlots:%d), %d destinations (VolumeSlots:%d, ShardSlots:%d)",
|
|
taskID, len(task.Sources), totalSourceImpact.VolumeSlots, totalSourceImpact.ShardSlots,
|
|
len(task.Destinations), totalDestImpact.VolumeSlots, totalDestImpact.ShardSlots)
|
|
|
|
return nil
|
|
}
|
|
|
|
// CompleteTask moves a task from assigned to recent and releases reserved capacity
|
|
// NOTE: This only releases the reserved capacity. The actual topology update (VolumeCount changes)
|
|
// should be handled by the master when it receives the task completion notification.
|
|
func (at *ActiveTopology) CompleteTask(taskID string) error {
|
|
at.mutex.Lock()
|
|
defer at.mutex.Unlock()
|
|
|
|
task, exists := at.assignedTasks[taskID]
|
|
if !exists {
|
|
// If not in assigned tasks, check pending tasks
|
|
if task, exists = at.pendingTasks[taskID]; exists {
|
|
delete(at.pendingTasks, taskID)
|
|
} else {
|
|
return fmt.Errorf("task %s not found in assigned or pending tasks", taskID)
|
|
}
|
|
} else {
|
|
delete(at.assignedTasks, taskID)
|
|
}
|
|
|
|
// Release reserved capacity by moving task to completed state
|
|
task.Status = TaskStatusCompleted
|
|
task.CompletedAt = time.Now()
|
|
at.recentTasks[taskID] = task
|
|
at.reassignTaskStates()
|
|
|
|
// Log capacity release information for all sources and destinations
|
|
totalSourceImpact := StorageSlotChange{}
|
|
totalDestImpact := StorageSlotChange{}
|
|
for _, source := range task.Sources {
|
|
totalSourceImpact.AddInPlace(source.StorageChange)
|
|
}
|
|
for _, dest := range task.Destinations {
|
|
totalDestImpact.AddInPlace(dest.StorageChange)
|
|
}
|
|
|
|
glog.V(2).Infof("Task %s completed and capacity released: %d sources (VolumeSlots:%d, ShardSlots:%d), %d destinations (VolumeSlots:%d, ShardSlots:%d)",
|
|
taskID, len(task.Sources), totalSourceImpact.VolumeSlots, totalSourceImpact.ShardSlots,
|
|
len(task.Destinations), totalDestImpact.VolumeSlots, totalDestImpact.ShardSlots)
|
|
|
|
// Clean up old recent tasks
|
|
at.cleanupRecentTasks()
|
|
|
|
return nil
|
|
}
|
|
|
|
// ApplyActualStorageChange updates the topology to reflect actual storage changes after task completion
|
|
// This should be called when the master updates the topology with new VolumeCount information
|
|
func (at *ActiveTopology) ApplyActualStorageChange(nodeID string, diskID uint32, volumeCountChange int64) {
|
|
at.mutex.Lock()
|
|
defer at.mutex.Unlock()
|
|
|
|
diskKey := fmt.Sprintf("%s:%d", nodeID, diskID)
|
|
if disk, exists := at.disks[diskKey]; exists && disk.DiskInfo != nil && disk.DiskInfo.DiskInfo != nil {
|
|
oldCount := disk.DiskInfo.DiskInfo.VolumeCount
|
|
disk.DiskInfo.DiskInfo.VolumeCount += volumeCountChange
|
|
|
|
glog.V(2).Infof("Applied actual storage change on disk %s: volume_count %d -> %d (change: %+d)",
|
|
diskKey, oldCount, disk.DiskInfo.DiskInfo.VolumeCount, volumeCountChange)
|
|
}
|
|
}
|
|
|
|
// AddPendingTask is the unified function that handles both simple and complex task creation
|
|
func (at *ActiveTopology) AddPendingTask(spec TaskSpec) error {
|
|
// Validation
|
|
if len(spec.Sources) == 0 {
|
|
return fmt.Errorf("at least one source is required")
|
|
}
|
|
if len(spec.Destinations) == 0 {
|
|
return fmt.Errorf("at least one destination is required")
|
|
}
|
|
|
|
at.mutex.Lock()
|
|
defer at.mutex.Unlock()
|
|
|
|
// Build sources array
|
|
sources := make([]TaskSource, len(spec.Sources))
|
|
for i, sourceSpec := range spec.Sources {
|
|
var storageImpact StorageSlotChange
|
|
var estimatedSize int64
|
|
|
|
if sourceSpec.StorageImpact != nil {
|
|
// Use manually specified impact
|
|
storageImpact = *sourceSpec.StorageImpact
|
|
} else {
|
|
// Auto-calculate based on task type and cleanup type
|
|
storageImpact = at.calculateSourceStorageImpact(spec.TaskType, sourceSpec.CleanupType, spec.VolumeSize)
|
|
}
|
|
|
|
if sourceSpec.EstimatedSize != nil {
|
|
estimatedSize = *sourceSpec.EstimatedSize
|
|
} else {
|
|
estimatedSize = spec.VolumeSize // Default to volume size
|
|
}
|
|
|
|
sources[i] = TaskSource{
|
|
SourceServer: sourceSpec.ServerID,
|
|
SourceDisk: sourceSpec.DiskID,
|
|
StorageChange: storageImpact,
|
|
EstimatedSize: estimatedSize,
|
|
}
|
|
}
|
|
|
|
// Build destinations array
|
|
destinations := make([]TaskDestination, len(spec.Destinations))
|
|
for i, destSpec := range spec.Destinations {
|
|
var storageImpact StorageSlotChange
|
|
var estimatedSize int64
|
|
|
|
if destSpec.StorageImpact != nil {
|
|
// Use manually specified impact
|
|
storageImpact = *destSpec.StorageImpact
|
|
} else {
|
|
// Auto-calculate based on task type
|
|
_, storageImpact = CalculateTaskStorageImpact(spec.TaskType, spec.VolumeSize)
|
|
}
|
|
|
|
if destSpec.EstimatedSize != nil {
|
|
estimatedSize = *destSpec.EstimatedSize
|
|
} else {
|
|
estimatedSize = spec.VolumeSize // Default to volume size
|
|
}
|
|
|
|
destinations[i] = TaskDestination{
|
|
TargetServer: destSpec.ServerID,
|
|
TargetDisk: destSpec.DiskID,
|
|
StorageChange: storageImpact,
|
|
EstimatedSize: estimatedSize,
|
|
}
|
|
}
|
|
|
|
// Create the task
|
|
task := &taskState{
|
|
VolumeID: spec.VolumeID,
|
|
TaskType: spec.TaskType,
|
|
Status: TaskStatusPending,
|
|
StartedAt: time.Now(),
|
|
EstimatedSize: spec.VolumeSize,
|
|
Sources: sources,
|
|
Destinations: destinations,
|
|
}
|
|
|
|
at.pendingTasks[spec.TaskID] = task
|
|
at.assignTaskToDisk(task)
|
|
|
|
return nil
|
|
}
|
|
|
|
// RestoreMaintenanceTask restores a task from persistent storage into the active topology
|
|
func (at *ActiveTopology) RestoreMaintenanceTask(taskID string, volumeID uint32, taskType TaskType, status TaskStatus, sources []TaskSource, destinations []TaskDestination, estimatedSize int64) error {
|
|
at.mutex.Lock()
|
|
defer at.mutex.Unlock()
|
|
|
|
task := &taskState{
|
|
VolumeID: volumeID,
|
|
TaskType: taskType,
|
|
Status: status,
|
|
StartedAt: time.Now(), // Fallback if not provided, will be updated by heartbeats
|
|
EstimatedSize: estimatedSize,
|
|
Sources: sources,
|
|
Destinations: destinations,
|
|
}
|
|
|
|
if status == TaskStatusInProgress {
|
|
at.assignedTasks[taskID] = task
|
|
} else if status == TaskStatusPending {
|
|
at.pendingTasks[taskID] = task
|
|
} else {
|
|
return nil // Ignore other statuses for topology tracking
|
|
}
|
|
|
|
// Re-register task with disks for capacity tracking
|
|
at.assignTaskToDisk(task)
|
|
|
|
glog.V(1).Infof("Restored %s task %s in topology: volume %d, %d sources, %d destinations",
|
|
taskType, taskID, volumeID, len(sources), len(destinations))
|
|
|
|
return nil
|
|
}
|
|
|
|
// HasTask checks if there is any pending or assigned task for the given volume and task type.
|
|
// If taskType is TaskTypeNone, it checks for ANY task type.
|
|
func (at *ActiveTopology) HasTask(volumeID uint32, taskType TaskType) bool {
|
|
at.mutex.RLock()
|
|
defer at.mutex.RUnlock()
|
|
|
|
for _, task := range at.pendingTasks {
|
|
if task.VolumeID == volumeID && (taskType == TaskTypeNone || task.TaskType == taskType) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
for _, task := range at.assignedTasks {
|
|
if task.VolumeID == volumeID && (taskType == TaskTypeNone || task.TaskType == taskType) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// HasAnyTask checks if there is any pending or assigned task for the given volume across all types.
|
|
func (at *ActiveTopology) HasAnyTask(volumeID uint32) bool {
|
|
return at.HasTask(volumeID, TaskTypeNone)
|
|
}
|
|
|
|
// GetTaskServerAdjustments returns per-server volume count adjustments for
|
|
// pending and assigned tasks of the given type. For each task, source servers
|
|
// are decremented and destination servers are incremented, reflecting the
|
|
// projected volume distribution once in-flight tasks complete.
|
|
func (at *ActiveTopology) GetTaskServerAdjustments(taskType TaskType) map[string]int {
|
|
at.mutex.RLock()
|
|
defer at.mutex.RUnlock()
|
|
|
|
adjustments := make(map[string]int)
|
|
for _, task := range at.pendingTasks {
|
|
if task.TaskType != taskType {
|
|
continue
|
|
}
|
|
for _, src := range task.Sources {
|
|
adjustments[src.SourceServer]--
|
|
}
|
|
for _, dst := range task.Destinations {
|
|
adjustments[dst.TargetServer]++
|
|
}
|
|
}
|
|
for _, task := range at.assignedTasks {
|
|
if task.TaskType != taskType {
|
|
continue
|
|
}
|
|
for _, src := range task.Sources {
|
|
adjustments[src.SourceServer]--
|
|
}
|
|
for _, dst := range task.Destinations {
|
|
adjustments[dst.TargetServer]++
|
|
}
|
|
}
|
|
return adjustments
|
|
}
|
|
|
|
// calculateSourceStorageImpact calculates storage impact for sources based on task type and cleanup type
|
|
func (at *ActiveTopology) calculateSourceStorageImpact(taskType TaskType, cleanupType SourceCleanupType, volumeSize int64) StorageSlotChange {
|
|
switch taskType {
|
|
case TaskTypeErasureCoding:
|
|
switch cleanupType {
|
|
case CleanupVolumeReplica:
|
|
impact, _ := CalculateTaskStorageImpact(TaskTypeErasureCoding, volumeSize)
|
|
return impact
|
|
case CleanupECShards:
|
|
return CalculateECShardCleanupImpact(volumeSize)
|
|
default:
|
|
impact, _ := CalculateTaskStorageImpact(TaskTypeErasureCoding, volumeSize)
|
|
return impact
|
|
}
|
|
default:
|
|
impact, _ := CalculateTaskStorageImpact(taskType, volumeSize)
|
|
return impact
|
|
}
|
|
}
|
|
|
|
// SourceCleanupType indicates what type of data needs to be cleaned up from a source
|
|
type SourceCleanupType int
|
|
|
|
const (
|
|
CleanupVolumeReplica SourceCleanupType = iota // Clean up volume replica (frees volume slots)
|
|
CleanupECShards // Clean up existing EC shards (frees shard slots)
|
|
)
|
|
|
|
// TaskSourceSpec represents a source specification for task creation
|
|
type TaskSourceSpec struct {
|
|
ServerID string
|
|
DiskID uint32
|
|
DataCenter string // Data center of the source server
|
|
Rack string // Rack of the source server
|
|
CleanupType SourceCleanupType // For EC: volume replica vs existing shards
|
|
ShardIds []uint32 // For CleanupECShards: shard ids on the source disk to clear before re-distributing
|
|
StorageImpact *StorageSlotChange // Optional: manual override
|
|
EstimatedSize *int64 // Optional: manual override
|
|
}
|
|
|
|
// TaskDestinationSpec represents a destination specification for task creation
|
|
type TaskDestinationSpec struct {
|
|
ServerID string
|
|
DiskID uint32
|
|
StorageImpact *StorageSlotChange // Optional: manual override
|
|
EstimatedSize *int64 // Optional: manual override
|
|
}
|
|
|
|
// TaskSpec represents a complete task specification
|
|
type TaskSpec struct {
|
|
TaskID string
|
|
TaskType TaskType
|
|
VolumeID uint32
|
|
VolumeSize int64 // Used for auto-calculation when manual impacts not provided
|
|
Sources []TaskSourceSpec // Can be single or multiple
|
|
Destinations []TaskDestinationSpec // Can be single or multiple
|
|
}
|