rustfs / rustfs/rustfs

Census never becomes correct on a continuously-written cluster: every cycle is Superseded, per-bucket cache never seeded — datausageinfo reports 36 objects for a 151k-object bucket (rc.5)

Open
#7,108 5 comments 0 reactions 2 assignees View on GitHub

@overtrue is already working on this.

Since Sep 4, 2026.

S-confirming
Dominant language
Rust
Stars
33.3k
Forks
1.5k
Avg merge
2h 57m
Merged PRs (30d)
962

Description

Environment

  • RustFS 1.0.0-rc.5 (official image, digest c36b3efe…), Docker, 3 nodes × 2 drives, EC 4+2, TLS
  • Same clusters as #6907 / #6958 / #6218. Scanner state was fully reset on 2026-09-02 (bootstrap from scratch, see #6907), so there is no inherited state involved here.
  • Workload: one bucket (loki-dati, 150,994 objects / 85.3 GB by mc du) receives continuous Loki writes — new chunks every few seconds, around the clock. RUSTFS_SCANNER_CYCLE=3600 (explicit cadence); everything else default.

Symptom

datausageinfo (and therefore the console and quota accounting) reports the cluster as 6,498 objects / 0.4 GB, with loki-dati at 36 objects / 0.01 GB — off by ~23× on count and ~200× on size — and it has stayed like this since the reset, while the S3 data plane is perfectly healthy:

datausageinfo: objects_total_count 6498 | objects_total_size 0.4 GB | buckets_count 14
               usage_snapshot_complete: true | usage_snapshot_converged: false | scanner_epoch 2 | scanner_cycle 30
  loki-dati        36 objects   0.01 GB      (real: 150,994 objects / 85.3 GB)
  ads-loki       6013 objects   0.02 GB      (real: 2,223 objects — the census counts versions here, fine)

scanner/status on the leader:

cycle_schedule: effective_interval 3600, superseded_cycles 28, superseded_retry_backoff_enabled true
metrics: last_cycle_result "superseded", last_cycle_duration_seconds 44.6, last_cycle_objects_scanned 6215,
         cycles_completed_at [], usage_freshness.last_publication_state "success"

Every cycle since the reset ended superseded; no cycle has ever ended Complete (cycles_completed_at is empty). Each hourly cycle lasts ~45 s and visits ~6k objects — the dirty subset — and the per-bucket usage cache for loki-dati (.rustfs.sys/buckets/loki-dati/.usage-cache.bin) is tiny (xl.meta ≈ 885 B, rewritten every cycle), i.e. it was never seeded with a full walk.

Why (from the rc.5 source)

classify_nsscanner_cycle (crates/scanner/src/scanner_io.rs) returns Complete only when the scan finished and activity_status == Unchanged && dirty_usage_status == Current; any concurrent namespace mutation during the cycle makes it Superseded. Superseded cycles do publish (should_publish_usage_snapshot), but — per the comment in scanner.rs — "a superseded cycle is explicitly observational and cannot replace the authoritative snapshot". On a cluster that is written every few seconds, every cycle is superseded, so:

  1. the bootstrap full walk after the reset (≈921k folder/object scans across the nodes) was itself superseded → never became the baseline, per-bucket caches never seeded;
  2. every subsequent cycle walks only the dirty subset (~6k objects in 45 s) and publishes it as the census;
  3. usage_snapshot_complete: true is reported for a snapshot that covers 4% of the objects.

The activity check appears to be cluster-wide (namespace mutation generation), so a single busy bucket prevents convergence for the whole cluster.

Control experiment — our second cluster, same version and config, where the hot bucket is written in hourly bursts (mirror jobs) with quiet gaps in between: census 22,496 vs real 22,534 objects on that bucket — correct. So the mechanism works as soon as one cycle can finish without concurrent writes; a site that never has such a gap never gets a correct census. This is the census-correctness side of the "never-converged site" problem (#6852): #6896 made the observed snapshot publishable (no more quota fail-close), but the published numbers are the dirty subset, not the usage.

Suggestions

  1. A structurally complete walk that was superseded only by concurrent writes is a far better baseline than nothing: let it seed the per-bucket/per-set caches and the authoritative snapshot (marked stale-by-N-mutations if you like) instead of discarding it as observational. Convergence can then proceed incrementally, as it does on the quiet cluster.
  2. Scope supersession per bucket/set (a write into bucket A should not invalidate the walk of bucket B), or make the activity proof tolerant of the mutation rate observed during the cycle.
  3. Don't report usage_snapshot_complete: true when the snapshot is a dirty-subset publication.
  4. Document that clusters with continuous writes cannot currently converge (until the above lands), and that usage-state/reset does not help in that situation.

Full scanner/status / datausageinfo JSON and the per-bucket cache objects available on request; happy to test a patch on this cluster, as usual.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.