From de4b4d72581aafc20aa56a19ca4fab1f4ab1589f Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Mon, 19 Jan 2026 17:52:31 +0000 Subject: [PATCH] fix(cache): remove sync.Pool for Yaegi compatibility (#121) - [x] Remove sync.Pool implementation that causes reflection panics - [x] Replace pool-based NewRESPWriter with direct instantiation - [x] Replace pool-based NewRESPReader with direct instantiation - [x] Convert Release() methods to no-ops for API compatibility - [x] Add documentation explaining sync.Pool removal for Yaegi - [x] Remove "sync" import Resolves #120 --- internal/cache/backends/resp.go | 49 ++++++++++----------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/internal/cache/backends/resp.go b/internal/cache/backends/resp.go index 2500acd..3b7fde7 100644 --- a/internal/cache/backends/resp.go +++ b/internal/cache/backends/resp.go @@ -7,52 +7,34 @@ import ( "io" "strconv" "strings" - "sync" ) // RESP (REdis Serialization Protocol) implementation // Pure Go implementation compatible with Yaegi interpreter (no unsafe package) +// +// NOTE: sync.Pool was intentionally removed for Yaegi compatibility. +// Yaegi (Traefik's Go interpreter) has issues with sync.Pool and reflection +// that cause "reflect: call of reflect.Value.Field on zero Value" panics. +// See: https://github.com/lukaszraczylo/traefikoidc/issues/120 var ( ErrInvalidRESP = errors.New("invalid RESP response") ErrNilResponse = errors.New("nil response") ) -// Object pools for memory optimization - reduces allocations by 50-70% -var ( - readerPool = sync.Pool{ - New: func() interface{} { - return &RESPReader{ - r: bufio.NewReaderSize(nil, 4096), - } - }, - } - - writerPool = sync.Pool{ - New: func() interface{} { - return &RESPWriter{ - w: nil, - } - }, - } -) - // RESPWriter writes RESP protocol messages type RESPWriter struct { w io.Writer } -// NewRESPWriter creates a new RESP writer from the pool (memory optimized) +// NewRESPWriter creates a new RESP writer func NewRESPWriter(w io.Writer) *RESPWriter { - writer, _ := writerPool.Get().(*RESPWriter) - writer.w = w - return writer + return &RESPWriter{w: w} } -// Release returns the writer to the pool for reuse +// Release is a no-op for API compatibility (pooling removed for Yaegi compatibility) func (w *RESPWriter) Release() { - w.w = nil - writerPool.Put(w) + // No-op: pooling removed for Yaegi compatibility } // WriteCommand writes a Redis command in RESP array format @@ -78,17 +60,16 @@ type RESPReader struct { r *bufio.Reader } -// NewRESPReader creates a new RESP reader from the pool (memory optimized) +// NewRESPReader creates a new RESP reader func NewRESPReader(r io.Reader) *RESPReader { - reader, _ := readerPool.Get().(*RESPReader) - reader.r.Reset(r) - return reader + return &RESPReader{ + r: bufio.NewReaderSize(r, 4096), + } } -// Release returns the reader to the pool for reuse +// Release is a no-op for API compatibility (pooling removed for Yaegi compatibility) func (r *RESPReader) Release() { - r.r.Reset(nil) - readerPool.Put(r) + // No-op: pooling removed for Yaegi compatibility } // ReadResponse reads a RESP response and returns the parsed value