Skip to content

fix: app session update notification#398

Merged
dimast-x merged 6 commits into
mainfrom
fix/asu-notif
Oct 27, 2025
Merged

fix: app session update notification#398
dimast-x merged 6 commits into
mainfrom
fix/asu-notif

Conversation

@dimast-x
Copy link
Copy Markdown
Contributor

@dimast-x dimast-x commented Oct 22, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Session state is reloaded from storage after updates; reload failures now return an error.
    • Zero-value allocations no longer cause errors when referencing unknown assets.
  • Notifications

    • Update notifications now use a nested RPC-style payload delivering app_session and allocation data.
  • API

    • SubmitAppState accepts an optional SessionData payload included in notifications.
  • Tests

    • Expanded tests validate notification content, timestamps, totals, and SessionData.

@dimast-x dimast-x requested a review from a team as a code owner October 22, 2025 09:44
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @dimast-x, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the reliability of app session update notifications by introducing a mechanism to fetch the latest session state from the database before sending notifications. This prevents potential issues where notifications might be sent with stale data. In cases where the database reload fails, the system gracefully falls back to using the recently updated in-memory session. The PR also incorporates routine updates to Go module dependencies.

Highlights

  • App Session Notification Reliability: Ensures that AppSessionNotification uses the most current session data by attempting to reload it from the database after an update.
  • Robust Error Handling: Implements a fallback mechanism where the cached session is used if reloading the session from the database fails, preventing notification failures due to transient database issues.
  • Dependency Updates: Includes updates to several golang.org/x module dependencies in go.work.sum.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 22, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

SubmitAppState now reloads the saved AppSession from the database and uses the reloaded record for notifications/responses; getAppSession omits the status filter when status is empty; notifications now emit rpc.AppSession/rpc.AppAllocation shapes; tests added to assert SessionData and zero-allocation behavior.

Changes

Cohort / File(s) Change Summary
Service: reload after save
clearnode/app_session_service.go
After updater.Update and save, the service reloads the AppSession by ID and replaces updatedAppSession with the reloaded value; on reload failure it returns an RPC error; subsequent notification/response use the reloaded session.
Query: conditional status filter
clearnode/app_session.go
getAppSession builds the DB query to always filter by session_id and only add a status filter when status is non-empty; ordering by nonce DESC remains.
Notifications: RPC types
clearnode/notification.go
Notification payload construction switched to use rpc.AppSession and rpc.AppAllocation, wrapped in rpc.AppSessionUpdateNotification; Protocol and allocations are emitted using RPC types.
Validation: allocation check relaxed for zeros
clearnode/rpc_router_private.go
verifyAllocations now checks allocation sums per asset and only errors for unknown assets when the allocSum is non-zero (zero allocs for unknown assets are allowed).
Logic: accumulate zero allocations
clearnode/app_session_service.go
Accumulation loop now always adds alloc.Amount into the per-asset sum (removed conditional skip for zero amounts).
Tests: session data & zero-allocation scenarios
clearnode/app_session_service_test.go
Added tests (e.g., NitroRPCv0.4_Operate_ZeroAllocations_Success) and extended deposit tests to pass SubmitAppStateParams.SessionData, validate AppSessionUpdateNotification fields, RFC3339 timestamps, participant allocations and totals.
SDK: nested app_session parsing
sdk/src/rpc/parse/app.ts
Parser updated to expect a nested app_session object and to source all AppSession fields (id, status, sessionData, protocol, allocations, timestamps, etc.) from raw.app_session instead of top-level fields.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Service as AppSessionService
    participant DB as Database
    participant Notif as Notifier

    Client->>Service: SubmitAppState(params)
    Service->>DB: apply updates & save (updater.Update)
    DB-->>Service: saved (updatedAppSession)
    Service->>DB: reload AppSession by ID
    alt Reload successful
        DB-->>Service: reloaded AppSession
        Service->>Notif: emit AppSessionUpdate (rpc.AppSession + rpc.AppAllocation)
        Service-->>Client: success (reloaded AppSession)
    else Reload fails
        DB--xService: reload error
        Service-->>Client: RPC error (no fallback)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • nksazonov
  • philanton

