# ResetRoutine vs RestartRoutine in the Go Keyed Package: What's the Difference?

> Understand the distinct differences between ResetRoutine and RestartRoutine in Go's keyed package. Learn when to discard or preserve routines and data for optimal performance.

- Repository: [Aperture Robotics/util](https://github.com/aperturerobotics/util)
- Tags: deep-dive
- Published: 2026-02-25

---

**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](https://github.com/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`](https://github.com/aperturerobotics/util/blob/main/keyed/keyed.go) (lines 90-124). This locked helper:

1. Cancels the existing context via `ctxCancel`
2. Invokes the constructor callback (`ctorCb`) to generate a **new** `Routine` instance and fresh data value
3. Creates a new `runningRoutine` via `newRunningRoutine`
4. Overwrites the stored entry in the map with the new instance

According to the source code comments in [`keyed/keyed.go`](https://github.com/aperturerobotics/util/blob/main/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`](https://github.com/aperturerobotics/util/blob/main/keyed/keyed.go)). This helper:

1. Cancels the current context if running
2. Retains the existing `runningRoutine` instance and its associated `v.data`
3. 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`](https://github.com/aperturerobotics/util/blob/main/keyed/keyed.go), the implementation checks:

```go
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:

```go
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 `ResetRoutine`** when 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 `RestartRoutine`** when 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`](https://github.com/aperturerobotics/util/blob/main/keyed/keyed.go)
- **RestartRoutine** preserves the existing `runningRoutine` and data, only canceling and restarting the context via `restartRoutineLocked`
- 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`](https://github.com/aperturerobotics/util/blob/main/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`](https://github.com/aperturerobotics/util/blob/main/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`](https://github.com/aperturerobotics/util/blob/main/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.