Add memory optimization improvements for reduced GC pressure (v0.36.16)
Some checks failed
Go / build-and-release (push) Has been cancelled
Some checks failed
Go / build-and-release (push) Has been cancelled
- 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>
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"git.mleku.dev/mleku/nostr/encoders/tag"
|
||||
"git.mleku.dev/mleku/nostr/encoders/varint"
|
||||
"lol.mleku.dev/chk"
|
||||
"next.orly.dev/pkg/database/bufpool"
|
||||
)
|
||||
|
||||
// CompactEventFormat defines the binary format for compact event storage.
|
||||
@@ -72,7 +73,8 @@ type SerialResolver interface {
|
||||
// MarshalCompactEvent encodes an event using compact serial references.
|
||||
// The resolver is used to look up/create serial mappings for pubkeys and event IDs.
|
||||
func MarshalCompactEvent(ev *event.E, resolver SerialResolver) (data []byte, err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf := bufpool.GetMedium()
|
||||
defer bufpool.PutMedium(buf)
|
||||
|
||||
// Version byte
|
||||
buf.WriteByte(CompactFormatVersion)
|
||||
@@ -109,7 +111,8 @@ func MarshalCompactEvent(ev *event.E, resolver SerialResolver) (data []byte, err
|
||||
// Signature (64 bytes)
|
||||
buf.Write(ev.Sig)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
// Copy bytes before returning buffer to pool
|
||||
return bufpool.CopyBytes(buf), nil
|
||||
}
|
||||
|
||||
// encodeCompactTag encodes a single tag with serial references for e/p tags.
|
||||
@@ -221,8 +224,8 @@ func writeUint40(w io.Writer, value uint64) {
|
||||
|
||||
// readUint40 reads a 5-byte big-endian unsigned integer.
|
||||
func readUint40(r io.Reader) (value uint64, err error) {
|
||||
buf := make([]byte, 5)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
var buf [5]byte // Fixed array avoids heap escape
|
||||
if _, err = io.ReadFull(r, buf[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
value = (uint64(buf[0]) << 32) |
|
||||
@@ -331,9 +334,9 @@ func decodeCompactTag(r io.Reader, resolver SerialResolver) (t *tag.T, err error
|
||||
|
||||
// decodeTagElement decodes a single tag element from compact format.
|
||||
func decodeTagElement(r io.Reader, resolver SerialResolver) (elem []byte, err error) {
|
||||
// Read type flag
|
||||
typeBuf := make([]byte, 1)
|
||||
if _, err = io.ReadFull(r, typeBuf); err != nil {
|
||||
// Read type flag (fixed array avoids heap escape)
|
||||
var typeBuf [1]byte
|
||||
if _, err = io.ReadFull(r, typeBuf[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typeFlag := typeBuf[0]
|
||||
|
||||
Reference in New Issue
Block a user