Files
seaweedfs/weed/filer/placement_overlay.go
Chris LuandGitHub 0f718f8509 filer: add a placement overlay seam for the write path (#10437)
* filer: add a placement overlay seam for the write path

New volumes take their disk type, replication, and data center from the
explicit request or the matched filer.conf rule. That leaves no way for a
feature to steer a whole collection onto a medium without an operator
writing an fs.configure rule by hand.

Add a generic PlacementOverlay hook on the filer: a func that maps a
collection to a placement override, installed by a factory the way the
plugin-worker handlers register. detectStorageOption consults it between
the explicit request value and the filer.conf rule, so it overrides the
rule but yields to a value the caller asked for.

The seam names no feature concepts, so it stays generic; a downstream
build registers the overlay it wants (e.g. a storage-class Landing tier).

Claude-Session: https://claude.ai/code/session_01Ks16jnt4S7gdDk8cheQ3xu

* filer: address review on the placement overlay seam

Honor ResolvePlacement's ok flag explicitly rather than relying on empty
values falling through the util.Nvl chain, and log at V(4) when the
overlay steers a collection. Document that RegisterPlacementOverlay is
init-only, so the unsynchronized read in NewFiler cannot race the write.

Claude-Session: https://claude.ai/code/session_01Ks16jnt4S7gdDk8cheQ3xu
2026-07-25 11:20:41 -07:00

37 lines
1.9 KiB
Go

package filer
// PlacementOverlay resolves a write-time placement override for a collection —
// the disk type, replication, and data center new volumes of that collection
// should land on — or ok=false when the collection has none.
//
// It exists so a feature can steer where a bound collection's data lands without
// every operator setting an fs.configure rule by hand. The type is deliberately
// generic: it names no placement-policy concepts, so a downstream build
// registers whatever overlay it wants without coupling this seam to it.
type PlacementOverlay func(collection string) (diskType, replication, dataCenter string, ok bool)
// newPlacementOverlay builds the overlay for a filer. Enterprise sets it in an
// init() pulled in by a side-effect import, the way plugin-worker handlers
// register — the setter cannot live here because this package is overwritten by
// the sync.
var newPlacementOverlay func(*Filer) PlacementOverlay
// RegisterPlacementOverlay installs the factory. It is meant to be called from a
// package init() — before any Filer is constructed and before serving starts —
// so the unsynchronized read in NewFiler never races the write. The last
// registration wins; there is only ever one overlay.
func RegisterPlacementOverlay(factory func(*Filer) PlacementOverlay) {
newPlacementOverlay = factory
}
// ResolvePlacement returns the overlay's placement for a collection, or
// ok=false when there is no overlay or the collection is not steered. The write
// path applies these between the explicit request value and the filer.conf
// rule, so an overlay overrides the rule but yields to an explicit request.
func (f *Filer) ResolvePlacement(collection string) (diskType, replication, dataCenter string, ok bool) {
if f == nil || f.placementOverlay == nil || collection == "" {
return "", "", "", false
}
return f.placementOverlay(collection)
}