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 19, 2024
1 parent dffbb7c commit 534fe0f
Showing 1 changed file with 45 additions and 2 deletions.
47 changes: 45 additions & 2 deletions 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 @@ -20,7 +22,10 @@ import (
"time"
)

var registerDriverOnce sync.Once
var (
registerDriverOnce sync.Once
wsrepSyncWait int64
)

// Database defines database client configuration.
type Database struct {
Expand Down Expand Up @@ -77,12 +82,14 @@ func (d *Database) Open(logger *logging.Logger) (*icingadb.DB, error) {
}
}

wsrepSyncWait = int64(d.Options.WsrepSyncWait)

c, err := mysql.NewConnector(config)
if err != nil {
return nil, errors.Wrap(err, "can't open mysql database")
}

connector := &icingadbDriver.RetryConnector{Connector: c, Logger: logger}
connector := &icingadbDriver.RetryConnector{Connector: c, Logger: logger, InitConn: setGaleraOpts}
db = sqlx.NewDb(sql.OpenDB(connector), icingadbDriver.MySQL)
case "pgsql":
uri := &url.URL{
Expand Down Expand Up @@ -182,3 +189,39 @@ func (d *Database) isUnixAddr() bool {
func unknownDbType(t string) error {
return errors.Errorf(`unknown database type %q, must be one of: "mysql", "pgsql"`, t)
}

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

// 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.
func setGaleraOpts(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 err != nil && 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
}

0 comments on commit 534fe0f

Please sign in to comment.