Co-authored-by: Aleksandr Tolstikhin <atolstikhin@mtsbank.ru> Reviewed-on: #365 Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org> Co-committed-by: Vasiliy Tolstov <v.tolstov@unistack.org>
		
			
				
	
	
		
			235 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package pool
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
	"sync/atomic"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"go.unistack.org/micro/v3/meter"
 | 
						|
	"go.unistack.org/micro/v3/semconv"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	pools   = make([]Statser, 0)
 | 
						|
	poolsMu sync.Mutex
 | 
						|
)
 | 
						|
 | 
						|
// Stats struct
 | 
						|
type Stats struct {
 | 
						|
	Get uint64
 | 
						|
	Put uint64
 | 
						|
	Mis uint64
 | 
						|
	Ret uint64
 | 
						|
}
 | 
						|
 | 
						|
// Statser provides buffer pool stats
 | 
						|
type Statser interface {
 | 
						|
	Stats() Stats
 | 
						|
	Cap() int
 | 
						|
}
 | 
						|
 | 
						|
func init() {
 | 
						|
	go newStatsMeter()
 | 
						|
}
 | 
						|
 | 
						|
func newStatsMeter() {
 | 
						|
	ticker := time.NewTicker(meter.DefaultMeterStatsInterval)
 | 
						|
	defer ticker.Stop()
 | 
						|
 | 
						|
	for range ticker.C {
 | 
						|
		poolsMu.Lock()
 | 
						|
		for _, st := range pools {
 | 
						|
			stats := st.Stats()
 | 
						|
			meter.DefaultMeter.Counter(semconv.PoolGetTotal, "capacity", strconv.Itoa(st.Cap())).Set(stats.Get)
 | 
						|
			meter.DefaultMeter.Counter(semconv.PoolPutTotal, "capacity", strconv.Itoa(st.Cap())).Set(stats.Put)
 | 
						|
			meter.DefaultMeter.Counter(semconv.PoolMisTotal, "capacity", strconv.Itoa(st.Cap())).Set(stats.Mis)
 | 
						|
			meter.DefaultMeter.Counter(semconv.PoolRetTotal, "capacity", strconv.Itoa(st.Cap())).Set(stats.Ret)
 | 
						|
		}
 | 
						|
		poolsMu.Unlock()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	_ Statser = (*BytePool)(nil)
 | 
						|
	_ Statser = (*BytesPool)(nil)
 | 
						|
	_ Statser = (*StringsPool)(nil)
 | 
						|
)
 | 
						|
 | 
						|
type Pool[T any] struct {
 | 
						|
	p *sync.Pool
 | 
						|
}
 | 
						|
 | 
						|
func (p Pool[T]) Put(t T) {
 | 
						|
	p.p.Put(t)
 | 
						|
}
 | 
						|
 | 
						|
func (p Pool[T]) Get() T {
 | 
						|
	return p.p.Get().(T)
 | 
						|
}
 | 
						|
 | 
						|
func NewPool[T any](fn func() T) Pool[T] {
 | 
						|
	return Pool[T]{
 | 
						|
		p: &sync.Pool{
 | 
						|
			New: func() interface{} {
 | 
						|
				return fn()
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type BytePool struct {
 | 
						|
	p   *sync.Pool
 | 
						|
	get uint64
 | 
						|
	put uint64
 | 
						|
	mis uint64
 | 
						|
	ret uint64
 | 
						|
	c   int
 | 
						|
}
 | 
						|
 | 
						|
func NewBytePool(size int) *BytePool {
 | 
						|
	p := &BytePool{c: size}
 | 
						|
	p.p = &sync.Pool{
 | 
						|
		New: func() interface{} {
 | 
						|
			atomic.AddUint64(&p.mis, 1)
 | 
						|
			b := make([]byte, 0, size)
 | 
						|
			return &b
 | 
						|
		},
 | 
						|
	}
 | 
						|
	poolsMu.Lock()
 | 
						|
	pools = append(pools, p)
 | 
						|
	poolsMu.Unlock()
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func (p *BytePool) Cap() int {
 | 
						|
	return p.c
 | 
						|
}
 | 
						|
 | 
						|
func (p *BytePool) Stats() Stats {
 | 
						|
	return Stats{
 | 
						|
		Put: atomic.LoadUint64(&p.put),
 | 
						|
		Get: atomic.LoadUint64(&p.get),
 | 
						|
		Mis: atomic.LoadUint64(&p.mis),
 | 
						|
		Ret: atomic.LoadUint64(&p.ret),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (p *BytePool) Get() *[]byte {
 | 
						|
	atomic.AddUint64(&p.get, 1)
 | 
						|
	return p.p.Get().(*[]byte)
 | 
						|
}
 | 
						|
 | 
						|
func (p *BytePool) Put(b *[]byte) {
 | 
						|
	atomic.AddUint64(&p.put, 1)
 | 
						|
	if cap(*b) > p.c {
 | 
						|
		atomic.AddUint64(&p.ret, 1)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	*b = (*b)[:0]
 | 
						|
	p.p.Put(b)
 | 
						|
}
 | 
						|
 | 
						|
type BytesPool struct {
 | 
						|
	p   *sync.Pool
 | 
						|
	get uint64
 | 
						|
	put uint64
 | 
						|
	mis uint64
 | 
						|
	ret uint64
 | 
						|
	c   int
 | 
						|
}
 | 
						|
 | 
						|
func NewBytesPool(size int) *BytesPool {
 | 
						|
	p := &BytesPool{c: size}
 | 
						|
	p.p = &sync.Pool{
 | 
						|
		New: func() interface{} {
 | 
						|
			atomic.AddUint64(&p.mis, 1)
 | 
						|
			b := bytes.NewBuffer(make([]byte, 0, size))
 | 
						|
			return b
 | 
						|
		},
 | 
						|
	}
 | 
						|
	poolsMu.Lock()
 | 
						|
	pools = append(pools, p)
 | 
						|
	poolsMu.Unlock()
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func (p *BytesPool) Cap() int {
 | 
						|
	return p.c
 | 
						|
}
 | 
						|
 | 
						|
func (p *BytesPool) Stats() Stats {
 | 
						|
	return Stats{
 | 
						|
		Put: atomic.LoadUint64(&p.put),
 | 
						|
		Get: atomic.LoadUint64(&p.get),
 | 
						|
		Mis: atomic.LoadUint64(&p.mis),
 | 
						|
		Ret: atomic.LoadUint64(&p.ret),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (p *BytesPool) Get() *bytes.Buffer {
 | 
						|
	return p.p.Get().(*bytes.Buffer)
 | 
						|
}
 | 
						|
 | 
						|
func (p *BytesPool) Put(b *bytes.Buffer) {
 | 
						|
	if (*b).Cap() > p.c {
 | 
						|
		atomic.AddUint64(&p.ret, 1)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	b.Reset()
 | 
						|
	p.p.Put(b)
 | 
						|
}
 | 
						|
 | 
						|
type StringsPool struct {
 | 
						|
	p   *sync.Pool
 | 
						|
	get uint64
 | 
						|
	put uint64
 | 
						|
	mis uint64
 | 
						|
	ret uint64
 | 
						|
	c   int
 | 
						|
}
 | 
						|
 | 
						|
func NewStringsPool(size int) *StringsPool {
 | 
						|
	p := &StringsPool{c: size}
 | 
						|
	p.p = &sync.Pool{
 | 
						|
		New: func() interface{} {
 | 
						|
			atomic.AddUint64(&p.mis, 1)
 | 
						|
			return &strings.Builder{}
 | 
						|
		},
 | 
						|
	}
 | 
						|
	poolsMu.Lock()
 | 
						|
	pools = append(pools, p)
 | 
						|
	poolsMu.Unlock()
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func (p *StringsPool) Cap() int {
 | 
						|
	return p.c
 | 
						|
}
 | 
						|
 | 
						|
func (p *StringsPool) Stats() Stats {
 | 
						|
	return Stats{
 | 
						|
		Put: atomic.LoadUint64(&p.put),
 | 
						|
		Get: atomic.LoadUint64(&p.get),
 | 
						|
		Mis: atomic.LoadUint64(&p.mis),
 | 
						|
		Ret: atomic.LoadUint64(&p.ret),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (p *StringsPool) Get() *strings.Builder {
 | 
						|
	atomic.AddUint64(&p.get, 1)
 | 
						|
	return p.p.Get().(*strings.Builder)
 | 
						|
}
 | 
						|
 | 
						|
func (p *StringsPool) Put(b *strings.Builder) {
 | 
						|
	atomic.AddUint64(&p.put, 1)
 | 
						|
	if b.Cap() > p.c {
 | 
						|
		atomic.AddUint64(&p.ret, 1)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	b.Reset()
 | 
						|
	p.p.Put(b)
 | 
						|
}
 |