ResetRoutine vs RestartRoutine in the Go Keyed Package: What's the Difference?
ResetRoutine discards the existing routine and its stored data to create a brand-new instance via the constructor, while RestartRoutine preserves the current routine and its data, only canceling the running context and restarting execution.
The keyed package in aperturerobotics/util provides lifecycle management for keyed routines in Go, offering two distinct methods for re-executing background work. Understanding the architectural distinction between ResetRoutine and RestartRoutine is critical for managing stateful processes correctly, as both evaluate optional condition functions before proceeding but handle routine instances and stored data differently.
Core Architectural Differences
ResetRoutine: Complete Instance Replacement
ResetRoutine performs a hard reset by calling resetRoutineLocked in keyed/keyed.go (lines 90-124). This locked helper:
- Cancels the existing context via
ctxCancel - Invokes the constructor callback (
ctorCb) to generate a newRoutineinstance and fresh data value - Creates a new
runningRoutinevianewRunningRoutine - Overwrites the stored entry in the map with the new instance
According to the source code comments in keyed/keyed.go, this operation explicitly overwrites existing data: "Note: this will overwrite the existing Data, if present!"
RestartRoutine: Context Cancellation with Data Preservation
RestartRoutine delegates to restartRoutineLocked (lines 55-89 in keyed/keyed.go). This helper:
- Cancels the current context if running
- Retains the existing
runningRoutineinstance and its associatedv.data - Calls
start(..., forceRestart=true)to recreate the context and execute the same routine again
The original data remains intact throughout the operation, making this suitable for temporary failures where state should persist. The source code explicitly notes: "In most cases RestartRoutine is actually what you want."
Condition Evaluation in Both Methods
Both routines share identical condition-checking logic before execution. In keyed/keyed.go, the implementation checks:
anyMatched := len(conds) == 0
for _, cond := range conds {
if cond != nil && cond(key, v.data) {
anyMatched = true
break
}
}
if !anyMatched {
return true, false // existed, but no reset/restart performed
}
If no condition functions are supplied (len(conds) == 0), the operation proceeds unconditionally. If conditions are provided but none return true, both methods exit as no-ops, returning true, false to indicate the key existed but no action was taken.
Impact on Retry State and Back-off
When ResetRoutine creates a new runningRoutine, it discards any existing retry timers (deferRetry) attached to the previous instance. The new routine starts with fresh back-off state.
Conversely, RestartRoutine maintains the same runningRoutine object, preserving existing retry state and back-off timers. This distinction is crucial when implementing exponential back-off strategies where maintaining retry history matters.
Practical Usage Example
The following example demonstrates the behavioral difference using the keyed package:
package main
import (
"context"
"fmt"
"github.com/aperturerobotics/util/keyed"
)
// Constructor returns a routine and initial data
func ctor(key string) (keyed.Routine, string) {
r := func(ctx context.Context) error {
fmt.Printf("running %s with data=%s\n", key, ctx.Value("data"))
return nil
}
return r, "initial"
}
func main() {
km := keyed.NewKeyed[string, string](ctor)
// Initialize the routine
km.SetKey("foo", true)
// Restart: same data, new context
km.RestartRoutine("foo")
// Reset: new instance, constructor called again
km.ResetRoutine("foo")
}
In this example, RestartRoutine executes the same routine instance with preserved data, while ResetRoutine triggers the constructor again, potentially replacing the stored data value with whatever the constructor returns.
When to Use Each Method
Choose the appropriate method based on your state management requirements:
-
Use
ResetRoutinewhen you need a clean slate—such as when internal routine state is corrupted, cached data requires refreshing, or you want to reinitialize the constructor logic completely. -
Use
RestartRoutinewhen you only need to re-run the same routine after a temporary failure while maintaining existing data and state.
Summary
- ResetRoutine invokes the constructor to create a fresh routine instance and overwrites stored data in
keyed/keyed.go - RestartRoutine preserves the existing
runningRoutineand data, only canceling and restarting the context viarestartRoutineLocked - Both methods evaluate optional condition functions and skip execution if conditions don't match
- Reset operations discard retry timers and back-off state, while restart operations maintain them
- The constructor callback (
ctorCb) only runs during reset, not restart
Frequently Asked Questions
Does ResetRoutine always create a new data value?
Yes. When you call ResetRoutine, the resetRoutineLocked helper invokes your constructor callback (ctorCb) and explicitly overwrites the existing data storage with the new return value, as implemented in keyed/keyed.go lines 90-124.
Can RestartRoutine change the routine's data?
No. RestartRoutine retains the existing runningRoutine instance and its associated data. It only cancels the current context and calls start() with forceRestart=true, leaving the data untouched according to the implementation in lines 55-89 of keyed/keyed.go.
What happens to retry logic when I reset versus restart?
ResetRoutine creates a completely new runningRoutine instance, which discards any existing deferRetry timers and back-off state. RestartRoutine preserves the existing routine object, maintaining its retry history and back-off timers for consistent exponential back-off behavior.
When should I prefer RestartRoutine over ResetRoutine?
According to the source code comments in keyed/keyed.go, you should prefer RestartRoutine in most cases unless you explicitly need to refresh data or reinitialize the routine from scratch. Use ResetRoutine only when you need to replace the routine instance entirely or reset to a clean state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →