Skip to content

seaweedfs: a replicationFactor above 10 renders a four-digit replica placement that the master silently truncates #4294

Open

Description

packages/extra/seaweedfs/templates/seaweedfs.yaml builds defaultReplicaPlacement by subtraction, at four sites: "00{{ sub .Values.replicationFactor 1 }}" for Simple topology and "{{ sub .Values.replicationFactor 1 }}00" for MultiZone, once in the master block and once in the filer block. A SeaweedFS placement is exactly three digits, and the subtraction stays single-digit only up to replicationFactor: 10. Nothing bounds the field: values.schema.json types it as a plain integer, and the chart's only preflight is lt 1.

At 11 both blocks render four digits:

$ helm template sw packages/extra/seaweedfs --namespace tenant-root --set replicationFactor=11 ... \
    | grep -E '^      (master|filer|volume):|defaultReplicaPlacement'
      master:
        defaultReplicaPlacement: "0010"
      volume:
      filer:
        defaultReplicaPlacement: "0010"

The master one goes nowhere, since the master is rendered from master.defaultReplication instead: that is #4241. The filer one is read. Rendering the data-plane chart with it puts four digits on the filer's command line:

$ helm template sw packages/system/seaweedfs --namespace tenant-root \
    --set-string seaweedfs.filer.defaultReplicaPlacement=0010 \
    | grep -nE 'defaultReplicaPlacement=|defaultReplication='
4624:              -defaultReplicaPlacement=0010 \
4797:              -defaultReplication=001 \

weed/command/filer.go carries that flag into FilerOption.DefaultReplication, and the filer sends it with every assign. The master parses it at weed/server/master_grpc_server_assign.go:62, one line below the branch that would have substituted the master's own default, so both strings reach the same NewReplicaPlacementFromString call.

That function pads inputs shorter than three digits, then walks the string with a switch i carrying cases 0, 1 and 2. A fourth character matches no case and is dropped. The check after the loop is on the numeric value of the three that survived, so 0010 becomes 001 and no error comes back.

Measured on the shipped weed 4.31 binary, driven through the master's own default because it is the same call:

$ weed master -defaultReplication=0010 ...
(no error, no fatal, process stays up)
$ curl -s http://127.0.0.1:9444/dir/assign
{"error":"... for collection: replication:001 ttl: ..."}

So an operator who asks for 11 copies gets 2, and nothing reports it. Not the render, not the master log, not the assign path.

The same blind spot shows from another angle. The per-character test is only count < 0, with no upper one, so a non-digit survives too: a master started with -defaultReplication=00a logs nothing and becomes leader, because 'a' - '0' is 49 and the composite 49 is under the 255 ceiling. The function validates a sum of three already-decoded fields rather than the characters it decoded them from, which is why a fourth character is invisible to it as well.

A maximum: 10 on the field, or a preflight beside the existing lt 1, makes the wrong value visible at render time, which is the only place left that can see it.

Activity

  1. added
    kind/bugCategorizes issue or PR as related to a bug
    area/storageIssues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor)
    triage/acceptedIndicates an issue is ready to be actively worked on
    priority/backlogGeneral backlog priority. Lower than priority/important-longterm
    and removed
    triage/needs-triageIndicates an issue needs triage by a maintainer
    on Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/storageIssues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor)kind/bugCategorizes issue or PR as related to a bugpriority/backlogGeneral backlog priority. Lower than priority/important-longtermtriage/acceptedIndicates an issue is ready to be actively worked on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions