{"record":{"id":"ca2356c7a8e9576a","repo":"geektutu/7days-golang","slug":"registerpeerpicker-called-more-than-once","errorCode":null,"errorMessage":"RegisterPeerPicker called more than once","messagePattern":"RegisterPeerPicker called more than once","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gee-cache/day5-multi-nodes/geecache/geecache.go","lineNumber":77,"sourceCode":"\n// Get value for a key from cache\nfunc (g *Group) Get(key string) (ByteView, error) {\n\tif key == \"\" {\n\t\treturn ByteView{}, fmt.Errorf(\"key is required\")\n\t}\n\n\tif v, ok := g.mainCache.get(key); ok {\n\t\tlog.Println(\"[GeeCache] hit\")\n\t\treturn v, nil\n\t}\n\n\treturn g.load(key)\n}\n\n// RegisterPeers registers a PeerPicker for choosing remote peer\nfunc (g *Group) RegisterPeers(peers PeerPicker) {\n\tif g.peers != nil {\n\t\tpanic(\"RegisterPeerPicker called more than once\")\n\t}\n\tg.peers = peers\n}\n\nfunc (g *Group) load(key string) (value ByteView, err error) {\n\tif g.peers != nil {\n\t\tif peer, ok := g.peers.PickPeer(key); ok {\n\t\t\tif value, err = g.getFromPeer(peer, key); err == nil {\n\t\t\t\treturn value, nil\n\t\t\t}\n\t\t\tlog.Println(\"[GeeCache] Failed to get from peer\", err)\n\t\t}\n\t}\n\n\treturn g.getLocally(key)\n}\n\nfunc (g *Group) populateCache(key string, value ByteView) {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-cache/day5-multi-nodes/geecache/geecache.go#L59-L95","documentation":"Group.RegisterPeers panics if it is called twice on the same Group: g.peers is already non-nil. Peer selection (the PeerPicker) is intended to be configured exactly once per group; re-registering would silently replace the peer topology mid-flight, so the library panics to surface the double initialization.","triggerScenarios":"Calling RegisterPeers twice on one Group — e.g. startCacheServer executed more than once for the same group in tests, re-running registration on config reload, or registering the same pool from two init paths.","commonSituations":"Test harness reusing a Group across subtests; hot-reload code re-applying peer config without recreating the Group; duplicated initialization in main and a helper.","solutions":["Call RegisterPeers exactly once per Group, right after NewGroup","Recreate the Group (or guard registration with a sync.Once / nil check) instead of re-registering","Refactor startup so peer setup happens in a single code path"],"exampleFix":"// before\ngrp.RegisterPeers(peers)\n// on reload:\ngrp.RegisterPeers(newPeers) // panic: RegisterPeerPicker called more than once\n// after\nif grp.GetPeers() == nil { // or use sync.Once\n    grp.RegisterPeers(newPeers)\n}","handlingStrategy":"validation","validationCode":"var peersRegistered sync.Once\nfunc registerPeers(g *geecache.Group, p geecache.PeerPicker) {\n    peersRegistered.Do(func() { g.RegisterPeers(p) })\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r == \"RegisterPeerPicker called more than once\" {\n        log.Println(\"peers already registered, ignoring\")\n    }\n}()\ng.RegisterPeers(peers)","preventionTips":["Call RegisterPeers exactly once, immediately after NewGroup","Wrap registration in sync.Once for idempotent startup","Never re-register peers on config reload — recreate the Group instead","Keep peer setup in a single startup function"],"tags":["go","cache","panic","initialization"],"backgroundTag":"duplicate-peer-registration","analyzedSha":"cf3644382101dc13e7fd92e8f5c66cabc51bcd3b","analyzedAt":"2026-09-03T18:31:24.087Z","contentChangedAt":"2026-09-03T18:31:24.087Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}