Files
seaweedfs/weed/shell/command_ec_shard_unmount.go
Chris LuandGitHub 944d967502 refactor: extract EC orchestration into a shared weed/ec package (#10760)
* shell: move ErrorWaitGroup to weed/util

* shell: remove unused CandidateEcNode and EcRack types

* ec: extract EC orchestration logic from weed/shell into weed/ec

Move the EC node/topology model, balance engine, encode pipeline, decode
pipeline, and rebuild engine into a new weed/ec package so shell commands
and maintenance workers can share the logic. Shell commands keep flag
parsing and delegate through a small ec.Env (dial option, topology fetch,
volume locations, lock check). Tests move along with the code.

* shell: remove unused proportional-rebalance type stubs

* ec: move scrub, replication check, and shard unmount engines into weed/ec

* worker: share the EC generation-aware shard counter from weed/ec

* ec: gofmt

* shell: drop EC aliases with no remaining callers

* ec: guard a missing topology hook and nil disk entries in topology helpers

* ec: drop trailing newlines from decode error strings

* ec: re-check the shell lock before applying shard unmounts

* shell: trim -node entries in ec.scrub
2026-08-14 13:54:12 -07:00

98 lines
3.0 KiB
Go

package shell
import (
"flag"
"fmt"
"io"
"math"
"github.com/seaweedfs/seaweedfs/weed/ec"
)
func init() {
Commands = append(Commands, &commandEcShardUnmount{})
}
type commandEcShardUnmount struct {
}
func (c *commandEcShardUnmount) Name() string {
return "ec.shard.unmount"
}
func (c *commandEcShardUnmount) Help() string {
return `Unmounts, and optionally deletes, EC volume shards.
ec.shard.unmount --volumeId=<volume id> --shardId=<comma-separated list of shard IDs> [--delete]
Shard IDs can be specified either as a single numeric ID (f.ex. "2") or with a
node address (<shard_id>@<address>, f.ex. "3@10.200.18.88:9007") to pick one
copy when shards are repeated and/or co-located on the same node. The live
topology is printed in the same form, so its entries can be copied verbatim.
Unmounting only takes shards offline on their volume servers; pass --delete to
also remove the shard files. This is useful to clean up over-replicated EC
volumes without a full rebalance.
Shards are processed one by one, best-effort: on error the command stops and
leaves already-processed shards as they are.
This command can, and will, irrevocably delete data if used incorrectly.
Tread carefully.
`
}
func (c *commandEcShardUnmount) HasTag(CommandTag) bool {
return false
}
func (c *commandEcShardUnmount) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
if handleHelpRequest(c, args, writer) {
return nil
}
ecShardUnmountCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
volumeID := ecShardUnmountCommand.Uint("volumeId", 0, "volume ID for the shards to process")
shardIDsStr := ecShardUnmountCommand.String("shardId", "", "comma-separated EC shard IDs for the volume")
delete := ecShardUnmountCommand.Bool("delete", false, "also delete the shard files after unmounting them")
ignoreInvalid := ecShardUnmountCommand.Bool("ignoreInvalid", false, "ignore user-provided shards that match nothing live, and try to proceed anyway")
apply := ecShardUnmountCommand.Bool("apply", false, "Execute actions on the cluster. By default only lists what actions would be executed.")
applyAlias := ecShardUnmountCommand.Bool("force", false, "Execute actions on the cluster (alias for -apply).")
if err = ecShardUnmountCommand.Parse(args); err != nil {
return err
}
if err = commandEnv.confirmIsLocked(args); err != nil {
return
}
if *volumeID == 0 {
return fmt.Errorf("no volume ID")
}
if *volumeID > math.MaxUint32 {
return fmt.Errorf("invalid volume ID %d", *volumeID)
}
if *shardIDsStr == "" {
return fmt.Errorf("missing shardId")
}
shards, err := ec.ShardRefsFromString(*shardIDsStr)
if err != nil {
return err
}
// collect topology information
topology, _, err := collectTopologyInfo(commandEnv, 0)
if err != nil {
return err
}
return ec.UnmountShards(commandEnv.ecEnv(), writer, ec.ShardUnmountRequest{
Topology: topology,
VolumeID: uint32(*volumeID),
Shards: shards,
Delete: *delete,
IgnoreInvalid: *ignoreInvalid,
Apply: *apply || *applyAlias,
})
}