Files
seaweedfs/weed/worker/tasks/s3_lifecycle/config.go
T
Chris Lu ce5768fab1 feat(s3/lifecycle): operator-declared meta-log retention activates PromotedHash (#9473)
* feat(s3/lifecycle): operator-declared meta-log retention activates PromotedHash

dailyrun.Config.RetentionWindow has been wired since Phase 4b but the
handler never supplied a value, so runShard always fell back to
maxTTL and engine.PromotedHash hashed nothing. The partition-flip
recovery trigger was dormant by design "until the handler plumbs the
real meta-log retention here."

This PR plumbs it via a new admin form field:
  Meta-Log Retention (days) — 0 = unbounded (current behavior).

When set, ParseConfig converts days to a time.Duration on
cfg.MetaLogRetention. The handler passes it as
dailyrun.Config.RetentionWindow, which runShard then feeds to
engine.PromotedHash. Rules whose TTL exceeds the declared window
land in the walk partition; the next time an operator shrinks
retention so a previously replay-eligible rule slips past it,
PromotedHash mismatches → recovery branch fires → walker re-evaluates
the rule across the whole filer tree.

0 stays the default, so existing deployments see no behavior change.

* chore(s3/lifecycle): rephrase days->duration conversion

gemini-code-assist flagged the original form as a compile error,
which it wasn't (time.Duration is a named int64 and supports * with
other time.Durations — the test suite verified the value was
correct). The suggested form is more idiomatic regardless:
days*24 happens in int64 space before the lift to time.Duration,
so the unit is unambiguous.
2026-05-12 18:26:52 -07:00

57 lines
1.5 KiB
Go

package s3_lifecycle
import (
"time"
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
)
const (
jobType = "s3_lifecycle"
// In-process fan-out across the 16 shards.
shardPipelineGoroutines = 1
defaultMaxRuntimeMinutes = int64(60)
)
type Config struct {
Workers int
MaxRuntime time.Duration
MetaLogRetention time.Duration
}
func ParseConfig(adminValues map[string]*plugin_pb.ConfigValue, workerValues map[string]*plugin_pb.ConfigValue) Config {
cfg := Config{
Workers: shardPipelineGoroutines,
MaxRuntime: time.Duration(readInt64(workerValues, "max_runtime_minutes", defaultMaxRuntimeMinutes)) * time.Minute,
}
if cfg.MaxRuntime <= 0 {
cfg.MaxRuntime = time.Duration(defaultMaxRuntimeMinutes) * time.Minute
}
// Operator-declared meta-log retention. Negative or zero values stay
// zero so runShard falls back to maxTTL (PromotedHash dormant).
// Convert days->hours in int64 space before lifting to time.Duration
// so the unit is unambiguous.
if days := readInt64(adminValues, MetaLogRetentionDaysAdminKey, 0); days > 0 {
cfg.MetaLogRetention = time.Duration(days*24) * time.Hour
}
return cfg
}
func readInt64(values map[string]*plugin_pb.ConfigValue, field string, fallback int64) int64 {
v, ok := values[field]
if !ok || v == nil {
return fallback
}
switch k := v.Kind.(type) {
case *plugin_pb.ConfigValue_Int64Value:
return k.Int64Value
case *plugin_pb.ConfigValue_DoubleValue:
return int64(k.DoubleValue)
case *plugin_pb.ConfigValue_StringValue:
return fallback
}
return fallback
}