{"record":{"id":"b6206586232c3d07","repo":"vitessio/vitess","slug":"theine-store-double-close","errorCode":null,"errorMessage":"theine.Store: double close","messagePattern":"theine\\.Store: double close","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/cache/theine/store.go","lineNumber":593,"sourceCode":"func (s *Store[K, V]) Range(epoch uint32, f func(key K, value V) bool) {\n\tfor _, shard := range s.shards {\n\t\tshard.mu.RLock()\n\t\tfor _, entry := range shard.hashmap {\n\t\t\tif entry.epoch.Load() < epoch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !f(entry.key, entry.value) {\n\t\t\t\tshard.mu.RUnlock()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tshard.mu.RUnlock()\n\t}\n}\n\nfunc (s *Store[K, V]) Close() {\n\tif !s.open.Swap(false) {\n\t\tpanic(\"theine.Store: double close\")\n\t}\n\n\tfor _, s := range s.shards {\n\t\ts.mu.Lock()\n\t\tclear(s.hashmap)\n\t\ts.mu.Unlock()\n\t}\n\tclose(s.writebuf)\n}\n","sourceCodeStart":575,"sourceCodeEnd":603,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/cache/theine/store.go#L575-L603","documentation":"theine.Store.Close uses an atomic open flag; if Close is called when open is already false (i.e. after a previous Close), it panics with 'double close'. The library treats closing twice as a caller bug rather than an idempotent operation.","triggerScenarios":"Calling s.Close() twice on the same theine.Store instance, often from two code paths each believing they own shutdown (e.g. defer Close plus explicit Close).","commonSituations":"Double shutdown during app teardown; defer + explicit close; cache wrapper layer also closing the underlying store; tests closing in both t.Cleanup and the test body.","solutions":["Ensure Close is called exactly once per Store — use sync.Once or remove duplicate close sites","Track ownership so only the creator closes the store","If needed, wrap the store with a once-guard: var once sync.Once; once.Do(func(){ s.Close() })"],"exampleFix":"// before\ns.Close()\n...\ns.Close() // panics: double close\n// after\nvar closeOnce sync.Once\ncloseOnce.Do(func() { s.Close() })","handlingStrategy":"try-catch","validationCode":"func (c *CacheWrapper[K, V]) CloseOnce() {\n    c.closeOnce.Do(func() { c.store.Close() })\n}","typeGuard":null,"tryCatchPattern":"func safeClose[K, V any](s *cache.Store[K, V]) (closed bool) {\n    defer func() {\n        if r := recover(); r != nil {\n            if fmt.Sprint(r) == \"theine.Store: double close\" {\n                closed = true // already closed, treat as success\n            } else {\n                panic(r)\n            }\n        }\n    }()\n    s.Close()\n    return true\n}","preventionTips":["Guard Close with sync.Once at every call site","Define a single owner responsible for store shutdown","Remove redundant defer+explicit Close pairs"],"tags":["cache","theine","panic","lifecycle"],"backgroundTag":"double-close","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}