Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc77369b

Browse files
committed
chore: added latency tooltips on workspaces
1 parent56003ed commitc77369b

File tree

4 files changed

+160
-34
lines changed

4 files changed

+160
-34
lines changed

‎App/ViewModels/AgentViewModel.cs

Lines changed: 107 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
usingSystem;
2-
usingSystem.Collections.Generic;
3-
usingSystem.Collections.ObjectModel;
4-
usingSystem.ComponentModel;
5-
usingSystem.Linq;
6-
usingSystem.Threading;
7-
usingSystem.Threading.Tasks;
8-
usingWindows.ApplicationModel.DataTransfer;
91
usingCoder.Desktop.App.Services;
102
usingCoder.Desktop.App.Utils;
113
usingCoder.Desktop.CoderSdk;
@@ -18,15 +10,24 @@
1810
usingMicrosoft.UI.Xaml;
1911
usingMicrosoft.UI.Xaml.Controls;
2012
usingMicrosoft.UI.Xaml.Controls.Primitives;
13+
usingSystem;
14+
usingSystem.Collections.Generic;
15+
usingSystem.Collections.ObjectModel;
16+
usingSystem.ComponentModel;
17+
usingSystem.Linq;
18+
usingSystem.Text;
19+
usingSystem.Threading;
20+
usingSystem.Threading.Tasks;
21+
usingSystem.Xml.Linq;
22+
usingWindows.ApplicationModel.DataTransfer;
2123

2224
namespaceCoder.Desktop.App.ViewModels;
2325

