mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-11 09:00:45 +02:00
The unmount+full-teardown of EC shards was duplicated: the plugin-worker EC task had unmountAndDeleteEcShards and the shell had unmountAndDeleteEcShardsQuiet, byte-identical apart from a fence parameter and a sentinel error. That duplication is how the teardown fence semantics drifted between the two paths. Distribute, mount and verify already live in weed/storage/erasure_coding and are shared by both callers; move the teardown there too, as UnmountAndDeleteEcShards plus the shared ErrFullTeardownNotAcked sentinel. Both paths now call the one function, so the fence semantics cannot diverge again. The shell keeps a thin type-converting wrapper and aliases the sentinel; behavior is unchanged.
77 lines
3.0 KiB
Go
77 lines
3.0 KiB
Go
package erasure_coding
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/operation"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
// ErrFullTeardownNotAcked marks a reachable server that completed the delete
|
|
// RPC but did not report a full teardown (e.g. a pre-upgrade volume server), so
|
|
// a stale EC generation may remain on it. Callers distinguish this from an
|
|
// unreachable node (which may recover and be re-swept) with errors.Is.
|
|
var ErrFullTeardownNotAcked = errors.New("delete did not perform full teardown (pre-upgrade volume server?); a stale EC generation may remain")
|
|
|
|
// UnmountAndDeleteEcShards unmounts then tears down the named EC shards for a
|
|
// volume on one server. Unmount must precede delete (delete requires the shard
|
|
// be unmounted); both RPCs are idempotent against missing shards.
|
|
//
|
|
// encodeTsNs fences both RPCs:
|
|
// - 0 selects the server's blanket, generation-independent teardown. This is
|
|
// the correct choice for a pre-encode or rollback wipe: it clears same-
|
|
// generation shards (a retried encode's prior attempt shares the job's
|
|
// generation) and shards whose .vif generation is unreadable (an
|
|
// interrupted distribute never landed the sidecar) — both of which a fenced
|
|
// teardown preserves. The blanket path aborts rather than clobber a live
|
|
// newer mount, and the caller must guarantee no concurrent newer encode of
|
|
// this volume (e.g. the admin dedupe key, or an operator lock).
|
|
// - a non-zero value fences the teardown to strictly-older generations,
|
|
// preserving same-or-newer, generation 0, and an unreadable .vif — for a
|
|
// stale-worker cleanup that must never wipe a newer run's live shards.
|
|
//
|
|
// Returns ErrFullTeardownNotAcked (wrapped, so errors.Is matches) when a
|
|
// reachable server does not ack the full teardown.
|
|
//
|
|
// This is the single teardown primitive shared by the plugin-worker EC task
|
|
// and the shell ec.encode pre-cleanup, so the fence semantics cannot drift
|
|
// between the two paths.
|
|
func UnmountAndDeleteEcShards(
|
|
ctx context.Context,
|
|
dialOption grpc.DialOption,
|
|
server pb.ServerAddress,
|
|
collection string,
|
|
volumeID uint32,
|
|
shardIds []uint32,
|
|
encodeTsNs int64,
|
|
) error {
|
|
return operation.WithVolumeServerClient(false, server, dialOption,
|
|
func(client volume_server_pb.VolumeServerClient) error {
|
|
if _, err := client.VolumeEcShardsUnmount(ctx, &volume_server_pb.VolumeEcShardsUnmountRequest{
|
|
VolumeId: volumeID,
|
|
ShardIds: shardIds,
|
|
EncodeTsNs: encodeTsNs,
|
|
}); err != nil {
|
|
return fmt.Errorf("unmount: %w", err)
|
|
}
|
|
resp, err := client.VolumeEcShardsDelete(ctx, &volume_server_pb.VolumeEcShardsDeleteRequest{
|
|
VolumeId: volumeID,
|
|
Collection: collection,
|
|
ShardIds: shardIds,
|
|
FullTeardown: true,
|
|
EncodeTsNs: encodeTsNs,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("delete: %w", err)
|
|
}
|
|
if !resp.GetFullTeardownDone() {
|
|
return fmt.Errorf("delete on %s: %w", server, ErrFullTeardownNotAcked)
|
|
}
|
|
return nil
|
|
})
|
|
}
|