mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-05-19 00:01:31 +00:00
Finding 1: Identity no longer address-derived - ReplicaAddr.ServerID field added (stable server identity from registry) - BlockVolumeAssignment.ReplicaServerID field added (scalar RF=2 path) - ControlBridge uses ServerID, NOT address, for ReplicaID - Missing ServerID → replica skipped (fail closed), logged Finding 2: Wired into real ProcessAssignments - BlockService.v2Bridge field initialized in StartBlockService - ProcessAssignments converts each assignment via v2Bridge.ConvertAssignment BEFORE existing V1 processing (parallel, not replacing yet) - Logged at glog V(1) Finding 3: Fail-closed on missing identity - Empty ServerID in ReplicaAddrs → replica skipped with log - Empty ReplicaServerID in scalar path → no replica created - Test: MissingServerID_FailsClosed verifies both paths 7 tests: StableServerID, AddressChange_IdentityPreserved, MultiReplica_StableServerIDs, MissingServerID_FailsClosed, EpochFencing_IntegratedPath, RebuildAssignment, ReplicaAssignment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
78 lines
2.2 KiB
Go
78 lines
2.2 KiB
Go
package weed_server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
|
)
|
|
|
|
// ShipperDebugInfo is the real-time shipper state for one replica.
|
|
type ShipperDebugInfo struct {
|
|
DataAddr string `json:"data_addr"`
|
|
State string `json:"state"`
|
|
FlushedLSN uint64 `json:"flushed_lsn"`
|
|
}
|
|
|
|
// BlockVolumeDebugInfo is the real-time block volume state.
|
|
type BlockVolumeDebugInfo struct {
|
|
Path string `json:"path"`
|
|
Role string `json:"role"`
|
|
Epoch uint64 `json:"epoch"`
|
|
HeadLSN uint64 `json:"head_lsn"`
|
|
Degraded bool `json:"degraded"`
|
|
Shippers []ShipperDebugInfo `json:"shippers,omitempty"`
|
|
Timestamp string `json:"timestamp"`
|
|
}
|
|
|
|
// debugBlockShipperHandler returns real-time shipper state for all block volumes.
|
|
// Unlike the master's replica_degraded (heartbeat-lagged), this reads directly
|
|
// from the shipper's atomic state field — no heartbeat delay.
|
|
//
|
|
// GET /debug/block/shipper
|
|
func (vs *VolumeServer) debugBlockShipperHandler(w http.ResponseWriter, r *http.Request) {
|
|
if vs.blockService == nil {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode([]BlockVolumeDebugInfo{})
|
|
return
|
|
}
|
|
|
|
store := vs.blockService.Store()
|
|
if store == nil {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode([]BlockVolumeDebugInfo{})
|
|
return
|
|
}
|
|
|
|
var infos []BlockVolumeDebugInfo
|
|
store.IterateBlockVolumes(func(path string, vol *blockvol.BlockVol) {
|
|
status := vol.Status()
|
|
info := BlockVolumeDebugInfo{
|
|
Path: path,
|
|
Role: status.Role.String(),
|
|
Epoch: status.Epoch,
|
|
HeadLSN: status.WALHeadLSN,
|
|
Degraded: status.ReplicaDegraded,
|
|
Timestamp: time.Now().UTC().Format(time.RFC3339Nano),
|
|
}
|
|
|
|
// Get per-shipper state from ShipperGroup if available.
|
|
sg := vol.GetShipperGroup()
|
|
if sg != nil {
|
|
for _, ss := range sg.ShipperStates() {
|
|
info.Shippers = append(info.Shippers, ShipperDebugInfo{
|
|
DataAddr: ss.DataAddr,
|
|
State: ss.State,
|
|
FlushedLSN: ss.FlushedLSN,
|
|
})
|
|
}
|
|
}
|
|
|
|
infos = append(infos, info)
|
|
})
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(infos)
|
|
}
|