From 59cccc788c25c905e660fb745d9f1d31cd2e89be Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 24 Oct 2026 07:04:45 -0700 Subject: [PATCH] master: read the writable-volumes cap from master.toml (#12009) * master: read the writable-volumes cap from master.toml -maxWritableVolumes becomes the master.max_writable_volumes config key, so the new master limit is set the same way as other master options instead of a flag wired into every command that embeds a master. * master: name the writable cap master.max_writable_volumes_per_layout The cap applies per volume layout (collection + replica placement + ttl + disk type), so the key names its scope instead of reading as a global cap. * master: keep an embedder's MaxWritableVolumes when the key is unset NewMasterServer overwrote option.MaxWritableVolumes with the viper value, which reads as 0 whenever master.toml omits the key, silently dropping a cap a Go caller supplied through MasterOption. Seed the default from the option instead so the field survives an absent key; an explicit config entry still wins. * master: drop stray nested-checkout gitlinks Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- weed/command/master.go | 3 --- weed/command/master_follower.go | 1 - weed/command/mini.go | 1 - weed/command/scaffold/master.toml | 7 +++++++ weed/command/server.go | 1 - weed/server/master_server.go | 2 ++ 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/weed/command/master.go b/weed/command/master.go index d205c3a6a9..64b9883bfa 100644 --- a/weed/command/master.go +++ b/weed/command/master.go @@ -57,7 +57,6 @@ type MasterOptions struct { fileSizeLimitMB *int volumePreallocate *bool maxParallelVacuumPerServer *int - maxWritableVolumes *int vacuumIntervalSeconds *int vacuumDeleteEmptyAfterSeconds *int // pulseSeconds *int @@ -96,7 +95,6 @@ func init() { m.fileSizeLimitMB = cmdMaster.Flag.Int("fileSizeLimitMB", 256, "limit the file size accepted by /submit, should match the volume servers' -fileSizeLimitMB (-volume.fileSizeLimitMB under weed server or weed mini, which set this for you)") m.volumePreallocate = cmdMaster.Flag.Bool("volumePreallocate", false, "Preallocate disk space for volumes.") m.maxParallelVacuumPerServer = cmdMaster.Flag.Int("maxParallelVacuumPerServer", 1, "maximum number of volumes to vacuum in parallel per volume server; 0 for no limit (deprecated, use master.vacuum.max_parallel_per_server in master.toml)") - m.maxWritableVolumes = cmdMaster.Flag.Int("maxWritableVolumes", 0, "maximum writable volumes in a volume layout (collection + replica placement + ttl + disk type) assignments are spread over, fullest picked first; 0 means no limit") m.vacuumIntervalSeconds = cmdMaster.Flag.Int("vacuumIntervalSeconds", 840, "seconds between automatic vacuum sweeps") m.vacuumDeleteEmptyAfterSeconds = cmdMaster.Flag.Int("vacuumDeleteEmptyAfterSeconds", 0, "automatic sweep deletes volume copies that stay empty this many seconds; 0 disables") // m.pulseSeconds = cmdMaster.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats") @@ -479,7 +477,6 @@ func (m *MasterOptions) toMasterOption(whiteList []string) *weed_server.MasterOp FileSizeLimitMB: *m.fileSizeLimitMB, VolumePreallocate: *m.volumePreallocate, MaxParallelVacuumPerServer: *m.maxParallelVacuumPerServer, - MaxWritableVolumes: *m.maxWritableVolumes, VacuumIntervalSeconds: *m.vacuumIntervalSeconds, VacuumDeleteEmptyAfterSeconds: *m.vacuumDeleteEmptyAfterSeconds, // PulseSeconds: *m.pulseSeconds, diff --git a/weed/command/master_follower.go b/weed/command/master_follower.go index 95c065775e..4e0983eb96 100644 --- a/weed/command/master_follower.go +++ b/weed/command/master_follower.go @@ -47,7 +47,6 @@ func init() { mf.metricsIntervalSec = aws.Int(0) mf.raftResumeState = aws.Bool(false) mf.maxParallelVacuumPerServer = aws.Int(1) - mf.maxWritableVolumes = aws.Int(0) mf.vacuumIntervalSeconds = aws.Int(840) mf.vacuumDeleteEmptyAfterSeconds = aws.Int(0) mf.telemetryUrl = aws.String("https://telemetry.seaweedfs.com/api/collect") diff --git a/weed/command/mini.go b/weed/command/mini.go index 7cc848d3b7..05a2c1f752 100644 --- a/weed/command/mini.go +++ b/weed/command/mini.go @@ -446,7 +446,6 @@ func initMiniMasterFlags() { miniMasterOptions.volumeSizeLimitMB = cmdMini.Flag.Uint("master.volumeSizeLimitMB", defaultMiniVolumeSizeMB, "Master stops directing writes to oversized volumes (default: 128MB for mini)") miniMasterOptions.volumePreallocate = cmdMini.Flag.Bool("master.volumePreallocate", false, "Preallocate disk space for volumes.") miniMasterOptions.maxParallelVacuumPerServer = cmdMini.Flag.Int("master.maxParallelVacuumPerServer", 1, "maximum number of volumes to vacuum in parallel on one volume server; 0 for no limit (deprecated, use master.vacuum.max_parallel_per_server in master.toml)") - miniMasterOptions.maxWritableVolumes = cmdMini.Flag.Int("master.maxWritableVolumes", 0, "maximum writable volumes in a volume layout (collection + replica placement + ttl + disk type) assignments are spread over, fullest picked first; 0 means no limit") miniMasterOptions.vacuumIntervalSeconds = cmdMini.Flag.Int("master.vacuumIntervalSeconds", 840, "seconds between automatic vacuum sweeps") miniMasterOptions.vacuumDeleteEmptyAfterSeconds = cmdMini.Flag.Int("master.vacuumDeleteEmptyAfterSeconds", 0, "automatic sweep deletes volume copies that stay empty this many seconds; 0 disables") miniMasterOptions.defaultReplication = cmdMini.Flag.String("master.defaultReplication", "", "Default replication type if not specified.") diff --git a/weed/command/scaffold/master.toml b/weed/command/scaffold/master.toml index 3e06fa2259..dfa8472030 100644 --- a/weed/command/scaffold/master.toml +++ b/weed/command/scaffold/master.toml @@ -4,6 +4,13 @@ # /etc/seaweedfs/master.toml # this file is read by master +[master] +# cap on writable volumes per volume layout (collection + replica placement + +# ttl + disk type) that write assignments spread over; fullest picked first. +# 0 = no limit +max_writable_volumes_per_layout = 0 + + [master.maintenance] # periodically run these scripts are the same as running them from 'weed shell' # Scripts are skipped while an admin server is connected. diff --git a/weed/command/server.go b/weed/command/server.go index f64f3b8973..a1d17a5fab 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -98,7 +98,6 @@ func init() { masterOptions.volumeSizeLimitMB = cmdServer.Flag.Uint("master.volumeSizeLimitMB", util.DefaultVolumeSizeLimitMB, "Master stops directing writes to oversized volumes.") masterOptions.volumePreallocate = cmdServer.Flag.Bool("master.volumePreallocate", false, "Preallocate disk space for volumes.") masterOptions.maxParallelVacuumPerServer = cmdServer.Flag.Int("master.maxParallelVacuumPerServer", 1, "maximum number of volumes to vacuum in parallel on one volume server; 0 for no limit (deprecated, use master.vacuum.max_parallel_per_server in master.toml)") - masterOptions.maxWritableVolumes = cmdServer.Flag.Int("master.maxWritableVolumes", 0, "maximum writable volumes in a volume layout (collection + replica placement + ttl + disk type) assignments are spread over, fullest picked first; 0 means no limit") masterOptions.vacuumIntervalSeconds = cmdServer.Flag.Int("master.vacuumIntervalSeconds", 840, "seconds between automatic vacuum sweeps") masterOptions.vacuumDeleteEmptyAfterSeconds = cmdServer.Flag.Int("master.vacuumDeleteEmptyAfterSeconds", 0, "automatic sweep deletes volume copies that stay empty this many seconds; 0 disables") masterOptions.defaultReplication = cmdServer.Flag.String("master.defaultReplication", "", "Default replication type if not specified.") diff --git a/weed/server/master_server.go b/weed/server/master_server.go index 1e6cb58df9..595259c7b1 100644 --- a/weed/server/master_server.go +++ b/weed/server/master_server.go @@ -134,6 +134,8 @@ func NewMasterServer(r *mux.Router, option *MasterOption, peers map[string]pb.Se if v.IsSet("master.vacuum.max_parallel_per_server") { option.MaxParallelVacuumPerServer = v.GetInt("master.vacuum.max_parallel_per_server") } + v.SetDefault("master.max_writable_volumes_per_layout", option.MaxWritableVolumes) + option.MaxWritableVolumes = v.GetInt("master.max_writable_volumes_per_layout") topology.VolumeGrowStrategy.Copy1Count = v.GetUint32("master.volume_growth.copy_1") topology.VolumeGrowStrategy.Copy2Count = v.GetUint32("master.volume_growth.copy_2")