{"record":{"id":"a49af665bd16bcad","repo":"geektutu/7days-golang","slug":"registerpeerpicker-called-more-than-once-a49af6","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/day7-proto-buf/geecache/geecache.go","lineNumber":83,"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\t// each key is only fetched once (either locally or remotely)\n\t// regardless of the number of concurrent callers.\n\tviewi, err := g.loader.Do(key, func() (interface{}, error) {\n\t\tif g.peers != nil {\n\t\t\tif peer, ok := g.peers.PickPeer(key); ok {\n\t\t\t\tif value, err = g.getFromPeer(peer, key); err == nil {\n\t\t\t\t\treturn value, nil\n\t\t\t\t}\n\t\t\t\tlog.Println(\"[GeeCache] Failed to get from peer\", err)\n\t\t\t}\n\t\t}\n\n\t\treturn g.getLocally(key)","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-cache/day7-proto-buf/geecache/geecache.go#L65-L101","documentation":"Group.RegisterPeers panics if called more than once on the same Group. The peers (PeerPicker) is designed to be set exactly once at startup; re-registering would silently replace the peer selection strategy and break in-flight loads. This is a misuse guard against double initialization.","triggerScenarios":"Calling g.RegisterPeers(peers) a second time on a Group that already has peers registered, typically during re-configuration, hot-reload, or calling RegisterPeers in both setup code and a test helper.","commonSituations":"Test setups that build servers twice in one process; restarting the peer server without recreating the Group; accidentally calling RegisterPeers inside a loop or per-request path.","solutions":["Call RegisterPeers exactly once per Group, at initialization time","If peer configuration changed, create a new Group instead of re-registering","Guard the call site with an 'if g.Peers() == nil' style check or a sync.Once","Restructure tests so each Group is used by only one RegisterPeers call"],"exampleFix":"// before\ng.RegisterPeers(peers)\n// ... later ...\ng.RegisterPeers(newPeers) // panics\n// after\nif g.Peers() == nil {\n    g.RegisterPeers(newPeers)\n} else {\n    g = geecache.NewGroup(\"scores\", 2<<20, getter)\n    g.RegisterPeers(newPeers)\n}","handlingStrategy":"validation","validationCode":"if g.Peers() == nil {\n    g.RegisterPeers(peers)\n}","typeGuard":null,"tryCatchPattern":"// Go panics cannot be caught by try/catch; guard at call site or recover:\nfunc safeRegister(g *geecache.Group, peers geecache.PeerPicker) (ok bool) {\n    defer func() { if recover() != nil { ok = false } }()\n    g.RegisterPeers(peers)\n    return true\n}","preventionTips":["Register peers once during process startup, never per-request","Use sync.Once around RegisterPeers","In tests, create a fresh Group per test instead of reusing"],"tags":["go","panic","cache","initialization"],"backgroundTag":"double-registration-panic","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"}