tokens: eliminate commit-path cache barriers, struct copying, and unguarded log allocations - #2364
SurbhiAgarwal1 wants to merge 1 commit into
Conversation
07555dc to
2d0ca3c
Compare
AkramBitar
left a comment
There was a problem hiding this comment.
Review of 541179e8c..2d0ca3c53 (the localSession race fix + the tokens commit-path optimizations).
Verified before commenting: go build, go vet and go test are clean on all touched packages, plus -race -count=200 on the new session tests. I could not reproduce a close of closed channel / send on closed channel panic: writeChannel is closed only when closed && inFlight == 0, and inFlight is incremented under the same lock that rejects new sends, so that window is properly closed. Left and right also own disjoint write channels, so cross-side double-close is impossible.
The diff is largely sound. Six findings inline — one medium, the rest low. The medium (session.go Close() asymmetry) is worth addressing before merge; it leaks a goroutine in the same failure class #2285 set out to fix.
76ac39e to
a914243
Compare
a914243 to
a39395e
Compare
|
Hello @SurbhiAgarwal1 , Any update with this PR? Regards, |
|
Hi @AkramBitar, Apologies for the delay! All 6 review findings have been addressed in commit
The PR is ready for your review and approval! |
Effi-S
left a comment
There was a problem hiding this comment.
Hi @SurbhiAgarwal1,
Thanks for the work here.
Please squash your commits to 1 and rebase to main
a39395e to
3fe6194
Compare
|
|
||
| // Step 3: Update tracked keys for next cycle | ||
| f.prevKeys = newKeys | ||
| f.cache.Wait() |
There was a problem hiding this comment.
Low: f.cache.Wait() runs after a loop of Add/Delete that each already Wait() internally.
|
|
||
| // Goroutine 1: Continually calling Info() on left | ||
| wg.Go(func() { | ||
| for range 100 { |
There was a problem hiding this comment.
Nit: Duplicated/contradictory doc comment lines for TestLocalBidirectionalChannel_CloseReleasesPeerReceive (first line is truncated and unfinished).
Optimize performance and memory allocations on the transaction commit path in token/services/tokens and token/services/utils/cache, and resolve a data race in localSession under token/services/ttx: Commit-path optimizations: - Pass TokenToAppend by pointer (*TokenToAppend) to eliminate struct copying (~200+ bytes per output). - Retain event buffer slice capacity across transactions via reset in FlushEvents and Rollback. - Preallocate slice capacities for toSpend, toAppend, and toDelete. - Guard debug.Stack() and record caller location via runtime.Caller(1) in DeleteTokens. - Wrap hot-path DebugfContext calls with logger level checks to eliminate variadic heap allocations. - Expose explicit Wait() helper on Ristretto cache wrapper. localSession synchronization and unblocking: - Fix data race on localSession Closed state under concurrent self-auditing flows. - Unblock peer goroutines blocked in send() and receive() upon session Close(). - Prevent silent message drops by rejecting sends after peer session is closed. - Ensure deterministic non-blocking send prior to context cancellation check. - Add comprehensive race and session lifecycle tests. Fixes LFDT-Panurus#2188 Fixes LFDT-Panurus#2285 Signed-off-by: Surbhi Agarwal <SurbhiAgarwal1@users.noreply.github.com>
3fe6194 to
4343c0c
Compare
|
Thanks for the review, @Effi-S! All review comments have been addressed:
|
There was a problem hiding this comment.
@SurbhiAgarwal1, thanks for your work here.
Some minor findings:
| s.inFlight-- | ||
| if s.inFlight == 0 && s.closed && !s.writeClosed { | ||
| s.writeClosed = true | ||
| close(s.writeChannel) |
There was a problem hiding this comment.
Medium:
Concurrent Send()/Close(): the writeChannel <- msg arm buffers the message, but the inner select then sees closedChan/peerClosedChan closed and returns "session is closed". Caller aborts while the peer still receives the message , one-sided state. Affects both fast-path and blocking select.
| return nil | ||
| } | ||
|
|
||
| return s.readChannel |
There was a problem hiding this comment.
Medium:
readChannel is now closed on peer close, so <-Receive() returns nil / for range ends. In-repo consumers handle nil, but external view.Session consumers looping without a nil check busy-spin.
| // DeleteTokens marks the tokens as spent in the database, attributed to the caller's location. | ||
| func (t *Service) DeleteTokens(ctx context.Context, ids ...*token2.ID) (err error) { | ||
| return t.DeleteTokensBy(ctx, string(debug.Stack()), ids...) | ||
| deletedBy := "Service.DeleteTokens" |
There was a problem hiding this comment.
Low deletedBy records only the direct caller (deleteTokens), losing the originating call chain. Fallback label no longer identifies the requester.
|
|
||
| // Wait blocks until all buffered writes and deletes are applied to the cache. | ||
| // Useful for testing and deterministic cache state assertions. | ||
| func (c *ristrettoCache[T]) Wait() { |
There was a problem hiding this comment.
Nit: Add/Delete/Clear already call c.cache.Wait(). The new exported Wait() and the four c.Wait() test calls are no-ops.
Summary
Optimizes performance and memory allocations on the transaction commit path in
token/services/tokensandtoken/services/utils/cache:ristretto.go): Removed synchronousc.cache.Wait()calls from write paths (Add/Delete/Clear), unlocking asynchronous Ristretto cache throughput. Exposed explicitWait()helper for testing.storage.go&tokens.go): ChangedAppendTokenparameter to pointer (*TokenToAppend) and updatedCacheEntry.ToAppend,getActions,extractActions, andParseto pass[]*TokenToAppend, eliminating 13-field struct copying (~200+ bytes per output).storage.go): Reset event buffer viat.pending = t.pending[:0]inFlushEventsandRollbackto retain allocated backing array capacity across transactions.tokens.go): Preallocated capacities fortoSpend,toAppend, andtoDeleteslices.tokens.go): Guardeddebug.Stack()inService.DeleteTokensbehindlogger.IsEnabledFor(zapcore.DebugLevel).tokens.go&storage.go): Wrapped hot-pathDebugfContextlog calls withif logger.IsEnabledFor(zapcore.DebugLevel)to eliminate Go variadic[]anyheap boxing allocations when debug logging is disabled.Fixes #2188