Skip to content

fix(provider/client): forward service filter and tail to gateway#310

Open
gosuri wants to merge 4 commits into
mainfrom
fix/lease-logs-events-query-params
Open

fix(provider/client): forward service filter and tail to gateway#310
gosuri wants to merge 4 commits into
mainfrom
fix/lease-logs-events-query-params

Conversation

@gosuri
Copy link
Copy Markdown
Member

@gosuri gosuri commented May 26, 2026

📝 Description

Three wire-level bugs in the provider gateway websocket client meant every consumer's --service / --tail flag was silently dropped. The interface in client.go:70-71 advertises services and tailLines, but the implementations bind both to _ and never write them to the query string. This PR makes the implementation honor the published interface contract.

  • LeaseLogs: ?services=?service= (gateway uses singular).
  • LeaseLogs: forward tailLines as ?tail=N (was dropped).
  • LeaseEvents: forward services as ?service= (was dropped).

Cross-verified against akash-network/provider main, gateway/rest/middleware.go requestStreamParams: the gateway reads vars.Get("service") and vars.Get("tail").

🔧 Purpose of the Change

  • Bug fix

📌 Related Issues

✅ Checklist

  • I've updated relevant documentation — N/A, no docs reference the broken behavior
  • Code follows Akash Network's style guide
  • I've added/updated relevant unit tests (httptest + gorilla/websocket upgrade, asserts on captured query string)
  • Dependencies have been properly updated — none
  • I agree and adhered to the Contribution Guidelines

📎 Notes for Reviewers

  • No external API or signature change. The interface in client.go:70-71 already named these parameters. Today's _ placeholders are an implementation drift from the published interface — not a behavior callers can be relying on.
  • tailLines guard. Forwarded only when > 0. The gateway accepts tail >= -1 (default -1), so we treat 0/negative as "leave gateway default". Open to flipping to >= 0 if reviewers prefer.
  • Test pattern. These are the first httptest users in this package — see captureQueryServer + newTestClient helpers in client_test.go. The helpers spin up httptest.NewTLSServer with a websocket.Upgrader and capture the query string at upgrade time; same-package access lets the helper replace c.tlsCfg directly so no production API surface is added for the test.

- LeaseLogs: ?services= → ?service= (gateway reads singular).
- LeaseLogs: forward tailLines as ?tail=N (was dropped).
- LeaseEvents: forward services as ?service= (was dropped).

The interface in client.go:70-71 already named these parameters, but
the implementations bound them to `_` and silently dropped the values.
This makes the implementation honor the published contract.

Cross-verified against akash-network/provider main,
gateway/rest/middleware.go requestStreamParams: the gateway reads
vars.Get("service") and vars.Get("tail").

Adds httptest-based unit tests that pin the wire format (singular
service key, tail forwarding, no legacy plural services key) and
guard the omit-when-empty/zero branches.

closes akash-network/support#523
@gosuri gosuri requested a review from a team as a code owner May 26, 2026 20:11
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7993e103-54db-4af3-a4bf-e453559da7da

📥 Commits

Reviewing files that changed from the base of the PR and between 124c998 and 905f631.

📒 Files selected for processing (1)
  • go/provider/client/client_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • go/provider/client/client_test.go

Walkthrough

Fixes client websocket query encoding: LeaseEvents now includes the service filter when non-empty; LeaseLogs accepts tailLines and includes tail when >0. Tests and helpers added to capture and assert the gateway upgrade query parameters.

Changes

Lease Streaming Query Parameters

Layer / File(s) Summary
LeaseEvents service filter forwarding
go/provider/client/client.go
LeaseEvents now binds and uses the services parameter and conditionally appends service=<services> to the websocket URL query when non-empty.
LeaseLogs tail and service parameters
go/provider/client/client.go
LeaseLogs now binds and uses the tailLines parameter, appending tail=<tailLines> when > 0, and forwards the service filter as singular service= in the query.
Test server and client helpers
go/provider/client/client_test.go
Imports extended for TLS/httptest/websocket; added captureQueryServer, newTestClient, and testLeaseID helpers to record websocket upgrade queries and create test clients trusting the server cert.
LeaseLogs query parameter tests
go/provider/client/client_test.go
TestLeaseLogs_ForwardsServiceAndTailToGateway and TestLeaseLogs_OmitsEmptyServiceAndZeroTail validate forwarding of service, tail, follow and omission of empty/zero values and legacy services.
LeaseEvents query parameter tests
go/provider/client/client_test.go
TestLeaseEvents_ForwardsServiceToGateway and TestLeaseEvents_OmitsEmptyService validate forwarding of service and follow and omission of empty values and legacy services.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A hop of code, a careful tweak,

service and tail no longer leak.
Websockets hum with truthful queues,
Tests record the path they use.
Hooray — the gateway gets its cues!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: forwarding service filter and tail parameters to the gateway in provider/client.
Description check ✅ Passed The description provides clear explanation of the bugs, their fix, purpose, related issue, and checklist completion with good reviewer notes.
Linked Issues check ✅ Passed The PR implementation directly addresses all three bugs identified in issue #523: singular service key, tail parameter forwarding, and LeaseEvents services parameter forwarding.
Out of Scope Changes check ✅ Passed All changes are in-scope: client.go fixes implement missing parameter forwarding, client_test.go adds comprehensive test coverage with httptest helpers for the new behavior.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/lease-logs-events-query-params

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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.

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: 1

🧹 Nitpick comments (2)
go/provider/client/client_test.go (2)

99-103: ⚡ Quick win

Capture only the first websocket query in the helper.

The helper contract says it records the first upgrade request, but it currently overwrites q on each request. That can make retries/reconnects clobber the asserted query and introduce flakiness.

