From 2dd119401bb9951f5612201bfac3b913e2ee9135 Mon Sep 17 00:00:00 2001 From: mleku Date: Thu, 11 Sep 2025 17:14:50 +0100 Subject: [PATCH] Improve memory safety with `defer` for event cleanup across handlers, optimize conditional logging, and bump version to `v0.4.3`. --- app/handle-req.go | 85 ++++++++++++++++++++++--------- pkg/database/export.go | 26 ++++++---- pkg/encoders/event/binary_test.go | 3 ++ pkg/version/version | 2 +- 4 files changed, 79 insertions(+), 37 deletions(-) diff --git a/app/handle-req.go b/app/handle-req.go index de5aa3f..8c92996 100644 --- a/app/handle-req.go +++ b/app/handle-req.go @@ -114,33 +114,56 @@ func (l *Listener) HandleReq(msg []byte) (err error) { continue } } - // Use a separate context for QueryEvents to prevent cancellation issues - queryCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - log.T.F("HandleReq: About to QueryEvents for %s, main context done: %v", l.remote, l.ctx.Err() != nil) - if events, err = l.QueryEvents(queryCtx, f); chk.E(err) { - if errors.Is(err, badger.ErrDBClosed) { - return - } - log.T.F("HandleReq: QueryEvents error for %s: %v", l.remote, err) - err = nil - } - log.T.F("HandleReq: QueryEvents completed for %s, found %d events", l.remote, len(events)) + // Use a separate context for QueryEvents to prevent cancellation issues + queryCtx, cancel := context.WithTimeout( + context.Background(), 30*time.Second, + ) + defer cancel() + log.T.F( + "HandleReq: About to QueryEvents for %s, main context done: %v", + l.remote, l.ctx.Err() != nil, + ) + if events, err = l.QueryEvents(queryCtx, f); chk.E(err) { + if errors.Is(err, badger.ErrDBClosed) { + return + } + log.T.F("HandleReq: QueryEvents error for %s: %v", l.remote, err) + err = nil + } + defer func() { + for _, ev := range events { + ev.Free() + } + }() + log.T.F( + "HandleReq: QueryEvents completed for %s, found %d events", + l.remote, len(events), + ) } var tmp event.S privCheck: for _, ev := range events { if kind.IsPrivileged(ev.Kind) && accessLevel != "admin" { // admins can see all events - log.I.F("checking privileged event %s", ev.ID) + log.T.C( + func() string { + return fmt.Sprintf( + "checking privileged event %0x", ev.ID, + ) + }, + ) pk := l.authedPubkey.Load() if pk == nil { continue } if utils.FastEqual(ev.Pubkey, pk) { - log.I.F( - "privileged event %s is for logged in pubkey %0x", ev.ID, - pk, + log.T.C( + func() string { + return fmt.Sprintf( + "privileged event %s is for logged in pubkey %0x", + ev.ID, pk, + ) + }, ) tmp = append(tmp, ev) continue @@ -152,17 +175,25 @@ privCheck: continue } if utils.FastEqual(pt, pk) { - log.I.F( - "privileged event %s is for logged in pubkey %0x", - ev.ID, pk, + log.T.C( + func() string { + return fmt.Sprintf( + "privileged event %s is for logged in pubkey %0x", + ev.ID, pk, + ) + }, ) tmp = append(tmp, ev) continue privCheck } } - log.W.F( - "privileged event %s does not contain the logged in pubkey %0x", - ev.ID, pk, + log.T.C( + func() string { + return fmt.Sprintf( + "privileged event %s does not contain the logged in pubkey %0x", + ev.ID, pk, + ) + }, ) } else { tmp = append(tmp, ev) @@ -171,9 +202,13 @@ privCheck: events = tmp seen := make(map[string]struct{}) for _, ev := range events { - log.T.F( - "REQ %s: sending EVENT id=%s kind=%d", env.Subscription, - hex.Enc(ev.ID), ev.Kind, + log.D.C( + func() string { + return fmt.Sprintf( + "REQ %s: sending EVENT id=%s kind=%d", env.Subscription, + hex.Enc(ev.ID), ev.Kind, + ) + }, ) log.T.C( func() string { diff --git a/pkg/database/export.go b/pkg/database/export.go index c2576b8..fb31b67 100644 --- a/pkg/database/export.go +++ b/pkg/database/export.go @@ -43,14 +43,16 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) { continue } // Serialize the event to JSON and write it to the output + defer func(ev *event.E) { + ev.Free() + evBuf.Reset() + }(ev) if _, err = w.Write(ev.Serialize()); chk.E(err) { return } if _, err = w.Write([]byte{'\n'}); chk.E(err) { return } - ev.Free() - evBuf.Reset() } return }, @@ -87,15 +89,17 @@ func (d *D) Export(c context.Context, w io.Writer, pubkeys ...[]byte) { if err = ev.UnmarshalBinary(evBuf); chk.E(err) { continue } - // Serialize the event to JSON and write it to the output - if _, err = w.Write(ev.Serialize()); chk.E(err) { - continue - } - if _, err = w.Write([]byte{'\n'}); chk.E(err) { - continue - } - ev.Free() - evBuf.Reset() + // Serialize the event to JSON and write it to the output + defer func(ev *event.E) { + ev.Free() + evBuf.Reset() + }(ev) + if _, err = w.Write(ev.Serialize()); chk.E(err) { + continue + } + if _, err = w.Write([]byte{'\n'}); chk.E(err) { + continue + } } return }, diff --git a/pkg/encoders/event/binary_test.go b/pkg/encoders/event/binary_test.go index 1d68c41..989be8f 100644 --- a/pkg/encoders/event/binary_test.go +++ b/pkg/encoders/event/binary_test.go @@ -48,6 +48,9 @@ func TestTMarshalBinary_UnmarshalBinary(t *testing.T) { // Marshal unmarshaled binary event back to JSON unmarshaledJSON := eb.Serialize() + defer func(ev *E) { + eb.Free() + }(eb) // Compare the two JSON representations if !utils.FastEqual(b, unmarshaledJSON) { diff --git a/pkg/version/version b/pkg/version/version index 6466da5..b37392d 100644 --- a/pkg/version/version +++ b/pkg/version/version @@ -1 +1 @@ -v0.4.2 \ No newline at end of file +v0.4.3 \ No newline at end of file