Documentation
¶
Overview ¶
Package scratch provides shared, size-bounded recycling primitives for the service codecs. Every codec that encodes into a transient *bytes.Buffer previously declared its own sync.Pool, its own 256 KiB cap-discard constant, and its own release helper. Centralising them here gives a single source of truth for the cap-discard policy and a single shared pool whose reuse rate is higher than nine independent pools (sync.Pool is internally per-P sharded, so consolidation does not add contention).
The pooling MECHANISM lives in internal/kernel/concur/recycler (ADR 0010); scratch is a codec-domain consumer that keeps the 256 KiB threshold and the concrete *bytes.Buffer / *bytes.Reader types here.
Lifetime contract: a value returned by an Acquire* call is owned by the caller until the matching Release* call. After Release the value — and any slice aliasing it (e.g. buf.Bytes()) — MUST NOT be used; clone the encoded bytes first if they need to outlive the release.
Index ¶
- Constants
- func AcquireBuffer() *bytes.Buffer
- func AcquireBufferSized(hint int) *bytes.Buffer
- func AcquireReader(src []byte) *bytes.Reader
- func DetachBuffer(buf *bytes.Buffer) []byte
- func ReleaseAppendWriter(w *AppendWriter)
- func ReleaseBuffer(buf *bytes.Buffer)
- func ReleaseBufferSized(buf *bytes.Buffer)
- func ReleaseReader(r *bytes.Reader)
- type AppendWriter
Constants ¶
const MaxRetainedBufBytes int = 256 << 10
MaxRetainedBufBytes is the project-wide cap-discard threshold for pooled codec buffers. A buffer whose capacity exceeds this is dropped on release rather than re-pooled, so a one-off oversized payload cannot pin a large allocation for the lifetime of the pool's GC window. It bounds the shared *bytes.Buffer pool below, and it is also the ceiling a codec passes to a recycler.CappedPool of its own when it pools a buffer shape this package does not offer — so the codec domain keeps one threshold, not one per codec.
const MaxRetainedSizedBytes int = 4 << 20
MaxRetainedSizedBytes is the capacity past which the size-classed tier drops a buffer: 4 MiB. Between MaxRetainedBufBytes and it, a released buffer joins the class its capacity falls in — up to 1 MiB, up to 4 MiB — so a workload of documents past 256 KiB reuses its buffers instead of growing one on every call; past it, one oversized payload still cannot pin its allocation.
Variables ¶
This section is empty.
Functions ¶
func AcquireBuffer ¶
AcquireBuffer returns a clean, zero-length *bytes.Buffer from the shared pool. The recycler reset the buffer on its previous Put, so callers append directly without re-resetting. The caller owns it until ReleaseBuffer.
func AcquireBufferSized ¶ added in v0.27.0
AcquireBufferSized returns a clean, zero-length *bytes.Buffer with room for at least hint bytes: from the shared pool when hint is within MaxRetainedBufBytes, else from the size class hint falls in — up to 1 MiB, up to 4 MiB — whose new buffers are allocated at the class's full size, so one serves every hint of its class. A codec that knows, or has seen, the size of what it encodes passes it, and a document past 256 KiB is encoded into a recycled buffer instead of a growth chain. The caller owns it until ReleaseBufferSized, which returns it to its class.
func AcquireReader ¶
AcquireReader returns a *bytes.Reader from the shared pool, reset to read from src. The caller owns it until ReleaseReader; src must stay alive and unmodified for as long as the reader is used.
func DetachBuffer ¶
DetachBuffer hands the caller the bytes encoded into buf and ends the caller's ownership of buf, by the cheaper of two releases for its size. A buffer within MaxRetainedBufBytes is cloned and repooled: the clone is small, and the next encode reuses the buffer. An over-cap buffer would pay a full copy of a large payload, so it is orphaned instead — the size-classed tier is for codecs that ask AcquireBufferSized for their buffer and release it with ReleaseBufferSized: the returned slice IS its storage, now the caller's, and buf is left for the GC without a reset — a reset would not zero the bytes, but a later write through buf would overwrite them. Either way, buf must not be used after the call. A nil buf yields nil.
func ReleaseAppendWriter ¶ added in v0.27.0
func ReleaseAppendWriter(w *AppendWriter)
ReleaseAppendWriter returns w to the shared pool. The writer forgets the slice it held, so an idle writer keeps no caller's buffer alive. Callers MUST NOT use w after this call; the slice AppendWriter.Bytes returned stays theirs. A nil w is a no-op.
func ReleaseBuffer ¶
ReleaseBuffer returns buf to the shared pool unless its capacity exceeds MaxRetainedBufBytes, in which case the recycler orphans it for the GC. Callers MUST NOT use buf — or any slice aliasing buf.Bytes() — after this call; clone first if the encoded bytes must outlive the release.
func ReleaseBufferSized ¶ added in v0.27.0
ReleaseBufferSized returns buf to the pool its capacity belongs to: the shared pool within MaxRetainedBufBytes, the size class past it, and none past MaxRetainedSizedBytes, where the recycler orphans it for the GC. It is the release of a buffer AcquireBufferSized handed out — or of any buffer a codec grew past the shared pool's cap and means to reuse. Callers MUST NOT use buf, or any slice aliasing buf.Bytes(), after this call.
func ReleaseReader ¶
ReleaseReader returns r to the shared pool. A bytes.Reader is fixed-size, so there is no cap-discard. Callers MUST NOT use r after this call.
Types ¶
type AppendWriter ¶ added in v0.27.0
type AppendWriter struct {
// contains filtered or unexported fields
}
AppendWriter is an io.Writer over a caller's slice: each Write appends to it, growing it as append grows a slice, and AppendWriter.Bytes returns the slice so far. AppendWriter.Truncate cuts it back to the length it had when the writer was acquired, so an encode that fails half-way leaves the caller's bytes as they were — the codec.Appender contract. It never fails a Write. Acquire one with AcquireAppendWriter for one encode, or keep one from NewAppendWriter; the zero AppendWriter appends to a nil slice.
func AcquireAppendWriter ¶ added in v0.27.0
func AcquireAppendWriter(dst []byte) *AppendWriter
AcquireAppendWriter returns an AppendWriter from the shared pool that appends to dst in place: what an encoder writes through it lands after dst's last byte, in dst's spare capacity while it lasts. It is how a codec's Append hands a caller's buffer to a standard-library encoder that takes an io.Writer, instead of encoding into a pooled buffer and copying the result onto dst. The caller owns the writer until ReleaseAppendWriter.
func NewAppendWriter ¶ added in v0.27.0
func NewAppendWriter(dst []byte) *AppendWriter
NewAppendWriter returns an AppendWriter appending to dst that is the caller's to keep, outside the pool: for a writer that lives as long as its owner — a stream encoder writing every record through the same one — where AcquireAppendWriter is for one encode.
func (*AppendWriter) Bytes ¶ added in v0.27.0
func (w *AppendWriter) Bytes() []byte
Bytes returns the slice so far: the bytes the writer was acquired with, then everything written since. It may be a new array when a Write outgrew the capacity of the one before.
func (*AppendWriter) Truncate ¶ added in v0.27.0
func (w *AppendWriter) Truncate() []byte
Truncate drops everything written since the writer was acquired and returns the slice cut back to its length then, so a failed encode hands the caller back the bytes it gave, unchanged.