Skip to content

Conversation

@gmegidish
Copy link
Member

@gmegidish gmegidish commented Nov 27, 2025

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability of frame rate configuration with automatic retry mechanism (up to 5 attempts) for transient failures.
    • Enhanced endpoint request handling with timeout enforcement to prevent hanging connections.
    • Refined session lifecycle management with improved creation and cleanup logic.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 27, 2025

Walkthrough

The PR modifies WDA session management by introducing strongly-typed session request structures and in-memory session caching, adds retry logic for framerate settings application, enhances endpoint handling with context timeouts and proper resource cleanup, and disables timing stat logging in device enumeration.

Changes

Cohort / File(s) Summary
Logging Disabled
devices/common.go
Timing stats logging in GetAllControllableDevices now disabled by changing conditional to constant false; offline device timing details path no longer executed.
Session Request Refactoring
devices/wda/requests.go
Added internal types (alwaysMatch, sessionCapabilities, sessionRequest) for structured session payloads. CreateSession now constructs strongly-typed request instead of inline map. GetEndpoint enhanced with 5-second context timeout and deferred response body closure. DeleteSession clears cached sessionId on deletion.
Session Management & Retry Logic
devices/wda/appium-settings.go
SetAppiumSettings updated to use "get or create" session pattern. SetMjpegFramerate gains retry logic with up to 5 attempts, logging each failure and returning aggregated error on complete failure.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant AppiumSettings
    participant Requests
    participant WDA as WDA Service

    rect rgba(200, 220, 255, 0.3)
    note over Caller,WDA: SetMjpegFramerate with Retry
    Caller->>AppiumSettings: SetMjpegFramerate(frameRate)
    loop Retry up to 5 attempts
        AppiumSettings->>AppiumSettings: Check session validity
        alt Session missing/invalid
            AppiumSettings->>Requests: GetOrCreateSession()
            Requests->>WDA: CreateSession
            WDA-->>Requests: sessionId
            Requests-->>AppiumSettings: sessionId
        end
        AppiumSettings->>Requests: PostEndpoint(framerate setting)
        Requests->>WDA: POST framerate request
        alt Success
            WDA-->>Requests: 200 OK
            Requests-->>AppiumSettings: success
            AppiumSettings-->>Caller: return nil
        else Failure
            WDA-->>Requests: error
            Requests-->>AppiumSettings: error logged
            AppiumSettings->>AppiumSettings: Attempt N failed, retry...
        end
    end
    rect rgba(255, 200, 200, 0.3)
    note over AppiumSettings: All 5 attempts failed
    AppiumSettings-->>Caller: Aggregated error
    end
    end

    rect rgba(200, 255, 220, 0.3)
    note over Requests: Session Lifecycle (Session Creation/Deletion)
    Caller->>Requests: CreateSession()
    Requests->>Requests: Build sessionRequest (typed)
    Requests->>WDA: POST with structured payload
    WDA-->>Requests: {sessionId, ...}
    Requests->>Requests: Cache sessionId in-memory
    Requests-->>Caller: {sessionId, ...}
    
    Caller->>Requests: DeleteSession(sessionId)
    Requests->>WDA: DELETE /session/{id}
    WDA-->>Requests: 200 OK
    Requests->>Requests: Clear cached sessionId if match
    Requests-->>Caller: success
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • devices/wda/requests.go: Review new internal types for session payload modeling and session caching logic; verify context timeout application and deferred resource cleanup patterns.
  • devices/wda/appium-settings.go: Examine retry loop implementation (5 attempts, error aggregation) and integration with session management pattern.
  • Session state consistency: Cross-file verification that cached sessionId is properly maintained across creation and deletion operations.

Possibly related PRs

  • feat: add wda session caching #111: Directly modifies WDA session management, adding cached sessionId field and GetOrCreateSession/isSessionStillValid logic for coordinated session lifecycle handling.

Poem

🐰 Sessions now cached, retries on the way,
Five chances to apply the frames, hip-hooray!
Structured requests march where maps once roamed,
Resources deferred, memory combed—
WDA whispers sweeter, more alive!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main fix in the changeset: adding retry logic to SetMjpegFramerate to address race conditions in WDA session management.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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 feat-mjpeg-framerate-wda-race-condition

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

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
devices/common.go (1)

119-122: Bug: all iOS device entries share the same pointer

In this loop:

for _, device := range iosDevices {
    allDevices = append(allDevices, &device)
}

device is a loop variable reused on each iteration, so every &device points to the same memory location, and all entries in allDevices will effectively reference the last iOS device.

Use the slice element address instead:

-	for _, device := range iosDevices {
-		allDevices = append(allDevices, &device)
-	}
+	for i := range iosDevices {
+		allDevices = append(allDevices, &iosDevices[i])
+	}

This ensures each ControllableDevice points to a distinct underlying device.

devices/wda/requests.go (1)

160-177: Safer handling of sessionId extraction recommended

Building the session request via sessionRequest{Capabilities: ...} is clean, and posting it through PostEndpoint("session", request) fits the existing helper.

The only concern is:

sessionId := response["sessionId"].(string)

If WDA ever returns an unexpected structure (e.g., missing key, nested under another field, or non-string type), this will panic. Consider a safer extraction pattern:

-	sessionId := response["sessionId"].(string)
-	return sessionId, nil
+	rawSessionID, ok := response["sessionId"]
+	if !ok {
+		return "", fmt.Errorf("create session response missing sessionId: %v", response)
+	}
+
+	sessionId, ok := rawSessionID.(string)
+	if !ok {
+		return "", fmt.Errorf("create session response has non-string sessionId: %T", rawSessionID)
+	}
+
+	return sessionId, nil

This keeps failures as regular errors instead of panics when the remote end misbehaves.

🧹 Nitpick comments (2)
devices/common.go (1)

147-150: Timing log effectively disabled via if false

Guarding the verbose timing log with if false is a simple way to silence it without touching surrounding logic, and it won’t affect compilation since the variables are still used. If this is meant to be permanent rather than a temporary toggle, consider either removing the block or guarding it with a config / verbosity flag instead of a hardcoded false to keep intent clearer.

devices/wda/appium-settings.go (1)

25-42: Retry loop is good; align logging and consider backoff

The retry logic around SetAppiumSettings is straightforward and should help with transient WDA races. Two small polish points:

  • Use the project’s logging utility (e.g. utils.Verbose or similar) instead of fmt.Printf so these messages respect the CLI’s log configuration.
  • Optionally add a short sleep between retries to avoid hammering WDA in quick succession and to give it time to recover.

For example:

-	for i := 0; i < maxRetries; i++ {
+	for i := 0; i < maxRetries; i++ {
 		err = c.SetAppiumSettings(map[string]interface{}{
 			"mjpegServerFramerate": framerate,
 		})
 
 		if err == nil {
 			return nil
 		}
 
-		fmt.Printf("attempt %d/%d failed to set mjpeg framerate: %v\n", i+1, maxRetries, err)
+		utils.Verbose("attempt %d/%d failed to set mjpeg framerate: %v", i+1, maxRetries, err)
+		time.Sleep(500 * time.Millisecond)
 	}

(Adjust sleep and logging helper to taste.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de3a2fb and 19db13f.

📒 Files selected for processing (3)
  • devices/common.go (1 hunks)
  • devices/wda/appium-settings.go (2 hunks)
  • devices/wda/requests.go (6 hunks)
⏰ 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). (1)
  • GitHub Check: lint
🔇 Additional comments (6)
devices/wda/appium-settings.go (1)

5-23: Session handling in SetAppiumSettings looks solid

Using GetOrCreateSession here is a good improvement: it reuses a valid cached WDA session and falls back to creating a new one when needed, and the updated error message is accurate. The endpoint construction and payload shape ({"settings": settings}) remain correct.

devices/wda/requests.go (5)

15-25: Typed session payload definitions are clear and appropriate

The alwaysMatch, sessionCapabilities, and sessionRequest structs mirror the W3C/WebDriver capabilities structure and keep the session creation payload explicit and type-safe. Keeping them unexported is appropriate since they’re internal to the WDA client.


43-43: Good: response body is now reliably closed in GetEndpoint

Adding the deferred resp.Body.Close() ensures no leaked connections from GET calls, which is important under sustained use of the WDA client.


80-80: Good: explicit JSON content type on POST

Setting Content-Type: application/json improves interoperability with servers that strictly validate headers. This is a nice robustness fix.


187-205: Cached-session reuse and invalidation flow still looks good

GetOrCreateSession correctly:

  • Returns the cached c.sessionId when isSessionStillValid succeeds.
  • Logs and clears c.sessionId when the cached session is no longer valid, then creates and caches a new session via CreateSession.

This aligns with the new usage from SetAppiumSettings and should reduce unnecessary session churn while still recovering cleanly from invalid sessions.


208-219: Nice: cached sessionId cleared when deleting the active session

Clearing c.sessionId when DeleteSession successfully deletes that same session keeps the client’s in-memory cache consistent with WDA’s state and prevents future calls from trying to reuse a dead session.

if c.sessionId == sessionId {
    c.sessionId = ""
}

This is a simple but important correctness improvement for the session lifecycle.

@gmegidish gmegidish merged commit 1a66429 into main Nov 27, 2025
15 checks passed
@gmegidish gmegidish deleted the feat-mjpeg-framerate-wda-race-condition branch November 27, 2025 11:41
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