{"record":{"id":"4e35e9da16de8113","repo":"geektutu/7days-golang","slug":"registerpeerpicker-called-more-than-once-4e35e9","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/day6-single-flight/geecache/geecache.go","lineNumber":82,"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":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-cache/day6-single-flight/geecache/geecache.go#L64-L100","documentation":"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.","triggerScenarios":"A second RegisterPeers call on the same Group — duplicate startup code, config reload re-registering peers, or tests reusing a Group instance across cases.","commonSituations":"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.","solutions":["Register peers once, immediately after NewGroup","Use sync.Once or a nil-check guard around registration","Recreate the Group when peer configuration changes"],"exampleFix":"// before\nvar once sync.Once\nfunc setup(g *geecache.Group, p geecache.PeerPicker) {\n    g.RegisterPeers(p) // called every time setup runs -> panic\n}\n// after\nvar once sync.Once\nfunc setup(g *geecache.Group, p geecache.PeerPicker) {\n    once.Do(func() { g.RegisterPeers(p) })\n}","handlingStrategy":"validation","validationCode":"var registerOnce sync.Once\nfunc initPeers(g *geecache.Group, p geecache.PeerPicker) {\n    registerOnce.Do(func() { g.RegisterPeers(p) })\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r == \"RegisterPeerPicker called more than once\" {\n        log.Println(\"RegisterPeers ignored: already registered\")\n    }\n}()\ng.RegisterPeers(peers)","preventionTips":["One registration site per binary, wrapped in sync.Once","On peer changes, construct a new Group rather than re-registering","Guard against retried init paths calling setup twice"],"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"}