diff --git a/README.md b/README.md index dc8f4fa..cfb6861 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,9 @@ services: namespace: keepalive - adapter: valkey - # One service can override the global interval. + # One service can override the global interval and counter key. interval: 30s + counter_key: valkey-counter config: url: valkey://default@valkey-a.example.com:6379 @@ -56,8 +57,11 @@ services: `interval` at the root sets the default schedule for every service and defaults to `1m`. `interval` inside a service overrides that default only for that service. In the example above, every service runs every `1m` except `valkey`, which runs every `30s`. +Interval values use Go duration syntax, for example `30s`, `5m`, `1h`, `1h30m`, or `1.5h`. Plain integers are treated as seconds, so `90` means `90s`. -`counter_key` can be set globally or per service. Per-service values override global values. +`counter_key` at the root sets the default counter key for every service and defaults to `counter`. +`counter_key` inside a service overrides that default only for that service. +In the example above, every service writes `counter` except `valkey`, which writes `valkey-counter`. ## Supported adapters diff --git a/config.example.yml b/config.example.yml index 3276f5a..68a449b 100644 --- a/config.example.yml +++ b/config.example.yml @@ -1,4 +1,5 @@ # Optional default interval shared by every service. +# Examples: 30s, 5m, 1h, 1h30m, 1.5h, or 90 for 90 seconds. interval: 1m counter_key: counter @@ -9,8 +10,9 @@ services: namespace: keepalive - adapter: valkey - # Example: this service runs every 30s while others use the root interval. + # Example: this service overrides root interval and counter_key. interval: 30s + counter_key: valkey-counter config: url: valkey://default@valkey-a.example.com:6379 diff --git a/config.go b/config.go index 8639a07..f2d1691 100644 --- a/config.go +++ b/config.go @@ -138,7 +138,7 @@ func parseConfigInterval(field, value string, def time.Duration) (time.Duration, } return d, nil } - return 0, fmt.Errorf("%s must be a duration like 30s or an integer number of seconds", field) + return 0, fmt.Errorf("%s must be a duration like 30s, 5m, or 1h30m, or an integer number of seconds", field) } func valueOrDefault(value, def string) string { diff --git a/config_test.go b/config_test.go index 8a04d38..cfbb4b0 100644 --- a/config_test.go +++ b/config_test.go @@ -51,6 +51,15 @@ func TestConfigExampleYMLParses(t *testing.T) { if services[2].Interval != time.Minute { t.Fatalf("services[2].Interval = %s, want 1m", services[2].Interval) } + if services[0].Config["counter_key"] != "counter" { + t.Fatalf("services[0] counter_key = %q, want counter", services[0].Config["counter_key"]) + } + if services[1].Config["counter_key"] != "valkey-counter" { + t.Fatalf("services[1] counter_key = %q, want valkey-counter", services[1].Config["counter_key"]) + } + if services[2].Config["counter_key"] != "counter" { + t.Fatalf("services[2] counter_key = %q, want counter", services[2].Config["counter_key"]) + } } func TestFirstExistingConfigFileFindsYMLFallback(t *testing.T) { @@ -156,6 +165,26 @@ func TestNormalizeConfigAppliesGlobalAndServiceDefaults(t *testing.T) { } } +func TestNormalizeConfigParsesCompoundAndFractionalIntervals(t *testing.T) { + services, err := normalizeConfig(appConfig{ + Interval: "1h30m", + Services: []serviceFileConfig{ + {Adapter: "redis", Config: map[string]string{"url": "redis://cache.example.com:6379"}}, + {Adapter: "redis", Interval: "1.5h", Config: map[string]string{"url": "redis://other.example.com:6379"}}, + }, + }) + if err != nil { + t.Fatalf("normalizeConfig returned error: %v", err) + } + + if services[0].Interval != 90*time.Minute { + t.Fatalf("services[0].Interval = %s, want 1h30m", services[0].Interval) + } + if services[1].Interval != 90*time.Minute { + t.Fatalf("services[1].Interval = %s, want 1.5h", services[1].Interval) + } +} + func TestNormalizeConfigRejectsNonPositiveInterval(t *testing.T) { _, err := normalizeConfig(appConfig{ Interval: "0s",