2426
publicinterfaceIAgentViewModelFactory
2527
{
2628
publicAgentViewModelCreate(IAgentExpanderHostexpanderHost,Uuidid,stringfullyQualifiedDomainName,
27-
stringhostnameSuffix,
28-
AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,string?workspaceName);
29-
29+
stringhostnameSuffix,AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,
30+
string?workspaceName,bool?didP2p,string?preferredDerp,TimeSpan?latency,TimeSpan?preferredDerpLatency,DateTime?lastHandshake);
3031
publicAgentViewModelCreateDummy(IAgentExpanderHostexpanderHost,Uuidid,
3132
stringhostnameSuffix,
3233
AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,stringworkspaceName);
@@ -40,8 +41,11 @@ public class AgentViewModelFactory(
4041
{
4142
publicAgentViewModelCreate(IAgentExpanderHostexpanderHost,Uuidid,stringfullyQualifiedDomainName,
4243
stringhostnameSuffix,
43-
AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,string?workspaceName)
44+
AgentConnectionStatusconnectionStatus,UridashboardBaseUrl,
45+
string?workspaceName,bool?didP2p,string?preferredDerp,TimeSpan?latency,TimeSpan?preferredDerpLatency,
46+
DateTime?lastHandshake)
4447
{
48+
System.Diagnostics.Debug.WriteLine($"Creating agent:{didP2p}{preferredDerp}{latency}{lastHandshake}");
4549
returnnewAgentViewModel(childLogger,coderApiClientFactory,credentialManager,agentAppViewModelFactory,
4650
expanderHost,id)
4751
{
@@ -51,6 +55,11 @@ public AgentViewModel Create(IAgentExpanderHost expanderHost, Uuid id, string fu
5155
ConnectionStatus=connectionStatus,
5256
DashboardBaseUrl=dashboardBaseUrl,
5357
WorkspaceName=workspaceName,
58+
DidP2p=didP2p,
59+
PreferredDerp=preferredDerp,
60+
Latency=latency,
61+
PreferredDerpLatency=preferredDerpLatency,
62+
LastHandshake=lastHandshake,
5463
};
5564
}
5665

@@ -73,10 +82,10 @@ public AgentViewModel CreateDummy(IAgentExpanderHost expanderHost, Uuid id,
7382

7483
publicenumAgentConnectionStatus
7584
{
76-
Green,
77-
Yellow,
78-
Red,
79-
Gray,
85+
Healthy,
86+
Unhealthy,
87+
NoRecentHandshake,
88+
Offline,
8089
}
8190

8291
publicpartialclassAgentViewModel:ObservableObject,IModelUpdateable<AgentViewModel>
@@ -182,6 +191,75 @@ public string FullyQualifiedDomainName
182191
[NotifyPropertyChangedFor(nameof(ExpandAppsMessage))]
183192
publicpartialboolAppFetchErrored{get;set;}=false;
184193

194+
[ObservableProperty]
195+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
196+
publicpartialbool?DidP2p{get;set;}=false;
197+
198+
[ObservableProperty]
199+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
200+
publicpartialstring?PreferredDerp{get;set;}=null;
201+
202+
[ObservableProperty]
203+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
204+
publicpartialTimeSpan?Latency{get;set;}=null;
205+
206+
[ObservableProperty]
207+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
208+
publicpartialTimeSpan?PreferredDerpLatency{get;set;}=null;
209+
210+
[ObservableProperty]
211+
[NotifyPropertyChangedFor(nameof(ConnectionTooltip))]
212+
publicpartialDateTime?LastHandshake{get;set;}=null;
213+
214+
publicstringConnectionTooltip{get
215+
{
216+
vardescription=newStringBuilder();
217+
218+
if(DidP2p!=null&&DidP2p.Value&&Latency!=null)
219+
{
220+
description.Append($"""
221+
You're connected peer-to-peer.
222+
223+
You ↔{Latency.Value.Milliseconds} ms ↔{WorkspaceName}
224+
"""
225+
);
226+
}
227+
elseif(PreferredDerpLatency!=null)
228+
{
229+
description.Append($"""
230+
You're connected through a DERP relay.
231+
We'll switch over to peer-to-peer when available.
232+
233+
Total latency:{PreferredDerpLatency.Value.Milliseconds} ms
234+
"""
235+
);
236+
237+
if(PreferredDerp!=null&&Latency!=null)
238+
{
239+
description.Append($"\nYou ↔{PreferredDerp}:{PreferredDerpLatency.Value.Milliseconds} ms");
240+
241+
varderpToWorkspaceEstimatedLatency=Latency-PreferredDerpLatency;
242+
243+
// Guard against negative values if the two readings were taken at different times
244+
if(derpToWorkspaceEstimatedLatency>TimeSpan.Zero)
245+
{
246+
description.Append($"\n{PreferredDerp} ms ↔{WorkspaceName}:{derpToWorkspaceEstimatedLatency.Value.Milliseconds} ms");
247+
}
248+
}
249+
}
250+
if(LastHandshake!=null)
251+
description.Append($"\n\nLast handshake:{LastHandshake?.ToString()??"Unknown"}");
252+
253+
vartooltip=description.ToString().TrimEnd('\n',' ');
254+
255+
if(string.IsNullOrEmpty(tooltip))
256+
return"No connection information available.";
257+
258+
returntooltip;
259+
}
260+
}
261+
262+
185263
// We only show 6 apps max, which fills the entire width of the tray
186264
// window.
187265
publicIEnumerable<AgentAppViewModel>VisibleApps=>Apps.Count>MaxAppsPerRow?Apps.Take(MaxAppsPerRow):Apps;
@@ -192,7 +270,7 @@ public string? ExpandAppsMessage
192270
{
193271
get
194272
{
195-
if(ConnectionStatus==AgentConnectionStatus.Gray)
273+
if(ConnectionStatus==AgentConnectionStatus.Offline)
196274
return"Your workspace is offline.";
197275
if(FetchingApps&&Apps.Count==0)
198276
// Don't show this message if we have any apps already. When
@@ -285,6 +363,16 @@ public bool TryApplyChanges(AgentViewModel model)
285363
DashboardBaseUrl=model.DashboardBaseUrl;
286364
if(WorkspaceName!=model.WorkspaceName)
287365
WorkspaceName=model.WorkspaceName;
366+
if(DidP2p!=model.DidP2p)
367+
DidP2p=model.DidP2p;
368+
if(PreferredDerp!=model.PreferredDerp)
369+
PreferredDerp=model.PreferredDerp;
370+
if(Latency!=model.Latency)
371+
Latency=model.Latency;
372+
if(PreferredDerpLatency!=model.PreferredDerpLatency)
373+
PreferredDerpLatency=model.PreferredDerpLatency;
374+
if(LastHandshake!=model.LastHandshake)
375+
LastHandshake=model.LastHandshake;
288376

289377
// Apps are not set externally.
290378

@@ -307,7 +395,7 @@ public void SetExpanded(bool expanded)
307395

308396
partialvoidOnConnectionStatusChanged(AgentConnectionStatusoldValue,AgentConnectionStatusnewValue)
309397
{
310-
if(IsExpanded&&newValueis notAgentConnectionStatus.Gray)FetchApps();
398+
if(IsExpanded&&newValueis notAgentConnectionStatus.Offline)FetchApps();
311399
}
312400

313401
privatevoidFetchApps()
@@ -316,7 +404,7 @@ private void FetchApps()
316404
FetchingApps=true;
317405

318406
// If the workspace is off, then there's no agent and there's no apps.
319-
if(ConnectionStatus==AgentConnectionStatus.Gray)
407+
if(ConnectionStatus==AgentConnectionStatus.Offline)
320408
{
321409
FetchingApps=false;
322410
Apps.Clear();

‎App/ViewModels/TrayWindowViewModel.cs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
usingSystem.Collections.ObjectModel;
44
usingSystem.ComponentModel;
55
usingSystem.Linq;
6+
usingSystem.Security.Principal;
67
usingSystem.Threading.Tasks;
78
usingCoder.Desktop.App.Models;
89
usingCoder.Desktop.App.Services;
@@ -29,6 +30,7 @@ public partial class TrayWindowViewModel : ObservableObject, IAgentExpanderHost
2930
{
3031
privateconstintMaxAgents=5;
3132
privateconststringDefaultDashboardUrl="https://coder.com";
33+
privatereadonlyTimeSpanHealthyPingThreshold=TimeSpan.FromMilliseconds(150);
3234

3335
privatereadonlyIServiceProvider_services;
3436
privatereadonlyIRpcController_rpcController;
@@ -222,12 +224,26 @@ private void UpdateFromRpcModel(RpcModel rpcModel)
222224
if(string.IsNullOrWhiteSpace(fqdn))
223225
continue;
224226

227+
225228
varlastHandshakeAgo=DateTime.UtcNow.Subtract(agent.LastHandshake.ToDateTime());
226-
varconnectionStatus=lastHandshakeAgo<TimeSpan.FromMinutes(5)
227-
?AgentConnectionStatus.Green
228-
:AgentConnectionStatus.Yellow;
229+
230+
// For compatibility with older deployments, we assume that if the
231+
// last ping is null, the agent is healthy.
232+
varisLatencyAcceptable=agent.LastPing!=null?agent.LastPing.Latency.ToTimeSpan()<HealthyPingThreshold:true;
233+
varconnectionStatus=AgentConnectionStatus.Healthy;
234+
if(lastHandshakeAgo>TimeSpan.FromMinutes(5))
235+
{
236+
connectionStatus=AgentConnectionStatus.NoRecentHandshake;
237+
}
238+
elseif(!isLatencyAcceptable)
239+
{
240+
connectionStatus=AgentConnectionStatus.Unhealthy;
241+
}
242+
243+
229244
workspacesWithAgents.Add(agent.WorkspaceId);
230245
varworkspace=rpcModel.Workspaces.FirstOrDefault(w=>w.Id==agent.WorkspaceId);
246+
System.Diagnostics.Debug.WriteLine($"Agent{uuid} LastHandshakeAgo:{lastHandshakeAgo} ConnectionStatus:{connectionStatus} FQDN:{fqdn} Last ping:{agent.LastPing} Last handshake:{agent.LastHandshake}");
231247

232248
agents.Add(_agentViewModelFactory.Create(
233249
this,
@@ -236,7 +252,12 @@ private void UpdateFromRpcModel(RpcModel rpcModel)
236252
_hostnameSuffixGetter.GetCachedSuffix(),
237253
connectionStatus,
238254
credentialModel.CoderUrl,
239-
workspace?.Name));
255+
workspace?.Name,
256+
agent.LastPing?.DidP2P,
257+
agent.LastPing?.PreferredDerp,
258+
agent.LastPing?.Latency?.ToTimeSpan(),
259+
agent.LastPing?.PreferredDerpLatency?.ToTimeSpan(),
260+
agent.LastHandshake?.ToDateTime()));
240261
}
241262

242263
// For every stopped workspace that doesn't have any agents, add a
@@ -253,7 +274,7 @@ private void UpdateFromRpcModel(RpcModel rpcModel)
253274
// conflict with any agent IDs.
254275
uuid,
255276
_hostnameSuffixGetter.GetCachedSuffix(),
256-
AgentConnectionStatus.Gray,
277+
AgentConnectionStatus.Offline,
257278
credentialModel.CoderUrl,
258279
workspace.Name));
259280
}
@@ -268,7 +289,7 @@ private void UpdateFromRpcModel(RpcModel rpcModel)
268289

269290
if(Agents.Count<MaxAgents)ShowAllAgents=false;
270291

271-
varfirstOnlineAgent=agents.FirstOrDefault(a=>a.ConnectionStatus!=AgentConnectionStatus.Gray);
292+
varfirstOnlineAgent=agents.FirstOrDefault(a=>a.ConnectionStatus!=AgentConnectionStatus.Offline);
272293
if(firstOnlineAgentisnull)
273294
_hasExpandedAgent=false;
274295
if(!_hasExpandedAgent&&firstOnlineAgentis notnull)
@@ -433,7 +454,7 @@ private static bool ShouldShowDummy(Workspace workspace)
433454
caseWorkspace.Types.Status.Stopping:
434455
caseWorkspace.Types.Status.Stopped:
435456
returntrue;
436-
// TODO: should we include and show a different color thanGray for workspaces that are
457+
// TODO: should we include and show a different color thanOffline for workspaces that are
437458
// failed, canceled or deleting?
438459
default:
439460
returnfalse;

‎App/Views/Pages/TrayWindowMainPage.xaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,22 +137,22 @@
137137
x:Key="StatusColor"
138138
SelectedKey="{x:Bind Path=ConnectionStatus, Mode=OneWay}">
139139

140-
<converters:StringToBrushSelectorItem>
140+
<converters:StringToBrushSelectorItemKey="Offline">
141141
<converters:StringToBrushSelectorItem.Value>
142142
<SolidColorBrushColor="#8e8e93" />
143143
</converters:StringToBrushSelectorItem.Value>
144144
</converters:StringToBrushSelectorItem>
145-
<converters:StringToBrushSelectorItemKey="Red">
145+
<converters:StringToBrushSelectorItemKey="NoRecentHandshake">
146146
<converters:StringToBrushSelectorItem.Value>
147147
<SolidColorBrushColor="#ff3b30" />
148148
</converters:StringToBrushSelectorItem.Value>
149149
</converters:StringToBrushSelectorItem>
150-
<converters:StringToBrushSelectorItemKey="Yellow">
150+
<converters:StringToBrushSelectorItemKey="Unhealthy">
151151
<converters:StringToBrushSelectorItem.Value>
152152
<SolidColorBrushColor="#ffcc01" />
153153
</converters:StringToBrushSelectorItem.Value>
154154
</converters:StringToBrushSelectorItem>
155-
<converters:StringToBrushSelectorItemKey="Green">
155+
<converters:StringToBrushSelectorItemKey="Healthy">
156156
<converters:StringToBrushSelectorItem.Value>
157157
<SolidColorBrushColor="#34c759" />
158158
</converters:StringToBrushSelectorItem.Value>
@@ -189,6 +189,7 @@
189189
HorizontalAlignment="Right"
190190
VerticalAlignment="Center"
191191
Height="14"Width="14"
192+
ToolTipService.ToolTip="{x:Bind ConnectionTooltip, Mode=OneWay}"
192193
Margin="0,1,0,0">
193194

194195
<Ellipse

‎Vpn.Proto/vpn.proto

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ option go_package = "github.com/coder/coder/v2/vpn";
33
optioncsharp_namespace="Coder.Desktop.Vpn.Proto";
44

55
import"google/protobuf/timestamp.proto";
6+
import"google/protobuf/duration.proto";
67

78
packagevpn;
89

@@ -48,10 +49,10 @@ message TunnelMessage {
4849
messageClientMessage {
4950
RPCrpc=1;
5051
oneofmsg {
51-
StartRequeststart=2;
52-
StopRequeststop=3;
53-
StatusRequeststatus=4;
54-
}
52+
StartRequeststart=2;
53+
StopRequeststop=3;
54+
StatusRequeststatus=4;
55+
}
5556
}
5657

5758
// ServiceMessage is a message from the service (to the client). Windows only.
@@ -131,6 +132,21 @@ message Agent {
131132
// last_handshake is the primary indicator of whether we are connected to a peer. Zero value or
132133
// anything longer than 5 minutes ago means there is a problem.
133134
google.protobuf.Timestamplast_handshake=6;
135+
// If unset, a successful ping has not yet been made.
136+
optionalLastPinglast_ping=7;
137+
}
138+
139+
messageLastPing {
140+
// latency is the RTT of the ping to the agent.
141+
google.protobuf.Durationlatency=1;
142+
// did_p2p indicates whether the ping was sent P2P, or over DERP.
143+
booldid_p2p=2;
144+
// preferred_derp is the human readable name of the preferred DERP region,
145+
// or the region used for the last ping, if it was sent over DERP.
146+
stringpreferred_derp=3;
147+
// preferred_derp_latency is the last known latency to the preferred DERP
148+
// region. Unset if the region does not appear in the DERP map.
149+
optionalgoogle.protobuf.Durationpreferred_derp_latency=4;
134150
}
135151

136152
// NetworkSettingsRequest is based on

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp