Skip to content

tokens: eliminate commit-path cache barriers, struct copying, and unguarded log allocations - #2364

Open
SurbhiAgarwal1 wants to merge 1 commit into
LFDT-Panurus:mainfrom
SurbhiAgarwal1:perf/2188-commit-path-optimizations
Open

SurbhiAgarwal1 wants to merge 1 commit into
LFDT-Panurus:mainfrom
SurbhiAgarwal1:perf/2188-commit-path-optimizations

Conversation

@SurbhiAgarwal1

Copy link
Copy Markdown
Contributor

Summary

Optimizes performance and memory allocations on the transaction commit path in token/services/tokens and token/services/utils/cache:

  • Cache Barrier Removal (ristretto.go): Removed synchronous c.cache.Wait() calls from write paths (Add/Delete/Clear), unlocking asynchronous Ristretto cache throughput. Exposed explicit Wait() helper for testing.
  • Pointer Pass Optimization (storage.go & tokens.go): Changed AppendToken parameter to pointer (*TokenToAppend) and updated CacheEntry.ToAppend, getActions, extractActions, and Parse to pass []*TokenToAppend, eliminating 13-field struct copying (~200+ bytes per output).
  • Event Buffer Capacity Retention (storage.go): Reset event buffer via t.pending = t.pending[:0] in FlushEvents and Rollback to retain allocated backing array capacity across transactions.
  • Upfront Slice Preallocation (tokens.go): Preallocated capacities for toSpend, toAppend, and toDelete slices.
  • Stack Trace Overhead Elimination (tokens.go): Guarded debug.Stack() in Service.DeleteTokens behind logger.IsEnabledFor(zapcore.DebugLevel).
  • Zero-Allocation Variadic Logging (tokens.go & storage.go): Wrapped hot-path DebugfContext log calls with if logger.IsEnabledFor(zapcore.DebugLevel) to eliminate Go variadic []any heap boxing allocations when debug logging is disabled.

Fixes #2188

Copilot AI lite review requested due to automatic review settings September 13, 2026 16:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@AkramBitar AkramBitar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread token/services/ttx/session.go
Comment thread token/services/utils/cache/ristretto.go
Comment thread token/services/tokens/storage.go Outdated
Comment thread token/services/ttx/session.go
Comment thread token/services/ttx/session.go
Comment thread token/services/tokens/tokens.go
@SurbhiAgarwal1
SurbhiAgarwal1 force-pushed the perf/2188-commit-path-optimizations branch from 76ac39e to a914243 Compare September 21, 2026 14:05
@AkramBitar
AkramBitar force-pushed the perf/2188-commit-path-optimizations branch from a914243 to a39395e Compare September 22, 2026 15:06
@AkramBitar

Copy link
Copy Markdown
Contributor

Hello @SurbhiAgarwal1 ,

Any update with this PR?

Regards,
Akram

@SurbhiAgarwal1

Copy link
Copy Markdown
Contributor Author

Hi @AkramBitar,

Apologies for the delay!

All 6 review findings have been addressed in commit a39395e:

  1. Unblock peer in send() on Close(): Added peerClosedChan so closing one side immediately releases any peer goroutine blocked in send().
  2. Prevent silent message drops: send() now checks if the peer has closed and rejects the message with an error instead of silently buffering it into an abandoned channel.
  3. Deterministic send(): Added a non-blocking attempt before selecting on ctx.Done() to eliminate the race with an already-ready buffered channel.
  4. Read-your-write cache consistency: Restored synchronous Wait() calls in Add, Delete, and Clear in the Ristretto cache wrapper.
  5. Event buffer aliasing: Reset pending = nil in FlushEvents and Rollback to eliminate backing-array aliasing during synchronous event publishing.
  6. Caller attribution: Updated DeleteTokens to record the caller location via runtime.Caller(1) for consistent and accurate attribution.

The PR is ready for your review and approval!

@Effi-S Effi-S left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @SurbhiAgarwal1,
Thanks for the work here.
Please squash your commits to 1 and rebase to main

@SurbhiAgarwal1
SurbhiAgarwal1 force-pushed the perf/2188-commit-path-optimizations branch from a39395e to 3fe6194 Compare September 30, 2026 18:19

@Effi-S Effi-S left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some low findings:


// Step 3: Update tracked keys for next cycle
f.prevKeys = newKeys
f.cache.Wait()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low: f.cache.Wait() runs after a loop of Add/Delete that each already Wait() internally.

Comment thread token/services/tokens/tokens.go

// Goroutine 1: Continually calling Info() on left
wg.Go(func() {
for range 100 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@SurbhiAgarwal1
SurbhiAgarwal1 force-pushed the perf/2188-commit-path-optimizations branch from 3fe6194 to 4343c0c Compare October 2, 2026 09:55
@SurbhiAgarwal1

Copy link
Copy Markdown
Contributor Author

Thanks for the review, @Effi-S!

All review comments have been addressed:

  • Removed the redundant f.cache.Wait() barrier and Wait() from the tokenCache interface in fetcher.go.
  • Added if logger.IsEnabledFor(zapcore.DebugLevel) guards around all remaining DebugfContext calls in tokens.go (AppendValid, CacheRequest, getActions, extractActions) and storage.go.
  • Removed the truncated duplicate doc comment in session_test.go.
  • Squashed into a single commit with DCO sign-off and rebased onto the latest main.

@Effi-S
Effi-S self-requested a review October 5, 2026 07:00

@Effi-S Effi-S left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Add/Delete/Clear already call c.cache.Wait(). The new exported Wait() and the four c.Wait() test calls are no-ops.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants