mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-13 07:19:18 +01:00
Wire low-level fencing primitives to master/VS control plane and CSI: - Proto: replica/rebuild address fields on assignment/info/response messages - Assignment queue: retain-until-confirmed (Peek+Confirm), stale epoch pruning - VS assignment receiver: processes assignments from HeartbeatResponse - BlockService replication: ProcessAssignments, deterministic ports (FNV hash) - Registry replica tracking: SetReplica/ClearReplica/SwapPrimaryReplica - CreateBlockVolume: primary + replica, enqueues assignments, single-copy mode - Failover: lease-aware promotion, deferred timers with cancellation on reconnect - ControllerPublish: returns fresh primary iSCSI address after failover - Recovery: recoverBlockVolumes drains pendingRebuilds, enqueues Rebuilding - Real integration tests on M02: failover address switch, rebuild data consistency, full lifecycle failover+rebuild (3 tests, all PASS) Review fixes (12 findings, 5 High, 5 Medium, 2 Low): - R1-1: AllocateBlockVolume returns replication ports - R1-2: setupPrimaryReplication starts rebuild server - R1-3: VS sends periodic block heartbeat for assignment confirmation - R2-F1: LastLeaseGrant set before Register (no stale-lease race) - R2-F2: Deferred promotion timers cancelled on VS reconnect - R2-F3: SwapPrimaryReplica uses RoleToWire instead of uint32(1) - R2-F4: DeleteBlockVolume deletes replica (best-effort) - R2-F5: SwapPrimaryReplica computes epoch atomically under lock - QA: SetReplica removes old replica from byServer index (BUG-QA-CP63-1) 126 CP6-3 tests (67 dev + 48 QA + 8 integration + 3 real). Cumulative Phase 6: 352 tests. All PASS. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
60 lines
2.0 KiB
Go
60 lines
2.0 KiB
Go
package weed_server
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb"
|
|
)
|
|
|
|
// AllocateBlockVolume creates a new block volume on this volume server.
|
|
func (vs *VolumeServer) AllocateBlockVolume(_ context.Context, req *volume_server_pb.AllocateBlockVolumeRequest) (*volume_server_pb.AllocateBlockVolumeResponse, error) {
|
|
if vs.blockService == nil {
|
|
return nil, fmt.Errorf("block service not enabled on this volume server")
|
|
}
|
|
if req.Name == "" {
|
|
return nil, fmt.Errorf("name is required")
|
|
}
|
|
if req.SizeBytes == 0 {
|
|
return nil, fmt.Errorf("size_bytes must be > 0")
|
|
}
|
|
|
|
path, iqn, iscsiAddr, err := vs.blockService.CreateBlockVol(req.Name, req.SizeBytes, req.DiskType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create block volume %q: %w", req.Name, err)
|
|
}
|
|
|
|
// R1-1: Return deterministic replication ports so master can wire WAL shipping.
|
|
dataPort, ctrlPort, rebuildPort := vs.blockService.ReplicationPorts(path)
|
|
host := vs.blockService.ListenAddr()
|
|
if idx := strings.LastIndex(host, ":"); idx >= 0 {
|
|
host = host[:idx]
|
|
}
|
|
|
|
return &volume_server_pb.AllocateBlockVolumeResponse{
|
|
Path: path,
|
|
Iqn: iqn,
|
|
IscsiAddr: iscsiAddr,
|
|
ReplicaDataAddr: fmt.Sprintf("%s:%d", host, dataPort),
|
|
ReplicaCtrlAddr: fmt.Sprintf("%s:%d", host, ctrlPort),
|
|
RebuildListenAddr: fmt.Sprintf("%s:%d", host, rebuildPort),
|
|
}, nil
|
|
}
|
|
|
|
// VolumeServerDeleteBlockVolume deletes a block volume on this volume server.
|
|
func (vs *VolumeServer) VolumeServerDeleteBlockVolume(_ context.Context, req *volume_server_pb.VolumeServerDeleteBlockVolumeRequest) (*volume_server_pb.VolumeServerDeleteBlockVolumeResponse, error) {
|
|
if vs.blockService == nil {
|
|
return nil, fmt.Errorf("block service not enabled on this volume server")
|
|
}
|
|
if req.Name == "" {
|
|
return nil, fmt.Errorf("name is required")
|
|
}
|
|
|
|
if err := vs.blockService.DeleteBlockVol(req.Name); err != nil {
|
|
return nil, fmt.Errorf("delete block volume %q: %w", req.Name, err)
|
|
}
|
|
|
|
return &volume_server_pb.VolumeServerDeleteBlockVolumeResponse{}, nil
|
|
}
|