geektutu/7days-golang · error

RegisterPeerPicker called more than once

Error message

RegisterPeerPicker called more than once

What it means

Same as error 95, in the day6-single-flight version: RegisterPeers panics with "RegisterPeerPicker called more than once" when g.peers is already set. Peer configuration is one-shot per Group to prevent live replacement of the peer-selection topology.

Source

Thrown at gee-cache/day6-single-flight/geecache/geecache.go:82

// Get value for a key from cache
func (g *Group) Get(key string) (ByteView, error) {
	if key == "" {
		return ByteView{}, fmt.Errorf("key is required")
	}

	if v, ok := g.mainCache.get(key); ok {
		log.Println("[GeeCache] hit")
		return v, nil
	}

	return g.load(key)
}

// RegisterPeers registers a PeerPicker for choosing remote peer
func (g *Group) RegisterPeers(peers PeerPicker) {
	if g.peers != nil {
		panic("RegisterPeerPicker called more than once")
	}
	g.peers = peers
}

func (g *Group) load(key string) (value ByteView, err error) {
	// each key is only fetched once (either locally or remotely)
	// regardless of the number of concurrent callers.
	viewi, err := g.loader.Do(key, func() (interface{}, error) {
		if g.peers != nil {
			if peer, ok := g.peers.PickPeer(key); ok {
				if value, err = g.getFromPeer(peer, key); err == nil {
					return value, nil
				}
				log.Println("[GeeCache] Failed to get from peer", err)
			}
		}

		return g.getLocally(key)

View on GitHub (pinned to cf36443821)

Solutions

  1. Register peers once, immediately after NewGroup
  2. Use sync.Once or a nil-check guard around registration
  3. Recreate the Group when peer configuration changes

Example fix

// before
var once sync.Once
func setup(g *geecache.Group, p geecache.PeerPicker) {
    g.RegisterPeers(p) // called every time setup runs -> panic
}
// after
var once sync.Once
func setup(g *geecache.Group, p geecache.PeerPicker) {
    once.Do(func() { g.RegisterPeers(p) })
}
Defensive patterns

Strategy: validation

Validate before calling

var registerOnce sync.Once
func initPeers(g *geecache.Group, p geecache.PeerPicker) {
    registerOnce.Do(func() { g.RegisterPeers(p) })
}

Try / catch

defer func() {
    if r := recover(); r == "RegisterPeerPicker called more than once" {
        log.Println("RegisterPeers ignored: already registered")
    }
}()
g.RegisterPeers(peers)

Prevention

When it happens

Trigger: A second RegisterPeers call on the same Group — duplicate startup code, config reload re-registering peers, or tests reusing a Group instance across cases.

Common situations: Server restart logic that re-runs setup without recreating the Group; two packages each calling RegisterPeers on a shared group; retried init after a partial failure.

Related errors


AI-assisted analysis of geektutu/7days-golang@cf36443821 (2026-09-03). Data as JSON: /api/errors/4e35e9da16de8113. Report an issue: GitHub.