Proposed refactor
 func captureQueryServer(t *testing.T) (*httptest.Server, func() url.Values) {
 	t.Helper()
 	var (
 		mu sync.Mutex
+		once sync.Once
 		q  url.Values
 	)
 	upgrader := websocket.Upgrader{
 		CheckOrigin: func(*http.Request) bool { return true },
 	}
 	srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		mu.Lock()
-		q = r.URL.Query()
-		mu.Unlock()
+		once.Do(func() {
+			mu.Lock()
+			q = r.URL.Query()
+			mu.Unlock()
+		})
 
 		conn, err := upgrader.Upgrade(w, r, nil)
 		if err != nil {
 			return
 		}
 		_ = conn.Close()
 	}))
 	return srv, func() url.Values {
 		mu.Lock()
 		defer mu.Unlock()
-		return q
+		out := make(url.Values, len(q))
+		for k, v := range q {
+			out[k] = append([]string(nil), v...)
+		}
+		return out
 	}
 }

Also applies to: 110-114

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@go/provider/client/client_test.go` around lines 99 - 103, The helper's HTTP
handler currently overwrites the recorded websocket query `q` on every request,
causing flaky tests; change the handler used with `httptest.NewTLSServer` (the
closure that reads `r.URL.Query()`) to record only the first upgrade request by
guarding the assignment with a check (e.g., use a sync.Once or only assign if
`len(q) == 0`) while still protecting with `mu.Lock()`/`mu.Unlock()`; apply the
same single-capture change to the second similar handler referenced around the
110-114 region so retries/reconnects don't clobber the originally asserted
query.

154-154: ⚡ Quick win

Avoid unbounded waits on stream close channels in tests.

Direct receives on OnClose can hang the suite indefinitely on regressions. Use a bounded wait helper with timeout for deterministic failure.

Proposed refactor
 import (
 	"context"
 	"crypto/tls"
 	"errors"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
 	"sync"
 	"testing"
+	"time"
@@
 )
+
+func waitOnClose(t *testing.T, ch <-chan struct{}) {
+	t.Helper()
+	select {
+	case <-ch:
+	case <-time.After(2 * time.Second):
+		t.Fatal("timed out waiting for stream close")
+	}
+}
@@
-	<-out.OnClose // wait for reader goroutine to drain after server close
+	waitOnClose(t, out.OnClose) // wait for reader goroutine to drain after server close
@@
-	<-out.OnClose
+	waitOnClose(t, out.OnClose)
@@
-	<-out.OnClose
+	waitOnClose(t, out.OnClose)
@@
-	<-out.OnClose
+	waitOnClose(t, out.OnClose)

Also applies to: 176-176, 195-195, 213-213

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@go/provider/client/client_test.go` at line 154, The test currently does a
direct receive from the stream close channel (<-out.OnClose) which can hang;
replace these unbounded receives in client_test.go (instances referencing
out.OnClose at the noted locations) with a bounded wait helper that times out
and fails the test (e.g., use a helper like waitForClose(t, out.OnClose,
timeout) or require.Eventually with a short timeout) so the test
deterministically fails on regressions instead of hanging; add the helper near
the tests and use it at each occurrence (lines referencing out.OnClose) to
assert the channel closed within the timeout.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@go/provider/client/client_test.go`:
- Around line 215-217: The test at getQuery()/q.Get("service") only asserts
service is empty; strengthen it by also asserting that the follow parameter is
explicitly "false" and that the "services" parameter is omitted/empty for
symmetry with other query-forwarding tests — add assertions like
require.Equal(t, "false", q.Get("follow")) and require.Empty(t,
q.Get("services")) alongside the existing require.Empty(t, q.Get("service")) in
the LeaseEvents-related test.

---

Nitpick comments:
In `@go/provider/client/client_test.go`:
- Around line 99-103: The helper's HTTP handler currently overwrites the
recorded websocket query `q` on every request, causing flaky tests; change the
handler used with `httptest.NewTLSServer` (the closure that reads
`r.URL.Query()`) to record only the first upgrade request by guarding the
assignment with a check (e.g., use a sync.Once or only assign if `len(q) == 0`)
while still protecting with `mu.Lock()`/`mu.Unlock()`; apply the same
single-capture change to the second similar handler referenced around the
110-114 region so retries/reconnects don't clobber the originally asserted
query.
- Line 154: The test currently does a direct receive from the stream close
channel (<-out.OnClose) which can hang; replace these unbounded receives in
client_test.go (instances referencing out.OnClose at the noted locations) with a
bounded wait helper that times out and fails the test (e.g., use a helper like
waitForClose(t, out.OnClose, timeout) or require.Eventually with a short
timeout) so the test deterministically fails on regressions instead of hanging;
add the helper near the tests and use it at each occurrence (lines referencing
out.OnClose) to assert the channel closed within the timeout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8a594591-f9e0-4708-9bc4-e113e2b2f3d2

📥 Commits

Reviewing files that changed from the base of the PR and between 3ccaa4d and 124c998.

📒 Files selected for processing (2)
  • go/provider/client/client.go
  • go/provider/client/client_test.go

Comment thread go/provider/client/client_test.go
Copy link
Copy Markdown
Contributor

@chalabi2 chalabi2 left a comment

Choose a reason for hiding this comment

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

Gateway-side verified, mocks unaffected, callers unchanged, tests pass. The tailLines > 0 guard is fine, --tail 0 is pathological and the default-coercion is a reasonable choice. If you want to be strict about user-intent preservation you can flip to >= 0, but I don't think it matters in practice.

GHA down but local review LGTM

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chain-sdk provider/client: LeaseLogs and LeaseEvents drop service filter and tail at the wire

2 participants