Poem

🐰 I hopped to write the session, then double-checked the tree,

I fetched the thing I'd saved to make sure it matched me.
If the reload sings true, I nibble on a treat,
If it stumbles, I thump — then tidy up the beat. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix: app session update notification" clearly refers to a significant and real change in the pull request. The core modification in notification.go restructures how app session update notifications are constructed by replacing inline data structures with dedicated RPC types (rpc.AppSession and rpc.AppSessionUpdateNotification). While the PR scope extends beyond notification changes to include database reloading logic, query building adjustments, and allocation validation updates, the notification refactoring represents a substantial and central focus of the changeset. The title is specific and descriptive rather than vague, making the primary concern immediately clear to reviewers scanning the commit history.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/asu-notif

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6ee55d and da197cb.

📒 Files selected for processing (3)
  • clearnode/app_session_service.go (2 hunks)
  • clearnode/app_session_service_test.go (3 hunks)
  • clearnode/rpc_router_private.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
clearnode/app_session_service.go (1)
clearnode/rpc.go (1)
  • RPCErrorf (147-151)
clearnode/rpc_router_private.go (1)
clearnode/rpc.go (1)
  • RPCErrorf (147-151)
clearnode/app_session_service_test.go (4)
clearnode/pkg/rpc/api.go (8)
  • VersionNitroRPCv0_4 (33-33)
  • AppSessionIntentOperate (598-598)
  • Version (27-27)
  • AppAllocation (584-591)
  • AppSessionIntentDeposit (600-600)
  • AppSessionUpdateNotification (446-450)
  • AppSession (556-581)
  • ChannelStatusOpen (644-644)
clearnode/ledger.go (2)
  • NewAccountID (43-49)
  • GetWalletLedger (55-57)
clearnode/rpc_router_private.go (2)
  • SubmitAppStateParams (42-48)
  • AppAllocation (56-60)
