periodic

package
v1.153.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package periodic schedules a task function to run repeatedly at a fixed interval, with optional random jitter and a per-invocation context timeout.

New constructs a Periodic scheduler from an interval, a jitter ceiling, a per-call timeout, and a TaskFn. Periodic.Start runs the task in a dedicated goroutine and Periodic.Stop shuts it down:

p, err := periodic.New(
	30*time.Second,  // run every 30 s
	5*time.Second,   // add up to 5 s of random jitter
	10*time.Second,  // each call gets a 10 s deadline
	myTask,
)
if err != nil {
	log.Fatal(err)
}

p.Start(ctx)
defer p.Stop()

Behavior

  • Fixed interval with random jitter: the pause between calls is interval + rand(0, jitter), spreading steady-state load across a fleet to avoid a thundering herd (https://en.wikipedia.org/wiki/Thundering_herd_problem).
  • Per-call timeout: each TaskFn invocation receives a context.Context derived from the parent with an independent deadline.
  • Context-aware shutdown: Periodic.Start accepts a parent context; canceling it (or calling Periodic.Stop) stops the loop after the current task invocation returns, without leaking a goroutine.
  • Eager first execution: by default the first call fires after ~1 ns so the task runs immediately on start rather than waiting for the first full interval. This first call is not jittered, so a fleet that starts in lockstep (a rolling deploy, a simultaneous restart) fires every replica's first call together; pass WithInitialJitter to spread the first call across [0, jitter) as well.

Constraints

  • interval must be > 0.
  • jitter must be >= 0 (pass 0 to disable jitter entirely).
  • timeout must be > 0.
  • task must not be nil.
  • task must not panic: it runs in the background goroutine with no recovery, so a panic crashes the process (recover inside the task if needed).

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Option added in v1.153.0

type Option func(*Periodic)

Option configures optional Periodic behavior. Pass options as the trailing arguments to New.

func WithInitialJitter added in v1.153.0

func WithInitialJitter() Option

WithInitialJitter delays the first invocation by a random duration in [0, jitter) instead of firing it immediately. Use it when many instances of a service start at once (a rolling deploy, a fleet-wide restart): with the eager default every replica's first call lands on the target simultaneously, which is the very thundering herd the per-tick jitter exists to prevent. It costs up to jitter of extra startup latency, once, and is a no-op when jitter is 0.

type Periodic

type Periodic struct {
	// contains filtered or unexported fields
}

Periodic schedules a TaskFn to run repeatedly with configurable interval and jitter. Create one with New, start it with Periodic.Start, and stop it with Periodic.Stop. The zero value is not usable; always use New.

func New

func New(interval time.Duration, jitter time.Duration, timeout time.Duration, task TaskFn, opts ...Option) (*Periodic, error)

New constructs a Periodic scheduler with constraints on interval, jitter, timeout, and task validation. Optional Option values configure non-default behavior (see WithInitialJitter). Returns error if any parameter violates its constraint; call Start() to begin execution.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/tecnickcom/nurago/pkg/periodic"
)

func main() {
	count := make(chan int, 1)

	count <- 0

	// example task to execute periodically
	task := func(_ context.Context) {
		v := <-count
		count <- (v + 1)
	}

	interval := 20 * time.Millisecond
	jitter := 2 * time.Millisecond
	timeout := 2 * time.Millisecond

	// create a new periodic job
	p, err := periodic.New(interval, jitter, timeout, task)
	if err != nil {
		close(count)
		log.Fatal(err)
	}

	// start the periodic job
	p.Start(context.TODO())

	// wait for 3 times the interval
	wait := 3 * interval
	time.Sleep(wait)

	// stop the periodic job
	p.Stop()

	fmt.Println(<-count)

	close(count)

}
Output:
3

func (*Periodic) Start

func (p *Periodic) Start(ctx context.Context)

Start begins periodic task execution in a background goroutine. The first invocation fires almost immediately by default (or after up to jitter when constructed with WithInitialJitter); subsequent calls are at interval+rand(0,jitter) after each completion. The loop exits when ctx is canceled or Stop() is called. A second Start on an already-started or already-stopped instance is a no-op, so it never leaks a goroutine; the instance is single-use.

If ctx is canceled just before the first tick, the task may still run once (with the already-canceled context); a well-behaved task returns promptly.

func (*Periodic) Stop

func (p *Periodic) Stop()

Stop cancels the execution loop and waits for the current task invocation to complete. Safe to call multiple times, before Start(), or concurrently with Start(): whatever the ordering, once Stop returns the scheduler is stopped and will not (re)start. A Stop that wins the race before Start prevents the loop from ever launching. The instance is single-use and cannot be restarted after Stop.

type TaskFn

type TaskFn func(context.Context)

TaskFn is the function signature executed on each scheduler tick. The context passed in carries the deadline configured via the timeout parameter of New. Implementations should respect context cancellation and return promptly when ctx.Done() is closed.

The task runs in the scheduler's background goroutine with no panic recovery: a panic in the task crashes the process. Recover inside the task if it may panic and the scheduler must survive.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL