mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-09 21:38:19 +01:00
* 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.
70 lines
2.3 KiB
Go
70 lines
2.3 KiB
Go
package s3_lifecycle
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
|
|
)
|
|
|
|
func TestParseConfigDefaults(t *testing.T) {
|
|
cfg := ParseConfig(nil, nil)
|
|
if cfg.Workers != shardPipelineGoroutines {
|
|
t.Errorf("Workers default=%d, want %d", cfg.Workers, shardPipelineGoroutines)
|
|
}
|
|
if cfg.MaxRuntime != 60*time.Minute {
|
|
t.Errorf("MaxRuntime default=%v, want 60m", cfg.MaxRuntime)
|
|
}
|
|
}
|
|
|
|
func TestParseConfigOverrideMaxRuntime(t *testing.T) {
|
|
worker := map[string]*plugin_pb.ConfigValue{
|
|
"max_runtime_minutes": {Kind: &plugin_pb.ConfigValue_Int64Value{Int64Value: 120}},
|
|
}
|
|
cfg := ParseConfig(nil, worker)
|
|
if cfg.MaxRuntime != 120*time.Minute {
|
|
t.Errorf("MaxRuntime=%v, want 120m", cfg.MaxRuntime)
|
|
}
|
|
}
|
|
|
|
func TestParseConfigNegativeMaxRuntimeClampsToDefault(t *testing.T) {
|
|
worker := map[string]*plugin_pb.ConfigValue{
|
|
"max_runtime_minutes": {Kind: &plugin_pb.ConfigValue_Int64Value{Int64Value: -5}},
|
|
}
|
|
cfg := ParseConfig(nil, worker)
|
|
if cfg.MaxRuntime != 60*time.Minute {
|
|
t.Errorf("negative MaxRuntime should clamp to default, got %v", cfg.MaxRuntime)
|
|
}
|
|
}
|
|
|
|
func TestParseConfigMetaLogRetentionDefaultsToZero(t *testing.T) {
|
|
// Unset key keeps MetaLogRetention at 0, which runShard treats as
|
|
// "no retention info supplied" and falls back to maxTTL.
|
|
cfg := ParseConfig(nil, nil)
|
|
if cfg.MetaLogRetention != 0 {
|
|
t.Errorf("MetaLogRetention default=%v, want 0", cfg.MetaLogRetention)
|
|
}
|
|
}
|
|
|
|
func TestParseConfigMetaLogRetentionDaysConvertsToDuration(t *testing.T) {
|
|
admin := map[string]*plugin_pb.ConfigValue{
|
|
MetaLogRetentionDaysAdminKey: {Kind: &plugin_pb.ConfigValue_Int64Value{Int64Value: 7}},
|
|
}
|
|
cfg := ParseConfig(admin, nil)
|
|
if want := 7 * 24 * time.Hour; cfg.MetaLogRetention != want {
|
|
t.Errorf("MetaLogRetention=%v, want %v (7 days)", cfg.MetaLogRetention, want)
|
|
}
|
|
}
|
|
|
|
func TestParseConfigMetaLogRetentionNegativeStaysZero(t *testing.T) {
|
|
// A negative declaration is nonsense; stay at 0 so runShard's
|
|
// fallback applies rather than producing a negative window.
|
|
admin := map[string]*plugin_pb.ConfigValue{
|
|
MetaLogRetentionDaysAdminKey: {Kind: &plugin_pb.ConfigValue_Int64Value{Int64Value: -3}},
|
|
}
|
|
cfg := ParseConfig(admin, nil)
|
|
if cfg.MetaLogRetention != 0 {
|
|
t.Errorf("negative MetaLogRetention should stay 0, got %v", cfg.MetaLogRetention)
|
|
}
|
|
}
|