Reuse Buffers with sync.Pool

Cut per-request allocations by borrowing bytes.Buffer from sync.Pool, copying the encoded JSON, and always returning the buffer.

GoResilience

Code

snippet
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"sync"
)

var bufferPool = sync.Pool{
	New: func() any { return new(bytes.Buffer) },
}

func EncodeJSON(value any) ([]byte, error) {
	buffer := bufferPool.Get().(*bytes.Buffer)
	buffer.Reset()
	defer bufferPool.Put(buffer)

	if err := json.NewEncoder(buffer).Encode(value); err != nil {
		return nil, err
	}
	out := make([]byte, buffer.Len())
	copy(out, buffer.Bytes())
	return out, nil
}

func main() {
	type payload struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	}
	encoded, err := EncodeJSON(payload{ID: 1, Name: "Ada"})
	if err != nil {
		panic(err)
	}
	fmt.Print(string(encoded))
	fmt.Println(bytes.Equal(encoded, []byte("{\"id\":1,\"name\":\"Ada\"}\n")))
}