diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index e0ad2eb07bc..8eee211ff9e 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -69,9 +69,19 @@ type vplayer struct { throttlerAppName string - foreignKeyCheck bool + // See updateFKCheck for more details on how the two fields below are used. + + // foreignKeyChecksEnabled is the current state of the foreign key checks for the current session. + // It reflects what we have set the @@session.foreign_key_checks session variable to. + foreignKeyChecksEnabled bool + + // foreignKeyChecksStateInitialized is set to true once we have initialized the foreignKeyChecksEnabled. + // The initialization is done on the first row event that this vplayer sees. + foreignKeyChecksStateInitialized bool } +// NoForeignKeyCheckFlagBitmask is the bitmask for the 2nd bit (least significant) of the flags in a binlog row event. +// This bit is set if foreign key checks are disabled. const NoForeignKeyCheckFlagBitmask uint32 = 1 << 1 // newVPlayer creates a new vplayer. Parameters: @@ -105,7 +115,6 @@ func newVPlayer(vr *vreplicator, settings binlogplayer.VRSettings, copyState map tablePlans: make(map[string]*TablePlan), phase: phase, throttlerAppName: throttlerapp.VCopierName.ConcatenateString(vr.throttlerAppName()), - foreignKeyCheck: false, } } @@ -138,23 +147,32 @@ func (vp *vplayer) play(ctx context.Context) error { return vp.fetchAndApply(ctx) } -// updateFKCheck will check if the fk checks value has changed from what it is currently set to and update it if it has. -// This is done to avoid setting the fk checks value for every row event. vplayer starts with fk checks off. +// updateFKCheck updates the @@session.foreign_key_checks variable based on the binlog row event flags. +// The function only does it if it has changed to avoid redundant updates, using the cached vplayer.foreignKeyChecksEnabled +// The foreign_key_checks value for a transaction is determined by the 2nd bit (least significant) of the flags: +// - If set (1), foreign key checks are disabled. +// - If unset (0), foreign key checks are enabled. +// updateFKCheck also updates the state for the first row event that this vplayer and hence the connection sees. func (vp *vplayer) updateFKCheck(ctx context.Context, flags2 uint32) error { - // The 2nd bit (least significant) of the flags attribute of the binlog row event - // is set to 1 if foreign key checks are disabled. - enableFK := true + dbForeignKeyChecksEnabled := true if flags2&NoForeignKeyCheckFlagBitmask == NoForeignKeyCheckFlagBitmask { - enableFK = false + dbForeignKeyChecksEnabled = false } - if vp.foreignKeyCheck == enableFK { - // if not changed, return + + if vp.foreignKeyChecksStateInitialized /* already set earlier */ && + dbForeignKeyChecksEnabled == vp.foreignKeyChecksEnabled /* no change in the state, no need to update */ { return nil } - vp.foreignKeyCheck = enableFK - log.Infof("Setting foreign_key_checks to %v", enableFK) - _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, "set @@session.foreign_key_checks="+strconv.FormatBool(enableFK)) - return err + log.Infof("Setting this session's foreign_key_checks to %s", strconv.FormatBool(dbForeignKeyChecksEnabled)) + if _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, "set @@session.foreign_key_checks="+strconv.FormatBool(dbForeignKeyChecksEnabled)); err != nil { + return fmt.Errorf("failed to set session foreign_key_checks: %w", err) + } + vp.foreignKeyChecksEnabled = dbForeignKeyChecksEnabled + if !vp.foreignKeyChecksStateInitialized { + log.Infof("First foreign_key_checks update to: %s", strconv.FormatBool(dbForeignKeyChecksEnabled)) + vp.foreignKeyChecksStateInitialized = true + } + return nil } // fetchAndApply performs the fetching and application of the binlogs.