Skip to content

Commit

Permalink
MySQL driver: on connect try setting wsrep_sync_wait, swallow error 1193
Browse files Browse the repository at this point in the history
In Galera clusters wsrep_sync_wait=7 lets statements catch up all
pending sync between nodes first. This way new child rows await fresh parent
ones from other nodes not to run into foreign key errors. MySQL single nodes
will reject this with error 1193 "Unknown system variable" which is OK.
  • Loading branch information
Al2Klimov authored and yhabteab committed Mar 22, 2024
1 parent e9021a5 commit f80bdce
Showing 1 changed file with 38 additions and 1 deletion.
39 changes: 38 additions & 1 deletion pkg/config/database.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package config

import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"github.com/go-sql-driver/mysql"
icingadbDriver "github.com/icinga/icingadb/pkg/driver"
Expand All @@ -22,6 +24,8 @@ import (

var registerDriverOnce sync.Once

var errUnknownSysVar = &mysql.MySQLError{Number: 1193}

// Database defines database client configuration.
type Database struct {
Type string `yaml:"type" default:"mysql"`
Expand Down Expand Up @@ -82,7 +86,40 @@ func (d *Database) Open(logger *logging.Logger) (*icingadb.DB, error) {
return nil, errors.Wrap(err, "can't open mysql database")
}

connector := &icingadbDriver.RetryConnector{Connector: c, SqlDriver: &mysql.MySQLDriver{}, Logger: logger}
wsrepSyncWait := int64(d.Options.WsrepSyncWait)

// Setting "wsrep_sync_wait" for each session ensures that causality checks are performed before execution
// and that each statement is executed on a fully synchronized node. Doing so prevents foreign key violation
// when inserting into dependent tables on different MariaDB/MySQL nodes. When using MySQL single nodes, the
// "SET SESSION" command will fail with "Unknown system variable (1193)" and will therefore be silently dropped.
// https://mariadb.com/kb/en/galera-cluster-system-variables/#wsrep_sync_wait
var setGaleraOpts = func(ctx context.Context, conn driver.Conn) error {
const galeraOpts = "SET SESSION wsrep_sync_wait=?"

stmt, err := conn.(driver.ConnPrepareContext).PrepareContext(ctx, galeraOpts)
if err != nil {
err = errors.Wrap(err, "can't prepare "+galeraOpts)
} else {
_, err = stmt.(driver.StmtExecContext).ExecContext(ctx, []driver.NamedValue{{Value: wsrepSyncWait}})
if err != nil {
err = errors.Wrap(err, "can't execute "+galeraOpts)
}
}

if errors.Is(err, errUnknownSysVar) {
err = nil
}

if stmt != nil {
if errClose := stmt.Close(); errClose != nil && err == nil {
err = errors.Wrap(errClose, "can't close statement "+galeraOpts)
}
}

return err
}

connector := &icingadbDriver.RetryConnector{Connector: c, SqlDriver: &mysql.MySQLDriver{}, Logger: logger, InitConn: setGaleraOpts}
db = sqlx.NewDb(sql.OpenDB(connector), icingadbDriver.MySQL)
case "pgsql":
uri := &url.URL{
Expand Down

0 comments on commit f80bdce

Please sign in to comment.