mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
Closes #17982. The purpose of this PR is to expose network latency via the API used by Coder Desktop. This PR has the tunnel ping all known agents every 5 seconds, in order to produce an instance of: ```proto message LastPing { // latency is the RTT of the ping to the agent. google.protobuf.Duration latency = 1; // did_p2p indicates whether the ping was sent P2P, or over DERP. bool did_p2p = 2; // preferred_derp is the human readable name of the preferred DERP region, // or the region used for the last ping, if it was sent over DERP. string preferred_derp = 3; // preferred_derp_latency is the last known latency to the preferred DERP // region. Unset if the region does not appear in the DERP map. optional google.protobuf.Duration preferred_derp_latency = 4; } ``` The contents of this message are stored and included on all subsequent upsertions of the agent. Note that we upsert existing agents every 5 seconds to update the `last_handshake` value. On the desktop apps, this message will be used to produce a tooltip similar to that of the VS Code extension: <img width="495" alt="image" src="https://github.com/user-attachments/assets/d8b65f3d-f536-4c64-9af9-35c1a42c92d2" /> (wording not final) Unlike the VS Code extension, we omit: - The Latency of *all* available DERP regions. It seems not ideal to send a copy of this entire map for every online agent, and it certainly doesn't make sense for it to be on the `Agent` or `LastPing` message. If we do want to expose this info on Coder Desktop, we should consider how best to do so; maybe we want to include it on a more generic `Netcheck` message. - The current throughput (Bytes up/down). This is out of scope of the linked issue, and is non-trivial to implement. I'm also not sure of the value given the frequency we're doing these status updates (every 5 seconds). If we want to expose it, it'll be in a separate PR. <img width="343" alt="image" src="https://github.com/user-attachments/assets/8447d03b-9721-4111-8ac1-332d70a1e8f1" />
41 lines
961 B
Go
41 lines
961 B
Go
package maps
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"golang.org/x/exp/constraints"
|
|
)
|
|
|
|
func Map[K comparable, F any, T any](params map[K]F, convert func(F) T) map[K]T {
|
|
into := make(map[K]T)
|
|
for k, item := range params {
|
|
into[k] = convert(item)
|
|
}
|
|
return into
|
|
}
|
|
|
|
// Subset returns true if all the keys of a are present
|
|
// in b and have the same values.
|
|
// If the corresponding value of a[k] is the zero value in
|
|
// b, Subset will skip comparing that value.
|
|
// This allows checking for the presence of map keys.
|
|
func Subset[T, U comparable](a, b map[T]U) bool {
|
|
var uz U
|
|
for ka, va := range a {
|
|
ignoreZeroValue := va == uz
|
|
if vb, ok := b[ka]; !ok || (!ignoreZeroValue && va != vb) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// SortedKeys returns the keys of m in sorted order.
|
|
func SortedKeys[T constraints.Ordered](m map[T]any) (keys []T) {
|
|
for k := range m {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
|
|
return keys
|
|
}
|