mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-25 14:30:43 +01:00
* pb: ResumeVolumeGrowth rpc for operator-driven growth resume (#11763) * master: freeze volume growth on topology id mismatch (#11763) * shell: volume.growth.resume command (#11763) * master: replicate the growth freeze and gate it on veteran heartbeats (#11763) * master: block allocation while the topology id is being minted DeferAllocation only armed after the topology id persisted, so growth requests arriving during init ran ahead of the warmup window. Gate NextVolumeId on an init-pending flag spanning the whole mint. * master: stash reported topology ids until the local id is minted (#11763) * drain stashed topology id reports when the id arrives outside the mint path * persist the growth-freeze flag in the mirror file and raft snapshots * log hashicorp raft marshal and apply errors in applyGrowthFreeze
75 lines
2.1 KiB
Go
75 lines
2.1 KiB
Go
package topology
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
hashicorpRaft "github.com/hashicorp/raft"
|
|
"github.com/seaweedfs/raft"
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
|
|
)
|
|
|
|
type MaxVolumeIdCommand struct {
|
|
MaxVolumeId needle.VolumeId `json:"maxVolumeId"`
|
|
TopologyId string `json:"topologyId"`
|
|
// GrowthFrozen replicates the volume-growth freeze through raft so a
|
|
// leader change does not silently lift it. nil means no change.
|
|
GrowthFrozen *bool `json:"growthFrozen,omitempty"`
|
|
}
|
|
|
|
func NewMaxVolumeIdCommand(value needle.VolumeId, topologyId string) *MaxVolumeIdCommand {
|
|
return &MaxVolumeIdCommand{
|
|
MaxVolumeId: value,
|
|
TopologyId: topologyId,
|
|
}
|
|
}
|
|
|
|
func (c *MaxVolumeIdCommand) CommandName() string {
|
|
return "MaxVolumeId"
|
|
}
|
|
|
|
// deprecatedCommandApply represents the old interface to apply a command to the server.
|
|
func (c *MaxVolumeIdCommand) Apply(server raft.Server) (interface{}, error) {
|
|
topo := server.Context().(*Topology)
|
|
before := topo.GetMaxVolumeId()
|
|
topo.UpAdjustMaxVolumeId(c.MaxVolumeId)
|
|
if c.TopologyId != "" {
|
|
prevTopologyId := topo.GetTopologyId()
|
|
topo.SetTopologyId(c.TopologyId)
|
|
// Log when TopologyId is set for the first time, with different messages for leader and follower.
|
|
if prevTopologyId == "" {
|
|
if server.State() == raft.Leader {
|
|
glog.V(0).Infof("TopologyId generated and applied on leader: %s", c.TopologyId)
|
|
} else {
|
|
glog.V(0).Infof("TopologyId applied on follower: %s", c.TopologyId)
|
|
}
|
|
}
|
|
}
|
|
if c.GrowthFrozen != nil {
|
|
topo.SetGrowthFrozen(*c.GrowthFrozen)
|
|
}
|
|
if err := topo.PersistMaxVolumeId(c.MaxVolumeId); err != nil {
|
|
glog.Warningf("persist max volume id %d: %v", c.MaxVolumeId, err)
|
|
}
|
|
glog.V(1).Infoln("max volume id", before, "==>", topo.GetMaxVolumeId())
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (s *MaxVolumeIdCommand) Persist(sink hashicorpRaft.SnapshotSink) error {
|
|
b, err := json.Marshal(s)
|
|
if err != nil {
|
|
return fmt.Errorf("marshal: %w", err)
|
|
}
|
|
_, err = sink.Write(b)
|
|
if err != nil {
|
|
sink.Cancel()
|
|
return fmt.Errorf("sink.Write(): %w", err)
|
|
}
|
|
return sink.Close()
|
|
}
|
|
|
|
func (s *MaxVolumeIdCommand) Release() {
|
|
}
|