Repository navigation
✨ feat(traffic): live network throughput in the status pill (#112) - #152
Merged
Merged
Conversation
…ack relay Traffic counts bytes read from and written to a database server with one relaxed atomic add per read or write call. pump() forwards a connection and counts its server side; RateWindow turns samples into bytes/s over a 2 s window. Relay forwards loopback connections to host:port and keeps why the server could not be reached (#112). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rate Every network connection of a PostgreSQL, MySQL or MariaDB workspace now dials a local end the app owns: the SSH tunnel, which counts what it forwards, or for a direct connection a loopback relay opened with the workspace. The new workspace_throughput command returns bytes/s over a 2 s window (null for SQLite and MongoDB, or before a reference exists); the capability 'throughput' tells the frontend which workspaces have one. When the relay cannot reach the server, the error says why instead of the driver's end-of-file (#112). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The workspace-wide relay listened on 127.0.0.1 for the workspace's whole life and forwarded any local connection to the database host. Direct connections are now counted by the drivers instead: - PostgreSQL opens its own TCP stream, wraps it in Counted and hands it to connect_raw; cancels use cancel_query_raw on a counted stream. No listener. - mysql_async cannot take a stream (and over a Unix socket it skips TLS and sends the password in clear), so each MySQL / MariaDB connection (session, KILL QUERY, Prefer retry) dials a one-shot relay: it accepts exactly one connection, closes its listener at once, and closes after 10 s if nobody dials it. A second connection is refused (tested). Integration tests cover cancel and verify-full on both counted paths (#112). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
While a PostgreSQL, MySQL or MariaDB workspace is connected, the pill polls workspace_throughput every second and shows read + written bytes per second in its right-hand slot (0 B/s, 1.2 KB/s, 3.4 MB/s; 1 KB = 1000 B). The slot is 8 characters wide, right-aligned with tabular figures and reserved from the start, so the pill never shifts. The rate is part of the pill's accessible name, not a live region. SQLite and MongoDB have no slot; no number is shown until the backend has one (#112). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t capability - OneShot carries a cancel handle: dropped before anyone dials it (the driver's connect future was dropped), its listener closes at once instead of after 10 s. An accepted connection keeps being forwarded. - A counted PostgreSQL session's cancel requests dial the address the session reached (peer_addr), not a new DNS resolution that could pick another server. - WorkspaceInfo.capabilities.throughput is what is actually counted: a PostgreSQL Unix-socket directory as host reports false, so the pill keeps no slot that would stay blank. - MySQL verify-full through the relay: the wrong-name case fails with the certificate error (CI) (#112 review). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…le focused - useThroughput stops polling while the window is hidden (minimized) and shows nothing then; shown again it starts over blank. An answer that arrives after the stop is dropped. - The pill's accessible name keeps the rate it had when the pill got focus until it loses it, so screen readers do not re-announce it every second; the visible slot keeps moving (#112 review). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rop, PG cancel Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The focus snapshot of the rate now belongs to one workspace, and a workspace that is not counted (disconnected) names no rate: switching workspace or disconnecting while the pill keeps focus no longer leaves the previous rate in its accessible name (#112 review). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #112
Summary
The status pill shows the live network rate (read + written) of the active workspace in its right-hand slot. This covers PostgreSQL, MySQL and MariaDB, direct and through SSH. SQLite and MongoDB show nothing, and never a guessed number.
Backend: what is counted, and where
AsyncRead + AsyncWriteadapter, and hands it to tokio-postgres throughconnect_raw.make_tls_connect(host), same rustls config.cancel_query_rawto the session's ownpeer_addr(), with the configured host as TLS name, so round-robin DNS can't send a cancel to another server.tls_hostname_override), never 127.0.0.1.prefer_socket(false)is kept.workspace_throughputreturns{readPerSec, writtenPerSec} | null(counts only, no query text or secret).capabilities.throughputsays whether the workspace is counted. A PostgreSQL Unix-socket host reports false.Frontend
0 B/s,1.2 KB/s,3.4 MB/s(1 KB = 1000 B). The slot is fixed-width with tabular figures and is reserved from connect, so the pill never shifts.Docs: spec §2.3 amendment #112. Capabilities stay
core:default.The pre-existing SSH tunnel loopback listener has the same open-forwarder exposure. It is filed separately as #151.
How it was tested
The new integration tests need the test databases and sshd, so they run in CI's
pnpm test:rust:tests/throughput.rs: rate after 300 kB, idle exactly 0, cancel on the counted PG and MySQL paths, and verify-full for PG and MySQL, including a case that must fail on the wrong certificate name;manager_ssh.rs::throughput_counts_the_tunnel_traffic.The existing manager-level PG and MySQL tests now go through the counted paths.
Manual check list
0 B/s.SELECT repeat('x', 5000000)in a SQL tab. The number climbs (KB/s, MB/s) while it loads, then falls back to0 B/swithin about 2 s.SELECT SLEEP(30)can still be cancelled.0 B/s.workspace_throughputcalls) and the number is blank for about a second on restore, then live again. This checks that WebKitGTK sends the visibility change.Checklist
pnpm testgreenpnpm lintgreen (tsc + clippy-D warnings)pnpm test:rustgreen (CI; test databases not available locally)src/lib/strings.ts<emoji> <type>(<scope>): <description>🤖 Generated with Claude Code