Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ roughtime -root-key-file /path/to/root.key -pq-root-key-file /path/to/pq-root.ke
| `-grease-rate` | 0.01 | Fraction of responses to grease (0 disables) |
| `-log-level` | info | `debug`, `info`, `warn`, or `error` |
| `-metrics-addr` | | `host:port` for Prometheus `/metrics` (no auth)|
| `-stats-interval` | 60s | Cadence of the periodic stats log (e.g. `10s`, `5m`); minimum 1s |

### Architecture

Expand Down
18 changes: 18 additions & 0 deletions cmd/roughtime/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,24 @@ func TestValidateFlagsRejects(t *testing.T) {
*greaseRate = 1.5
t.Cleanup(func() { *greaseRate = prev })
}, "-grease-rate"},
{"stats interval zero", func(t *testing.T) {
setRootKeyPath(t, "/x")
prev := *statsInterval
*statsInterval = 0
t.Cleanup(func() { *statsInterval = prev })
}, "-stats-interval"},
{"stats interval negative", func(t *testing.T) {
setRootKeyPath(t, "/x")
prev := *statsInterval
*statsInterval = -time.Second
t.Cleanup(func() { *statsInterval = prev })
}, "-stats-interval"},
{"stats interval below floor", func(t *testing.T) {
setRootKeyPath(t, "/x")
prev := *statsInterval
*statsInterval = 500 * time.Millisecond
t.Cleanup(func() { *statsInterval = prev })
}, "-stats-interval"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
Expand Down
7 changes: 6 additions & 1 deletion cmd/roughtime/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ var (
// metricsAddr is the host:port for the optional Prometheus /metrics
// endpoint; empty disables the listener entirely.
metricsAddr = flag.String("metrics-addr", "", "address (host:port) for the Prometheus /metrics endpoint; empty disables. No auth — use 127.0.0.1:PORT to restrict to loopback")
// statsInterval is the cadence of the periodic stats log.
statsInterval = flag.Duration("stats-interval", 60*time.Second, "cadence of the periodic stats log (e.g. 10s, 5m); minimum 1s")
)

// Server-wide tunable constants.
Expand Down Expand Up @@ -108,6 +110,9 @@ func validateFlags() error {
return fmt.Errorf("-metrics-addr %q invalid (want host:port): %w", *metricsAddr, err)
}
}
if *statsInterval < time.Second {
return fmt.Errorf("-stats-interval %v must be at least 1s", *statsInterval)
}
return nil
}

Expand Down Expand Up @@ -194,7 +199,7 @@ func serve(ctx context.Context) error {
enc.AddDuration("cert_end_offset", certEndOffset)
enc.AddDuration("cert_refresh_threshold", certRefreshThreshold)
enc.AddDuration("cert_check_interval", certCheckInterval)
enc.AddDuration("stats_interval", statsInterval)
enc.AddDuration("stats_interval", *statsInterval)
return nil
})),
)
Expand Down
2 changes: 2 additions & 0 deletions cmd/roughtime/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func withFlagGlobals(t *testing.T, edKey, pqKey, level string, p int, grease flo
origPQKeygen := *pqKeygen
origPQPubkey := *pqPubkey
origMetrics := *metricsAddr
origStatsInterval := *statsInterval
t.Cleanup(func() {
*port = origPort
*rootKeySeedHexFile = origEd
Expand All @@ -46,6 +47,7 @@ func withFlagGlobals(t *testing.T, edKey, pqKey, level string, p int, grease flo
*pqKeygen = origPQKeygen
*pqPubkey = origPQPubkey
*metricsAddr = origMetrics
*statsInterval = origStatsInterval
})
*port = p
*rootKeySeedHexFile = edKey
Expand Down
8 changes: 3 additions & 5 deletions cmd/roughtime/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import (
"go.uber.org/zap"
)

// statsInterval is the cadence of the periodic stats log.
var statsInterval = 60 * time.Second

// Server-wide un-labeled counters; labeled request/response/drop counters live
// in metrics.go.
var (
Expand All @@ -33,9 +30,10 @@ var (

// statsLoop emits a periodic summary of server activity until ctx is cancelled.
func statsLoop(ctx context.Context, log *zap.Logger, edState, pqState *atomic.Pointer[certState]) {
ticker := time.NewTicker(statsInterval)
interval := *statsInterval
ticker := time.NewTicker(interval)
defer ticker.Stop()
log.Info("stats loop started", zap.Duration("interval", statsInterval))
log.Info("stats loop started", zap.Duration("interval", interval))

var lastReceived, lastResponded, lastDropped, lastPanics, lastBatchCount, lastBatchTotal, lastBatchErrs uint64
var lastTCPAccepted, lastTCPRejected, lastTCPCompleted, lastAmp uint64
Expand Down
2 changes: 1 addition & 1 deletion cmd/roughtime/stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestStatsLoopExitsOnCtxCancel(t *testing.T) {
// TestStatsLoopTicks verifies statsLoop emits at least one log line on its tick
// interval.
func TestStatsLoopTicks(t *testing.T) {
withInterval(t, &statsInterval, 5*time.Millisecond)
withInterval(t, statsInterval, 5*time.Millisecond)
_, st := newUnitCertState(t)
statePtr := &atomic.Pointer[certState]{}
statePtr.Store(st)
Expand Down