GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — feat: report port-forward sessions as app usage by nickvigilante · Pull Request #30156 · coder/coder Conversation
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I think this test is flaky because of the issue that #30132 attempts to fix. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@codex review |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Here are some automated review suggestions for this pull request.
Reviewed commit: 41e81cdb20
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The server records these sessions only when the hidden workspace-usage experiment is on. Without it, postWorkspaceUsage returns 204 before it reads the body. Can you say that in the description?
The description also says the data feeds application usage insights and Prometheus insights metrics. While port_forward is unregistered, both drop it as the unknown family. Today it only reaches active users, usage minutes, the deployment banner, and telemetry.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@f0ssel what do we want to do with |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@EhabY Thanks, I updated the PR description. Generated by Coder Agents on behalf of @nickvigilante. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I would sugegst hold on to this until we are sure we are going to keep the experiment. I think there is plan to remove it PLAT-152 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Fair enough. I'm going to close this PR and park its corresponding issue until we settle on the plan you mentioned. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
oops wrong issue linked. corrected |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Tbh I see no harm in merging this since it means we now correctly count |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@matifali Let me know if we should reconsider. I'm happy to do what I'm told here. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Yes we can. Its a net additive chnage so I am good adding it |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Would leave review to @EhabY |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
One thing I would like to know is how this data would be consumed? Is it going to Template insights and what Prometheus metrics? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Currently it would only be shown in the Deployment banner, but after the scalable IDE work lands it'll be part of every other metric |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
28cd398 to
b205b39
Compare
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
LGTM. A few optional simplifications inline.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| // request returns the next usage report. The body is empty, which only bumps | ||
| // the workspace's last-used time, unless a connection is open or opened since | ||
| // the previous call. | ||
| func (u *portForwardUsage) request(agentID uuid.UUID) codersdk.PostWorkspaceUsageRequest { | ||
| // Swap first so a connection that opened and closed between calls counts. | ||
| if u.seen.Swap(false) || u.active.Load() > 0 { | ||
| return codersdk.PostWorkspaceUsageRequest{ | ||
| AgentID: agentID, | ||
| AppName: string(codersdk.UsageAppNamePortForwarding), | ||
| } | ||
| } | ||
| return codersdk.PostWorkspaceUsageRequest{} | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The tracker doesn't need to know the SDK request shape. Return a bool and let the reporter build the body (see below). TestPortForwardUsageRequest can then compare against u.inUse() with want []bool.
| // request returns the next usage report. The body is empty, which only bumps | |
| // the workspace's last-used time, unless a connection is open or opened since | |
| // the previous call. | |
| func (u *portForwardUsage) request(agentID uuid.UUID) codersdk.PostWorkspaceUsageRequest { | |
| // Swap first so a connection that opened and closed between calls counts. | |
| if u.seen.Swap(false) || u.active.Load() > 0 { | |
| return codersdk.PostWorkspaceUsageRequest{ | |
| AgentID: agentID, | |
| AppName: string(codersdk.UsageAppNamePortForwarding), | |
| } | |
| } | |
| return codersdk.PostWorkspaceUsageRequest{} | |
| } | |
| // inUse reports whether a connection is open or opened since the last call. | |
| func (u *portForwardUsage) inUse() bool { | |
| // Swap first so a connection that opened and closed between calls counts. | |
| return u.seen.Swap(false) || u.active.Load() > 0 | |
| } |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Applied in a55b934: the tracker now exposes inUse() bool and TestPortForwardUsageRequest compares against want []bool.
Generated by Coder Agents on behalf of @nickvigilante.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| report := func(ctx context.Context) { | ||
| if err := client.PostWorkspaceUsageWithBody(ctx, workspaceID, usage.request(agentID)); err != nil { | ||
| client.Logger().Warn(ctx, "failed to post workspace usage", slog.Error(err)) | ||
| } | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Follows from the inUse() suggestion above.
| report := func(ctx context.Context) { | |
| if err := client.PostWorkspaceUsageWithBody(ctx, workspaceID, usage.request(agentID)); err != nil { | |
| client.Logger().Warn(ctx, "failed to post workspace usage", slog.Error(err)) | |
| } | |
| } | |
| report := func(ctx context.Context) { | |
| var req codersdk.PostWorkspaceUsageRequest | |
| if usage.inUse() { | |
| req = codersdk.PostWorkspaceUsageRequest{ | |
| AgentID: agentID, | |
| AppName: string(codersdk.UsageAppNamePortForwarding), | |
| } | |
| } | |
| if err := client.PostWorkspaceUsageWithBody(ctx, workspaceID, req); err != nil { | |
| client.Logger().Warn(ctx, "failed to post workspace usage", slog.Error(err)) | |
| } | |
| } |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Applied in a55b934: the reporter builds the PostWorkspaceUsageRequest itself from inUse().
Generated by Coder Agents on behalf of @nickvigilante.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| report(ctx) | ||
| ticker := time.NewTicker(time.Minute) | ||
| doneCh := make(chan struct{}) | ||
| go func() { | ||
| defer func() { | ||
| ticker.Stop() | ||
| close(doneCh) | ||
| }() | ||
| for { | ||
| select { | ||
| case <-ticker.C: | ||
| report(ctx) | ||
| case <-ctx.Done(): | ||
| // ctx is canceled, so send the final report on a detached one. | ||
| finalCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) | ||
| report(finalCtx) | ||
| cancel() | ||
| return | ||
| } | ||
| } | ||
| }() | ||
| return func() { | ||
| <-doneCh | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The returned func only waits, so it blocks forever unless the caller cancels ctx first (L193 happens to). Cancel internally like UpdateWorkspaceUsageWithBodyContext does, and use defer for cleanup. The doc on L254 would then read "Call the returned func to stop and wait for the final report."
| report(ctx) | |
| ticker := time.NewTicker(time.Minute) | |
| doneCh := make(chan struct{}) | |
| go func() { | |
| defer func() { | |
| ticker.Stop() | |
| close(doneCh) | |
| }() | |
| for { | |
| select { | |
| case <-ticker.C: | |
| report(ctx) | |
| case <-ctx.Done(): | |
| // ctx is canceled, so send the final report on a detached one. | |
| finalCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) | |
| report(finalCtx) | |
| cancel() | |
| return | |
| } | |
| } | |
| }() | |
| return func() { | |
| <-doneCh | |
| } | |
| ctx, cancel := context.WithCancel(ctx) | |
| report(ctx) | |
| done := make(chan struct{}) | |
| go func() { | |
| defer close(done) | |
| ticker := time.NewTicker(time.Minute) | |
| defer ticker.Stop() | |
| for { | |
| select { | |
| case <-ticker.C: | |
| report(ctx) | |
| case <-ctx.Done(): | |
| // ctx is canceled, so send the final report on a detached one. | |
| finalCtx, finalCancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) | |
| report(finalCtx) | |
| finalCancel() | |
| return | |
| } | |
| } | |
| }() | |
| return func() { | |
| cancel() | |
| <-done | |
| } |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Applied in a55b934: the returned func now cancels an internal context and waits for the final report, which is sent on a detached context with a 5s timeout. The doc comment is updated. The caller around L193 still calls its own cancel() for the command context, which is still needed to stop the listeners, so it is unchanged.
Generated by Coder Agents on behalf of @nickvigilante.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
`coder port-forward` reported usage with the deprecated `UpdateWorkspaceUsageContext`. It now uses `UpdateWorkspaceUsageWithBodyContext` with the agent ID and a new `port_forward` app name, like `coder ssh` and the VS Code SSH path. Linear: CLI-31
Rename codersdk.UsageAppNamePortForward to UsageAppNamePortForwarding (wire value port_forwarding) to match the arbitrary app names RFC's connection-method name, per review. Report port-forwarding usage only while a forwarded connection is open, instead of on a fixed one-minute ticker regardless of activity. An idle coder port-forward with nothing connected no longer counts as an active session. A final report runs on shutdown so a session that starts and ends between ticks is not lost. Update TestPortForward_UpdateUsage: the no-connection case now expects no session count, and a new case opens a forwarded connection and expects a port_forwarding session.
- Register a port_forwarding app family so sessions show as Port Forwarding instead of unknown. The deployment banner totals it under Other. - Count only TCP connections. UDP forwards never see EOF, so one datagram kept the session active until exit. - Report once at startup so the last-used bump isn't delayed a minute. - Move the idle/active decision into portForwardUsage.request and table-test it. Linear: CLI-31
The usage tracker now returns a bool and the reporter builds the SDK request. The reporter's stop func cancels its own context before waiting, so callers no longer need to cancel first.
b205b39 to
a55b934
Compare
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.