clearnode/notification.go (1)
  • AppSessionUpdateEventType (47-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Publish (Clearnode)
  • GitHub Check: Test (Integration) / Test Integration
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
clearnode/app_session_service.go (2)

269-269: LGTM: Unconditional allocation sum accumulation aligns with updated validation.

The change to always accumulate alloc.Amount (including zero values) correctly supports the updated verifyAllocations logic in clearnode/rpc_router_private.go (lines 650-655), which now distinguishes between zero and non-zero allocations for unknown assets.


461-465: The review comment is incorrect; the reload is necessary.

The reload at lines 461-465 serves a legitimate purpose. GORM v2 does not automatically refetch database DEFAULT values after Save(). Since the AppSession table has created_at and updated_at columns with DEFAULT CURRENT_TIMESTAMP, GORM's Save() operation will not populate the struct's CreatedAt and UpdatedAt fields with the actual database-persisted values. The reload ensures consistency between the application state and what's stored in the database.

Likely an incorrect or invalid review comment.

clearnode/rpc_router_private.go (1)

650-655: LGTM: Zero-allocation validation refinement is correct.

The updated logic correctly distinguishes between zero and non-zero allocations for unknown assets. Zero allocations are harmless and can be safely ignored, while non-zero allocations for unknown assets are still properly caught and rejected.

clearnode/app_session_service_test.go (3)

390-422: LGTM: Zero-allocation test provides essential coverage.

This test case validates that the operate intent correctly handles zero allocations for all participants, ensuring the updated validation logic (lines 650-655 in clearnode/rpc_router_private.go) works as expected.


449-449: LGTM: SessionData test addition validates state persistence.

The test properly validates that the SessionData field is persisted and propagated through the update flow.

Also applies to: 454-454


476-515: LGTM: Comprehensive notification verification ensures data integrity.

This thorough validation of the AppSessionUpdateNotification content ensures that all fields are correctly populated, timestamps are properly formatted, and allocations are accurate. The assertions cover both structural correctness and business logic validation.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Oct 22, 2025

Codecov Report

❌ Patch coverage is 78.26087% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
clearnode/app_session_service.go 60.00% 1 Missing and 1 partial ⚠️
clearnode/rpc_router_private.go 33.33% 1 Missing and 1 partial ⚠️
clearnode/app_session.go 75.00% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request aims to fix an issue with app session update notifications by reloading the session from the database after the update transaction. However, the implementation has a bug where the session is reloaded with an incorrect status parameter, causing the reload to always fail and fall back to the previous behavior while logging an error. I've provided a critical comment to fix this. The dependency updates in go.work.sum look standard.

Comment thread clearnode/app_session_service.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16d769c and 0bd578a.

⛔ Files ignored due to path filters (1)
  • go.work.sum is excluded by !**/*.sum
📒 Files selected for processing (1)
  • clearnode/app_session_service.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
clearnode/app_session_service.go (4)
clearnode/log.go (1)
  • LoggerFromContext (95-100)
clearnode/notification.go (2)
  • NewBalanceNotification (54-61)
  • NewAppSessionNotification (95-122)
clearnode/ledger.go (1)
  • NewAccountID (43-49)
clearnode/rpc_router_private.go (1)
  • AppSessionResponse (62-75)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Publish (Clearnode)
  • GitHub Check: Test (Integration) / Test Integration
  • GitHub Check: Analyze (go)
🔇 Additional comments (1)
clearnode/app_session_service.go (1)

482-489: Verify: Using reloaded session may cause notifications with inconsistent state.

Following from the race condition concern above, when appSession contains data from a concurrent update (not this operation), the participant allocations retrieved here and the notifications sent will reflect someone else's changes rather than the changes made by this operation.

Confirm whether concurrent updates to the same app session are possible in your system. If they are, the current implementation may send incorrect notifications to participants.

Comment thread clearnode/app_session_service.go Outdated
Comment thread clearnode/app_session_service.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
clearnode/app_session.go (1)

34-37: Consider adding a documentation comment for the conditional status filter.

The conditional status filtering is a key behavior of this function. Adding a comment would help future maintainers understand that passing an empty status retrieves the latest session regardless of its status.

Apply this diff to add clarifying documentation:

 func getAppSession(tx *gorm.DB, sessionID, status string) (*AppSession, error) {
 	var appSession AppSession
+	// Build query with session_id filter; optionally add status filter if status is non-empty
 	query := tx.Where("session_id = ?", sessionID)
 	if status != "" {
 		query = query.Where("status = ?", status)
 	}
 	if err := query.Order("nonce DESC").First(&appSession).Error; err != nil {
 		return nil, err
 	}
 	return &appSession, nil
 }
clearnode/app_session_service_test.go (3)

440-481: Consider asserting the exact count of AppSessionUpdate notifications and extracting validation to a helper.

The current implementation iterates through all notifications and validates each AppSessionUpdate notification found, but doesn't verify that there's exactly one per user. This could mask issues where duplicate notifications are sent.

Consider these improvements:

  1. Assert exact notification count per user:
 		// Verify AppSession fields are not empty in the notification
+		notificationCount := 0
 		for _, notifications := range capturedNotifications {
 			for _, notification := range notifications {
 				if notification.eventType == AppSessionUpdateEventType {
+					notificationCount++
 					notificationData, ok := notification.data.(struct {
 						AppSessionResponse
 						ParticipantAllocations []AppAllocation `json:"participant_allocations"`
 					})
 					require.True(t, ok, "notification data should be AppSessionUpdateNotification")
 
 					assert.Equal(t, session.SessionID, notificationData.AppSessionID, "AppSessionID should match")
 					// ... rest of assertions
 				}
 			}
 		}
+		assert.Equal(t, 2, notificationCount, "Should have exactly one AppSessionUpdate notification per participant")
  1. Extract to a reusable helper function to avoid duplication across test cases:
func assertAppSessionUpdateNotification(t *testing.T, notification Notification, expectedSessionID string, expectedStatus string, expectedVersion uint64) {
	notificationData, ok := notification.data.(struct {
		AppSessionResponse
		ParticipantAllocations []AppAllocation `json:"participant_allocations"`
	})
	require.True(t, ok, "notification data should be AppSessionUpdateNotification")
	
	assert.Equal(t, expectedSessionID, notificationData.AppSessionID)
	assert.Equal(t, expectedStatus, notificationData.Status)
	// ... additional assertions
}

This would improve maintainability and allow adding similar assertions to other test cases like MultipleParticipantsTokens (lines 490-542).


444-447: Consider defining the notification payload type explicitly rather than using an inline struct.

The inline anonymous struct definition works but makes the code harder to maintain and prevents reuse across test cases.

If this type matches the actual notification structure from the service layer, consider importing or defining it at the package level:

// At package level
type AppSessionUpdateNotification struct {
	AppSessionResponse
	ParticipantAllocations []AppAllocation `json:"participant_allocations"`
}

// In test
notificationData, ok := notification.data.(AppSessionUpdateNotification)
require.True(t, ok, "notification data should be AppSessionUpdateNotification")

This improves type safety and makes it easier to reuse the validation logic across multiple test cases.


467-467: Timestamp comparison allows equality but message suggests strict inequality.

Line 467 uses updatedAt.After(createdAt) || updatedAt.Equal(createdAt) but the assertion message says "UpdatedAt should be >= CreatedAt", which correctly describes the check. However, Go's time package provides a cleaner alternative.

Consider using the more idiomatic approach:

-					assert.True(t, updatedAt.After(createdAt) || updatedAt.Equal(createdAt), "UpdatedAt should be >= CreatedAt")
+					assert.False(t, updatedAt.Before(createdAt), "UpdatedAt should be >= CreatedAt")

Or use assert.GreaterOrEqual from testify if available in your version:

-					assert.True(t, updatedAt.After(createdAt) || updatedAt.Equal(createdAt), "UpdatedAt should be >= CreatedAt")
+					assert.GreaterOrEqual(t, updatedAt.Unix(), createdAt.Unix(), "UpdatedAt should be >= CreatedAt")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d916343 and bf18344.

📒 Files selected for processing (2)
  • clearnode/app_session.go (1 hunks)
  • clearnode/app_session_service_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
clearnode/app_session_service_test.go (3)
clearnode/notification.go (1)
  • AppSessionUpdateEventType (46-46)
clearnode/rpc_router_private.go (2)
  • AppSessionResponse (62-75)
  • AppAllocation (56-60)
clearnode/pkg/rpc/api.go (4)
  • AppAllocation (584-591)
  • ChannelStatusOpen (644-644)
  • VersionNitroRPCv0_4 (33-33)
  • Version (27-27)
🔇 Additional comments (1)
clearnode/app_session.go (1)

32-42: The review comment can be resolved—all callers already handle the new behavior correctly.

The concern about the status filtering change is based on a misunderstanding of the code's intent. ChannelStatus only has three valid values: "open", "closed", and "challenged". Empty string "" is not a valid status.

The old implementation (filtering with status = "") would have returned zero results at line 463, making that code path broken. The new conditional filtering—which skips the status filter when an empty string is passed—actually fixes this bug. Line 463 reloads a session after update and correctly needs to retrieve the session regardless of its status.

The three call sites are all correct:

  • Line 411 & 507: Pass explicit "open" status—behavior unchanged
  • Line 463: Passes "" for a reload after save—now works correctly (was broken before)

@dimast-x dimast-x merged commit ec468ff into main Oct 27, 2025
13 checks passed
@dimast-x dimast-x deleted the fix/asu-notif branch October 27, 2025 05:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants