[management] remove GLOBAL when disabling foreign keys on mysql (#4615)

This commit is contained in:
Pascal Fischer
2025-11-07 16:03:14 +01:00
committed by GitHub
parent 98ddac07bf
commit dbfc8a52c9

View File

@@ -85,12 +85,7 @@ func NewSqlStore(ctx context.Context, db *gorm.DB, storeEngine types.Engine, met
conns = runtime.NumCPU()
}
switch storeEngine {
case types.MysqlStoreEngine:
if err := db.Exec("SET GLOBAL FOREIGN_KEY_CHECKS = 0").Error; err != nil {
return nil, err
}
case types.SqliteStoreEngine:
if storeEngine == types.SqliteStoreEngine {
if err == nil {
log.WithContext(ctx).Warnf("setting NB_SQL_MAX_OPEN_CONNS is not supported for sqlite, using default value 1")
}
@@ -175,7 +170,7 @@ func (s *SqlStore) SaveAccount(ctx context.Context, account *types.Account) erro
group.StoreGroupPeers()
}
err := s.db.Transaction(func(tx *gorm.DB) error {
err := s.transaction(func(tx *gorm.DB) error {
result := tx.Select(clause.Associations).Delete(account.Policies, "account_id = ?", account.Id)
if result.Error != nil {
return result.Error
@@ -270,7 +265,7 @@ func (s *SqlStore) checkAccountDomainBeforeSave(ctx context.Context, accountID,
func (s *SqlStore) DeleteAccount(ctx context.Context, account *types.Account) error {
start := time.Now()
err := s.db.Transaction(func(tx *gorm.DB) error {
err := s.transaction(func(tx *gorm.DB) error {
result := tx.Select(clause.Associations).Delete(account.Policies, "account_id = ?", account.Id)
if result.Error != nil {
return result.Error
@@ -324,7 +319,7 @@ func (s *SqlStore) SavePeer(ctx context.Context, accountID string, peer *nbpeer.
peerCopy := peer.Copy()
peerCopy.AccountID = accountID
err := s.db.Transaction(func(tx *gorm.DB) error {
err := s.transaction(func(tx *gorm.DB) error {
// check if peer exists before saving
var peerID string
result := tx.Model(&nbpeer.Peer{}).Select("id").Take(&peerID, accountAndIDQueryCondition, accountID, peer.ID)
@@ -613,7 +608,7 @@ func (s *SqlStore) GetUserByUserID(ctx context.Context, lockStrength LockingStre
}
func (s *SqlStore) DeleteUser(ctx context.Context, accountID, userID string) error {
err := s.db.Transaction(func(tx *gorm.DB) error {
err := s.transaction(func(tx *gorm.DB) error {
result := tx.Delete(&types.PersonalAccessToken{}, "user_id = ?", userID)
if result.Error != nil {
return result.Error
@@ -2932,6 +2927,16 @@ func (s *SqlStore) ExecuteInTransaction(ctx context.Context, operation func(stor
if tx.Error != nil {
return tx.Error
}
// For MySQL, disable FK checks within this transaction to avoid deadlocks
// This is session-scoped and doesn't require SUPER privileges
if s.storeEngine == types.MysqlStoreEngine {
if err := tx.Exec("SET FOREIGN_KEY_CHECKS = 0").Error; err != nil {
tx.Rollback()
return fmt.Errorf("failed to disable FK checks: %w", err)
}
}
repo := s.withTx(tx)
err := operation(repo)
if err != nil {
@@ -2939,6 +2944,14 @@ func (s *SqlStore) ExecuteInTransaction(ctx context.Context, operation func(stor
return err
}
// Re-enable FK checks before commit (optional, as transaction end resets it)
if s.storeEngine == types.MysqlStoreEngine {
if err := tx.Exec("SET FOREIGN_KEY_CHECKS = 1").Error; err != nil {
tx.Rollback()
return fmt.Errorf("failed to re-enable FK checks: %w", err)
}
}
err = tx.Commit().Error
log.WithContext(ctx).Tracef("transaction took %v", time.Since(startTime))
@@ -2956,6 +2969,31 @@ func (s *SqlStore) withTx(tx *gorm.DB) Store {
}
}
// transaction wraps a GORM transaction with MySQL-specific FK checks handling
// Use this instead of db.Transaction() directly to avoid deadlocks on MySQL/Aurora
func (s *SqlStore) transaction(fn func(*gorm.DB) error) error {
return s.db.Transaction(func(tx *gorm.DB) error {
// For MySQL, disable FK checks within this transaction to avoid deadlocks
// This is session-scoped and doesn't require SUPER privileges
if s.storeEngine == types.MysqlStoreEngine {
if err := tx.Exec("SET FOREIGN_KEY_CHECKS = 0").Error; err != nil {
return fmt.Errorf("failed to disable FK checks: %w", err)
}
}
err := fn(tx)
// Re-enable FK checks before commit (optional, as transaction end resets it)
if s.storeEngine == types.MysqlStoreEngine && err == nil {
if fkErr := tx.Exec("SET FOREIGN_KEY_CHECKS = 1").Error; fkErr != nil {
return fmt.Errorf("failed to re-enable FK checks: %w", fkErr)
}
}
return err
})
}
func (s *SqlStore) GetDB() *gorm.DB {
return s.db
}
@@ -3212,7 +3250,7 @@ func (s *SqlStore) SavePolicy(ctx context.Context, policy *types.Policy) error {
}
func (s *SqlStore) DeletePolicy(ctx context.Context, accountID, policyID string) error {
return s.db.Transaction(func(tx *gorm.DB) error {
return s.transaction(func(tx *gorm.DB) error {
if err := tx.Where("policy_id = ?", policyID).Delete(&types.PolicyRule{}).Error; err != nil {
return fmt.Errorf("delete policy rules: %w", err)
}