mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-19 15:19:55 +00:00
[client/ui] Add async Up mode to avoid blocking profile switches
The daemon's Up RPC previously always blocked in waitForUp (up to 50s) until the engine connected. The UI does not need this — status updates already flow through the SubscribeStatus stream. Add bool async = 4 to UpRequest. When true the daemon starts connectWithRetryRuns and returns immediately; the CLI path (async=false, the default) is unchanged. ProfileSwitcher.SwitchActive now sets Async:true so all three RPCs (Status, Switch, Down, Up) return quickly. The background goroutine and its associated race condition are removed entirely.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.36.6
|
// protoc-gen-go v1.36.6
|
||||||
// protoc v7.34.1
|
// protoc v3.21.12
|
||||||
// source: daemon.proto
|
// source: daemon.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
@@ -823,9 +823,15 @@ func (x *WaitSSOLoginResponse) GetEmail() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UpRequest struct {
|
type UpRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
ProfileName *string `protobuf:"bytes,1,opt,name=profileName,proto3,oneof" json:"profileName,omitempty"`
|
ProfileName *string `protobuf:"bytes,1,opt,name=profileName,proto3,oneof" json:"profileName,omitempty"`
|
||||||
Username *string `protobuf:"bytes,2,opt,name=username,proto3,oneof" json:"username,omitempty"`
|
Username *string `protobuf:"bytes,2,opt,name=username,proto3,oneof" json:"username,omitempty"`
|
||||||
|
// async instructs the daemon to start the connection attempt and return
|
||||||
|
// immediately without waiting for the engine to become ready. Status updates
|
||||||
|
// are delivered via the SubscribeStatus stream. When false (the default) the
|
||||||
|
// RPC blocks until the engine is running or gives up, which is the behaviour
|
||||||
|
// needed by the CLI.
|
||||||
|
Async bool `protobuf:"varint,4,opt,name=async,proto3" json:"async,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -874,6 +880,13 @@ func (x *UpRequest) GetUsername() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *UpRequest) GetAsync() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Async
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type UpResponse struct {
|
type UpResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
@@ -6309,10 +6322,11 @@ const file_daemon_proto_rawDesc = "" +
|
|||||||
"\buserCode\x18\x01 \x01(\tR\buserCode\x12\x1a\n" +
|
"\buserCode\x18\x01 \x01(\tR\buserCode\x12\x1a\n" +
|
||||||
"\bhostname\x18\x02 \x01(\tR\bhostname\",\n" +
|
"\bhostname\x18\x02 \x01(\tR\bhostname\",\n" +
|
||||||
"\x14WaitSSOLoginResponse\x12\x14\n" +
|
"\x14WaitSSOLoginResponse\x12\x14\n" +
|
||||||
"\x05email\x18\x01 \x01(\tR\x05email\"v\n" +
|
"\x05email\x18\x01 \x01(\tR\x05email\"\x8c\x01\n" +
|
||||||
"\tUpRequest\x12%\n" +
|
"\tUpRequest\x12%\n" +
|
||||||
"\vprofileName\x18\x01 \x01(\tH\x00R\vprofileName\x88\x01\x01\x12\x1f\n" +
|
"\vprofileName\x18\x01 \x01(\tH\x00R\vprofileName\x88\x01\x01\x12\x1f\n" +
|
||||||
"\busername\x18\x02 \x01(\tH\x01R\busername\x88\x01\x01B\x0e\n" +
|
"\busername\x18\x02 \x01(\tH\x01R\busername\x88\x01\x01\x12\x14\n" +
|
||||||
|
"\x05async\x18\x04 \x01(\bR\x05asyncB\x0e\n" +
|
||||||
"\f_profileNameB\v\n" +
|
"\f_profileNameB\v\n" +
|
||||||
"\t_usernameJ\x04\b\x03\x10\x04\"\f\n" +
|
"\t_usernameJ\x04\b\x03\x10\x04\"\f\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
|||||||
@@ -233,6 +233,12 @@ message UpRequest {
|
|||||||
optional string profileName = 1;
|
optional string profileName = 1;
|
||||||
optional string username = 2;
|
optional string username = 2;
|
||||||
reserved 3;
|
reserved 3;
|
||||||
|
// async instructs the daemon to start the connection attempt and return
|
||||||
|
// immediately without waiting for the engine to become ready. Status updates
|
||||||
|
// are delivered via the SubscribeStatus stream. When false (the default) the
|
||||||
|
// RPC blocks until the engine is running or gives up, which is the behaviour
|
||||||
|
// needed by the CLI.
|
||||||
|
bool async = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpResponse {}
|
message UpResponse {}
|
||||||
|
|||||||
@@ -747,6 +747,9 @@ func (s *Server) Up(callerCtx context.Context, msg *proto.UpRequest) (*proto.UpR
|
|||||||
go s.connectWithRetryRuns(ctx, s.config, s.statusRecorder, s.clientRunningChan, s.clientGiveUpChan)
|
go s.connectWithRetryRuns(ctx, s.config, s.statusRecorder, s.clientRunningChan, s.clientGiveUpChan)
|
||||||
|
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
if msg.GetAsync() {
|
||||||
|
return &proto.UpResponse{}, nil
|
||||||
|
}
|
||||||
return s.waitForUp(callerCtx)
|
return s.waitForUp(callerCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ type WaitSSOParams struct {
|
|||||||
type UpParams struct {
|
type UpParams struct {
|
||||||
ProfileName string `json:"profileName"`
|
ProfileName string `json:"profileName"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
// Async instructs the daemon to start the connection and return
|
||||||
|
// immediately. Status updates flow via the SubscribeStatus stream.
|
||||||
|
// When false (the default) the RPC blocks until connected, which is
|
||||||
|
// the CLI behaviour.
|
||||||
|
Async bool `json:"async"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogoutParams selects the profile the daemon should log out.
|
// LogoutParams selects the profile the daemon should log out.
|
||||||
@@ -147,7 +152,7 @@ func (s *Connection) Up(ctx context.Context, p UpParams) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req := &proto.UpRequest{}
|
req := &proto.UpRequest{Async: p.Async}
|
||||||
if p.ProfileName != "" {
|
if p.ProfileName != "" {
|
||||||
req.ProfileName = ptrStr(p.ProfileName)
|
req.ProfileName = ptrStr(p.ProfileName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,9 @@ func NewProfileSwitcher(profiles *Profiles, connection *Connection, peers *Peers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SwitchActive switches to the named profile applying the reconnect policy.
|
// SwitchActive switches to the named profile applying the reconnect policy.
|
||||||
// It returns after the Switch RPC completes so the caller can refresh its UI
|
// All RPCs complete quickly: Up uses async mode so the daemon starts the
|
||||||
// immediately; Down and Up run in a background goroutine.
|
// connection attempt and returns immediately; status updates flow via the
|
||||||
|
// SubscribeStatus stream.
|
||||||
func (s *ProfileSwitcher) SwitchActive(ctx context.Context, p ProfileRef) error {
|
func (s *ProfileSwitcher) SwitchActive(ctx context.Context, p ProfileRef) error {
|
||||||
prevStatus := ""
|
prevStatus := ""
|
||||||
if st, err := s.peers.Get(ctx); err == nil {
|
if st, err := s.peers.Get(ctx); err == nil {
|
||||||
@@ -61,22 +62,21 @@ func (s *ProfileSwitcher) SwitchActive(ctx context.Context, p ProfileRef) error
|
|||||||
return fmt.Errorf("switch profile %q: %w", p.ProfileName, err)
|
return fmt.Errorf("switch profile %q: %w", p.ProfileName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
if needsDown {
|
||||||
bgCtx := context.Background()
|
if err := s.connection.Down(ctx); err != nil {
|
||||||
if needsDown {
|
log.Errorf("profileswitcher: Down: %v", err)
|
||||||
if err := s.connection.Down(bgCtx); err != nil {
|
|
||||||
log.Errorf("profileswitcher: Down: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if wasActive {
|
}
|
||||||
if err := s.connection.Up(bgCtx, UpParams{
|
|
||||||
ProfileName: p.ProfileName,
|
if wasActive {
|
||||||
Username: p.Username,
|
if err := s.connection.Up(ctx, UpParams{
|
||||||
}); err != nil {
|
ProfileName: p.ProfileName,
|
||||||
log.Errorf("profileswitcher: Up %s: %v", p.ProfileName, err)
|
Username: p.Username,
|
||||||
}
|
Async: true,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("reconnect %q: %w", p.ProfileName, err)
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user