Skip to content

Commit 68c481f

Browse files
authored
[management] Move service reload outside transaction in account settings update (#5325)
Bug Fixes Network and DNS updates now defer service and reverse-proxy reloads until after account updates complete, preventing inconsistent proxy state and race conditions. Chores Removed automatic peer/broadcast updates immediately following bulk service reloads. Tests Added a test ensuring network-range changes complete without deadlock.
1 parent 01a9cd4 commit 68c481f

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

management/internals/modules/reverseproxy/manager/manager.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,8 +473,6 @@ func (m *managerImpl) ReloadAllServicesForAccount(ctx context.Context, accountID
473473
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Update, "", m.proxyGRPCServer.GetOIDCValidationConfig()), service.ProxyCluster)
474474
}
475475

476-
m.accountManager.UpdateAccountPeers(ctx, accountID)
477-
478476
return nil
479477
}
480478

management/server/account.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
297297
var oldSettings *types.Settings
298298
var updateAccountPeers bool
299299
var groupChangesAffectPeers bool
300+
var reloadReverseProxy bool
300301

301302
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
302303
var groupsUpdated bool
@@ -327,9 +328,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
327328
if err = am.reallocateAccountPeerIPs(ctx, transaction, accountID, newSettings.NetworkRange); err != nil {
328329
return err
329330
}
330-
if err = am.reverseProxyManager.ReloadAllServicesForAccount(ctx, accountID); err != nil {
331-
log.WithContext(ctx).Warnf("failed to reload all services for account %s: %v", accountID, err)
332-
}
331+
reloadReverseProxy = true
333332
updateAccountPeers = true
334333
}
335334

@@ -394,6 +393,11 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
394393
}
395394
am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountNetworkRangeUpdated, eventMeta)
396395
}
396+
if reloadReverseProxy {
397+
if err = am.reverseProxyManager.ReloadAllServicesForAccount(ctx, accountID); err != nil {
398+
log.WithContext(ctx).Warnf("failed to reload all services for account %s: %v", accountID, err)
399+
}
400+
}
397401

398402
if updateAccountPeers || extraSettingsChanged || groupChangesAffectPeers {
399403
go am.UpdateAccountPeers(ctx, accountID)

management/server/account_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3918,3 +3918,36 @@ func TestAddNewUserToDomainAccountWithoutApproval(t *testing.T) {
39183918
assert.False(t, user.PendingApproval, "User should not be pending approval")
39193919
assert.Equal(t, existingAccountID, user.AccountID)
39203920
}
3921+
3922+
// TestDefaultAccountManager_UpdateAccountSettings_NetworkRangeChange verifies that
3923+
// changing NetworkRange via UpdateAccountSettings does not deadlock.
3924+
// The deadlock occurs because ReloadAllServicesForAccount is called inside a DB
3925+
// transaction but uses the main store connection, which blocks on the transaction lock.
3926+
func TestDefaultAccountManager_UpdateAccountSettings_NetworkRangeChange(t *testing.T) {
3927+
manager, _, err := createManager(t)
3928+
require.NoError(t, err)
3929+
3930+
accountID, err := manager.GetAccountIDByUserID(context.Background(), auth.UserAuth{UserId: userID})
3931+
require.NoError(t, err)
3932+
3933+
ctx := context.Background()
3934+
3935+
// Use a channel to detect if the call completes or hangs
3936+
done := make(chan error, 1)
3937+
go func() {
3938+
_, err := manager.UpdateAccountSettings(ctx, accountID, userID, &types.Settings{
3939+
PeerLoginExpiration: time.Hour,
3940+
PeerLoginExpirationEnabled: true,
3941+
NetworkRange: netip.MustParsePrefix("10.100.0.0/16"),
3942+
Extra: &types.ExtraSettings{},
3943+
})
3944+
done <- err
3945+
}()
3946+
3947+
select {
3948+
case err := <-done:
3949+
require.NoError(t, err, "UpdateAccountSettings should complete without error")
3950+
case <-time.After(10 * time.Second):
3951+
t.Fatal("UpdateAccountSettings deadlocked when changing NetworkRange")
3952+
}
3953+
}

0 commit comments

Comments
 (0)