mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-15 17:16:07 +00:00
fix(sync): mark node dirty inside the mutation transaction (atomic ConfigDirty) (#5611)
* fix(sync): mark node dirty inside the mutation transaction ConfigDirty is currently set by MarkNodeDirty AFTER the mutation, on a separate DB handle outside the mutation's transaction. A crash or error between the committed change and the mark leaves a committed config change that never reconciles to the node (silent drift). Add MarkNodeDirtyTx(tx, id) and call it inside each mutation's transaction so the dirty mark commits atomically with the change. * fix(test): initialize DB in TestResolveInboundAddress and group gorm import Two CI failures on this branch: - race (-shuffle=on): TestResolveInboundAddress reaches resolveInboundAddress -> configuredPublicHost -> GetSubDomain, which reads the global DB. The test never initialized one, relying on another sub-package test to do so first; under shuffle it ran first and nil-dereferenced gorm. Call initSubDB(t) so it is self-sufficient (empty DB yields an empty subDomain, so the subscriber-host fallback still holds). - golangci goimports: gorm.io/gorm was grouped with the github.com/mhsanaei/3x-ui local imports in node_dirty_test.go. Move it into the third-party group.
This commit is contained in:
@@ -598,25 +598,19 @@ func (s *ClientService) bulkAdjustInboundClients(
|
||||
res.needRestart = true
|
||||
}
|
||||
|
||||
markDirty := false
|
||||
if oldInbound.NodeID != nil {
|
||||
rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
|
||||
rt, push, _, perr := inboundSvc.nodePushPlan(oldInbound)
|
||||
if perr != nil {
|
||||
for email := range foundEmails {
|
||||
res.perEmailSkipped[email] = perr.Error()
|
||||
delete(foundEmails, email)
|
||||
}
|
||||
} else {
|
||||
if dirty {
|
||||
markDirty = true
|
||||
}
|
||||
if flowChanged {
|
||||
markDirty = true
|
||||
push = false
|
||||
}
|
||||
// Large batches collapse into one reconcile push rather than M updates.
|
||||
if push && len(foundEmails) > nodeBulkPushThreshold {
|
||||
markDirty = true
|
||||
push = false
|
||||
}
|
||||
if push {
|
||||
@@ -632,7 +626,6 @@ func (s *ClientService) bulkAdjustInboundClients(
|
||||
updated.UpdatedAt = nowMs
|
||||
if err1 := rt.UpdateUser(context.Background(), oldInbound, email, updated); err1 != nil {
|
||||
logger.Warning("Error in updating client on", rt.Name(), ":", err1)
|
||||
markDirty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -649,7 +642,13 @@ func (s *ClientService) bulkAdjustInboundClients(
|
||||
if gcErr != nil {
|
||||
return gcErr
|
||||
}
|
||||
return s.SyncInbound(tx, inboundId, finalClients)
|
||||
if err := s.SyncInbound(tx, inboundId, finalClients); err != nil {
|
||||
return err
|
||||
}
|
||||
if oldInbound.NodeID != nil {
|
||||
return (&NodeService{}).MarkNodeDirtyTx(tx, *oldInbound.NodeID)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if txErr != nil {
|
||||
for email := range foundEmails {
|
||||
@@ -657,10 +656,6 @@ func (s *ClientService) bulkAdjustInboundClients(
|
||||
res.perEmailSkipped[email] = txErr.Error()
|
||||
}
|
||||
}
|
||||
} else if markDirty && oldInbound.NodeID != nil {
|
||||
if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
|
||||
logger.Warning("mark node dirty failed:", dErr)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
@@ -973,7 +968,6 @@ func (s *ClientService) bulkDelInboundClients(
|
||||
}
|
||||
}
|
||||
|
||||
markDirty := false
|
||||
if oldInbound.NodeID == nil {
|
||||
rt, rterr := inboundSvc.runtimeFor(oldInbound)
|
||||
if rterr != nil {
|
||||
@@ -995,26 +989,21 @@ func (s *ClientService) bulkDelInboundClients(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
|
||||
rt, push, _, perr := inboundSvc.nodePushPlan(oldInbound)
|
||||
if perr != nil {
|
||||
for email := range foundEmails {
|
||||
res.perEmailSkipped[email] = perr.Error()
|
||||
delete(foundEmails, email)
|
||||
}
|
||||
} else {
|
||||
if dirty {
|
||||
markDirty = true
|
||||
}
|
||||
// Large batches collapse into one reconcile push rather than M deletes.
|
||||
if push && len(foundEmails) > nodeBulkPushThreshold {
|
||||
markDirty = true
|
||||
push = false
|
||||
}
|
||||
if push {
|
||||
for email := range foundEmails {
|
||||
if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
|
||||
logger.Warning("Error in deleting client on", rt.Name(), ":", err1)
|
||||
markDirty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1031,7 +1020,13 @@ func (s *ClientService) bulkDelInboundClients(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.SyncInbound(tx, inboundId, finalClients)
|
||||
if err := s.SyncInbound(tx, inboundId, finalClients); err != nil {
|
||||
return err
|
||||
}
|
||||
if oldInbound.NodeID != nil {
|
||||
return (&NodeService{}).MarkNodeDirtyTx(tx, *oldInbound.NodeID)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if txErr != nil {
|
||||
for email := range foundEmails {
|
||||
@@ -1039,10 +1034,6 @@ func (s *ClientService) bulkDelInboundClients(
|
||||
res.perEmailSkipped[email] = txErr.Error()
|
||||
}
|
||||
}
|
||||
} else if markDirty && oldInbound.NodeID != nil {
|
||||
if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
|
||||
logger.Warning("mark node dirty failed:", dErr)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
@@ -1512,16 +1503,14 @@ func (s *ClientService) bulkSetEnableInboundClients(inboundSvc *InboundService,
|
||||
}
|
||||
oldInbound.Settings = string(newSettings)
|
||||
|
||||
rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
|
||||
rt, push, _, perr := inboundSvc.nodePushPlan(oldInbound)
|
||||
if perr != nil {
|
||||
for _, ch := range changed {
|
||||
res.perEmailSkipped[ch.email] = perr.Error()
|
||||
}
|
||||
return res
|
||||
}
|
||||
markDirty := dirty
|
||||
if oldInbound.NodeID != nil && push && len(changed) > nodeBulkPushThreshold {
|
||||
markDirty = true
|
||||
push = false
|
||||
}
|
||||
|
||||
@@ -1533,7 +1522,13 @@ func (s *ClientService) bulkSetEnableInboundClients(inboundSvc *InboundService,
|
||||
if gcErr != nil {
|
||||
return gcErr
|
||||
}
|
||||
return s.SyncInbound(tx, inboundId, finalClients)
|
||||
if err := s.SyncInbound(tx, inboundId, finalClients); err != nil {
|
||||
return err
|
||||
}
|
||||
if oldInbound.NodeID != nil {
|
||||
return (&NodeService{}).MarkNodeDirtyTx(tx, *oldInbound.NodeID)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if txErr != nil {
|
||||
for _, ch := range changed {
|
||||
@@ -1576,16 +1571,9 @@ func (s *ClientService) bulkSetEnableInboundClients(inboundSvc *InboundService,
|
||||
updated.UpdatedAt = nowMs
|
||||
if err1 := rt.UpdateUser(context.Background(), oldInbound, ch.email, updated); err1 != nil {
|
||||
logger.Warning("Error in updating client on", rt.Name(), ":", err1)
|
||||
markDirty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if markDirty && oldInbound.NodeID != nil {
|
||||
if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
|
||||
logger.Warning("mark node dirty failed:", dErr)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user