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 20, 2024
1 parent 28058d9 commit bb74479
Showing 1 changed file with 41 additions and 1 deletion.
42 changes: 41 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,43 @@ 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)

// setGaleraOpts tries SET SESSION wsrep_sync_wait.
//
// This ensures causality checks will take place before execution,
// ensuring that every statement is executed on a fully synced node.
// https://mariadb.com/kb/en/galera-cluster-system-variables/#wsrep_sync_wait
//
// It prevents running into foreign key errors while inserting into linked tables on different MySQL nodes.
// Error 1193 "Unknown system variable" is ignored to support MySQL single nodes.
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 bb74479

Please sign in to comment.