Skip to content

feat(gateway): load balancing - #370

Open
bernie-g wants to merge 7 commits into
mainfrom
bernie/pam-375-gateway-load-reporting
Open

feat(gateway): load balancing#370
bernie-g wants to merge 7 commits into
mainfrom
bernie/pam-375-gateway-load-reporting

Conversation

@bernie-g

Copy link
Copy Markdown
Contributor

Description 📣

The gateway now keeps a count of how many connections it is currently handling and reports that number to Infisical every 10 seconds. This lets gateway pools send new work to whichever gateway is least busy, including work the platform never sees directly such as PAM sessions opened from the CLI.

Pairs with the platform change in Infisical/infisical#7703. Older gateways simply don't report, and the platform falls back to its own view for those.

Type ✨

  • Bug fix
  • New feature
  • Improvement
  • Breaking change
  • Documentation

Tests 🛠️

Ran two gateways in a pool and watched the reported count while holding connections open. It rises with each connection, holds while they are open, and returns to zero when they close. Verified this includes a PAM session opened with infisical pam access, which the platform hands certificates to and never proxies itself.

# open a PAM session against an account on a gateway pool
infisical pam access df/my-account --duration 10m

# the gateway handling it reports its connection count
redis-cli get "gateway-reported-load:<gatewayId>"
# 2:1787065046473   <- two open connections

@infisical-review-police

Copy link
Copy Markdown

💬 Discussion in Slack: #pr-review-cli-370-feat-gateway-report-active-connection-count-for-pool-load-bala

Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel.

@linear

linear Bot commented Aug 18, 2026

Copy link
Copy Markdown

PAM-375

@greptile-apps

greptile-apps Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds active SSH-channel accounting to gateway v2 and reports the current count to a new backend endpoint every ten seconds for pool load balancing.

  • Adds the typed load-report API request and endpoint wrapper.
  • Counts accepted channels for the duration of synchronous gateway handling.
  • Starts a process-lifetime periodic reporting loop alongside gateway heartbeats.
  • The reporter currently lacks a request timeout, so a stalled endpoint can suspend future reports.

Confidence Score: 4/5

The PR appears safe to merge, though the load-report request should be bounded so a stalled endpoint cannot permanently suspend updates.

Channel accounting follows the complete synchronous handler lifetime and survives relay reconnects, but the periodic reporter can stop making progress when its unbounded synchronous HTTP request stalls.

Files Needing Attention: packages/gateway-v2/gateway.go, packages/api/api.go

Important Files Changed

Filename Overview
packages/api/api.go Adds the v2 gateway load-report endpoint wrapper; its synchronous request inherits the shared client's lack of a timeout.
packages/api/model.go Adds the activeChannels request DTO with the expected JSON field.
packages/gateway-v2/gateway.go Adds atomic channel accounting and periodic reporting, with a non-blocking concern around indefinitely stalled HTTP requests.

Reviews (1): Last reviewed commit: "feat(gateway): report active channel cou..." | Re-trigger Greptile

Comment thread packages/gateway-v2/gateway.go Outdated
Comment thread packages/api/api.go Outdated
@bernie-g bernie-g changed the title feat(gateway): report active connection count for pool load balancing feat(gateway): load balancing Aug 19, 2026
Counts channels in handleIncomingChannel, the single point every inbound
channel passes through whatever opened it, and reports the total every 10s
so the platform can route new work to the least busy member of a pool.

Kept off the heartbeat deliberately: a heartbeat makes the platform dial
back through the relay and write to its database, which is far too costly
at the cadence selection needs. A missed report only costs accuracy, so
failures are logged at debug and the platform falls back to its own view.
The report ran with no deadline, so an endpoint that accepts the connection
and then stalls would block the reporting loop indefinitely: no further
reports, and no reaction to cancellation either, so shutdown would hang
behind it.

Each report now runs on a context with a 5s deadline derived from the
gateway's own context, which keeps a stall from reaching the next tick and
lets cancellation abort a request already in flight.
@bernie-g
bernie-g force-pushed the bernie/pam-375-gateway-load-reporting branch from c8f7b62 to 1d66d56 Compare August 20, 2026 20:10
Clears GO-2026-6163, a panic on a malformed XOR-MAPPED-ADDRESS attribute.
Reached through pion/turn in the gateway relay path. v3.1.5 is the first fixed
release; later patches pull in dtls and transport bumps this does not need.
The e2e module replaces the root module, so its go.sum needs the same versions
or every e2e job fails with "updates to go.mod needed".
Comment thread packages/gateway-v2/gateway.go
Comment thread packages/gateway-v2/gateway.go Outdated
Comment thread packages/gateway-v2/gateway.go Outdated
The doc on the reporting loop said the platform falls back to its own view when
a gateway stops reporting. It no longer does: a non-reporting member takes its
whole pool off load-aware selection.
A handler that never returns left the count high for the life of the process, so
the gateway reported itself permanently busy and stopped attracting work. The
count resets when a relay connection is established, since channels do not
outlive the connection they arrived on, and the decrement is floored so a
handler from the previous connection cannot push it negative.

Reports against a platform with no metrics endpoint retried every 10s forever.
After five consecutive failures the interval drops to five minutes and warns
once, naming the consequence: pools containing the gateway select at random.
Backs off rather than stopping, so an upgraded platform recovers without a
gateway restart.

reportMetrics becomes startMetricsReport, since it spawns the loop.
if current <= 0 {
return
}
if g.activeChannels.CompareAndSwap(current, current-1) {

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: Stale handlers decrement the new connection's count

After activeChannels is reset during reconnect, this CAS cannot tell whether the releasing handler belongs to the old or current connection. An authenticated client can keep old handlers stalled across a reconnect and close them after new channels are counted, making those stale handlers decrement the new connection's load and attract additional traffic. Track counts per connection generation, or pass a generation-specific counter into each handler, so releases only affect the connection that acquired them.

@veria-ai

veria-ai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

PR overview

This pull request adds load balancing to the gateway, including tracking active channels across connection and reconnection lifecycles.

One low-impact load-accounting issue remains open. An authenticated client can hold handlers across a reconnect and later cause the new connection's active-channel count to be understated, potentially skewing load balancing and attracting excess traffic to that connection.

Open issues (1)

Fixed/addressed: 0 · PR risk: 5/10

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.

3 participants