{"record":{"id":"3f4b0a50cc4a73aa","repo":"thanos-io/thanos","slug":"errpoolexhausted","errorCode":"ErrPoolExhausted","errorMessage":"pool exhausted","messagePattern":"pool exhausted","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/pool/pool.go","lineNumber":86,"sourceCode":"\tvar sizes []int\n\n\tfor s := minSize; s <= maxSize; s = int(float64(s) * factor) {\n\t\tsizes = append(sizes, s)\n\t}\n\tp := &BucketedPool[T]{\n\t\tbuckets:  make([]sync.Pool, len(sizes)),\n\t\tsizes:    sizes,\n\t\tmaxTotal: maxTotal,\n\t\tnew: func(sz int) *[]T {\n\t\t\ts := make([]T, 0, sz)\n\t\t\treturn &s\n\t\t},\n\t}\n\treturn p, nil\n}\n\n// ErrPoolExhausted is returned if a pool cannot provide the requested slice.\nvar ErrPoolExhausted = errors.New(\"pool exhausted\")\n\n// Get returns a slice into from the bucket that fits the given size.\nfunc (p *BucketedPool[T]) Get(sz int) (*[]T, error) {\n\tp.mtx.Lock()\n\tdefer p.mtx.Unlock()\n\n\tif p.maxTotal > 0 && p.usedTotal+uint64(sz) > p.maxTotal {\n\t\treturn nil, ErrPoolExhausted\n\t}\n\n\tfor i, bktSize := range p.sizes {\n\t\tif sz > bktSize {\n\t\t\tcontinue\n\t\t}\n\t\tb, ok := p.buckets[i].Get().(*[]T)\n\t\tif !ok {\n\t\t\tb = p.new(bktSize)\n\t\t}","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/pool/pool.go#L68-L104","documentation":"ErrPoolExhausted is returned by BucketedPool.Get when maxTotal is nonzero and allocating a slice of the requested size would push total used bytes over the cap. It is a backpressure signal: the pool is protecting memory by refusing the allocation, not an internal fault.","triggerScenarios":"BucketedPool.Get(sz) with p.maxTotal > 0 and usedTotal + sz > maxTotal, e.g. chunkPool.Get(600) when the pool's remaining capacity is below 600.","commonSituations":"High concurrent ingest exceeding configured chunk pool memory limit; leak of Put() calls (forgotten returns) inflating usedTotal; undersized pool for workload.","solutions":["Ensure callers always return slices via Put so usedTotal is released","Increase the pool's maxTotal limit or remove it (maxTotal = 0 means unlimited)","Reduce concurrency or per-item sizes consuming the pool","Handle the error at Get call sites with backpressure (retry after returning slices)"],"exampleFix":"// before\nb, err := chunkPool.Get(600)\nif err != nil { return err }\n// after\nb, err := chunkPool.Get(600)\nif errors.Is(err, pool.ErrPoolExhausted) {\n    return ErrBackpressure // drop/retry; pool memory cap reached\n}","handlingStrategy":"try-catch","validationCode":"// check headroom before a large Get\nif chunkPool.Capacity() != 0 { /* if a capacity accessor exists, compare against sz */ }","typeGuard":null,"tryCatchPattern":"b, err := chunkPool.Get(sz)\nif errors.Is(err, pool.ErrPoolExhausted) {\n    // backpressure: shed load or retry after Put releases memory\n    return ErrBusy\n}","preventionTips":["Always call Put for every Get (use defer for short-lived slices)","Size maxTotal to peak concurrent ingest, not average","Set maxTotal = 0 if unbounded allocation is acceptable","Monitor pool usage metrics and alert near the cap"],"tags":["memory","pool","backpressure"],"backgroundTag":"resource-limit-exceeded","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}