Files
next.orly.dev/pkg/database/bufpool/pool.go
mleku eddd05eabf
Some checks failed
Go / build-and-release (push) Has been cancelled
Add memory optimization improvements for reduced GC pressure (v0.36.16)
- Add buffer pool (pkg/database/bufpool) with SmallPool (64B) and MediumPool (1KB)
  for reusing bytes.Buffer instances on hot paths
- Fix escape analysis in index types (uint40, letter, word) by using fixed-size
  arrays instead of make() calls that escape to heap
- Add handler concurrency limiter (ORLY_MAX_HANDLERS_PER_CONN, default 100) to
  prevent unbounded goroutine growth under WebSocket load
- Add pre-allocation hints to Uint40s.Union/Intersection/Difference methods
- Update compact_event.go, save-event.go, serial_cache.go, and
  get-indexes-for-event.go to use pooled buffers

Files modified:
- app/config/config.go: Add MaxHandlersPerConnection config
- app/handle-websocket.go: Initialize handler semaphore
- app/listener.go: Add semaphore acquire/release in messageProcessor
- pkg/database/bufpool/pool.go: New buffer pool package
- pkg/database/compact_event.go: Use buffer pool, fix escape analysis
- pkg/database/get-indexes-for-event.go: Reuse single buffer for all indexes
- pkg/database/indexes/types/letter.go: Fixed array in UnmarshalRead
- pkg/database/indexes/types/uint40.go: Fixed arrays, pre-allocation hints
- pkg/database/indexes/types/word.go: Fixed array in UnmarshalRead
- pkg/database/save-event.go: Use buffer pool for key encoding
- pkg/database/serial_cache.go: Use buffer pool for lookups

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 06:03:53 +01:00

95 lines
2.3 KiB
Go

//go:build !(js && wasm)
// Package bufpool provides buffer pools for reducing GC pressure in hot paths.
//
// Two pool sizes are provided:
// - SmallPool (64 bytes): For index keys, serial encoding, short buffers
// - MediumPool (1KB): For event encoding, larger serialization buffers
//
// Usage:
//
// buf := bufpool.GetSmall()
// defer bufpool.PutSmall(buf)
// // Use buf...
// // IMPORTANT: Copy buf.Bytes() before Put if data is needed after
package bufpool
import (
"bytes"
"sync"
)
const (
// SmallBufferSize for index keys (8-64 bytes typical)
SmallBufferSize = 64
// MediumBufferSize for event encoding (300-1000 bytes typical)
MediumBufferSize = 1024
)
var (
// smallPool for index keys and short encodings
smallPool = sync.Pool{
New: func() interface{} {
return bytes.NewBuffer(make([]byte, 0, SmallBufferSize))
},
}
// mediumPool for event encoding and larger buffers
mediumPool = sync.Pool{
New: func() interface{} {
return bytes.NewBuffer(make([]byte, 0, MediumBufferSize))
},
}
)
// GetSmall returns a small buffer (64 bytes) from the pool.
// Call PutSmall when done to return it to the pool.
//
// WARNING: Copy buf.Bytes() before calling PutSmall if the data
// is needed after the buffer is returned to the pool.
func GetSmall() *bytes.Buffer {
return smallPool.Get().(*bytes.Buffer)
}
// PutSmall returns a small buffer to the pool.
// The buffer is reset before being returned.
func PutSmall(buf *bytes.Buffer) {
if buf == nil {
return
}
buf.Reset()
smallPool.Put(buf)
}
// GetMedium returns a medium buffer (1KB) from the pool.
// Call PutMedium when done to return it to the pool.
//
// WARNING: Copy buf.Bytes() before calling PutMedium if the data
// is needed after the buffer is returned to the pool.
func GetMedium() *bytes.Buffer {
return mediumPool.Get().(*bytes.Buffer)
}
// PutMedium returns a medium buffer to the pool.
// The buffer is reset before being returned.
func PutMedium(buf *bytes.Buffer) {
if buf == nil {
return
}
buf.Reset()
mediumPool.Put(buf)
}
// CopyBytes copies the buffer contents to a new slice.
// Use this before returning the buffer to the pool if the
// data needs to persist.
func CopyBytes(buf *bytes.Buffer) []byte {
if buf == nil || buf.Len() == 0 {
return nil
}
result := make([]byte, buf.Len())
copy(result, buf.Bytes())
return result
}