diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e7b06ca3a2e..98fb6d6cbcb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -178,6 +178,7 @@ /devenv/docker/blocks/influxdb1/ @grafana/observability-metrics /devenv/docker/blocks/jaeger/ @grafana/observability-traces-and-profiling /devenv/docker/blocks/maildev/ @grafana/alerting-squad-frontend +/devenv/docker/blocks/mariadb/ @grafana/grafana-bi-squad /devenv/docker/blocks/memcached/ @grafana/backend-platform /devenv/docker/blocks/mssql/ @grafana/grafana-bi-squad /devenv/docker/blocks/mssql_arm64/ @grafana/grafana-bi-squad diff --git a/devenv/docker/blocks/mariadb/docker-compose.yaml b/devenv/docker/blocks/mariadb/docker-compose.yaml new file mode 100644 index 00000000000..d475152eca1 --- /dev/null +++ b/devenv/docker/blocks/mariadb/docker-compose.yaml @@ -0,0 +1,10 @@ + mariadb: + image: mariadb:latest + restart: always + environment: + MARIADB_ROOT_PASSWORD: rootpass + MARIADB_DATABASE: grafana + MARIADB_USER: grafana + MARIADB_PASSWORD: password + ports: + - "3306:3306" diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index b2dd6e5d860..efab13da66b 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -298,7 +298,7 @@ func (ss *SQLStore) buildConnectionString() (string, error) { if isolation := ss.dbCfg.IsolationLevel; isolation != "" { val := url.QueryEscape(fmt.Sprintf("'%s'", isolation)) - cnnstr += fmt.Sprintf("&tx_isolation=%s", val) + cnnstr += fmt.Sprintf("&transaction_isolation=%s", val) } if ss.Cfg.IsFeatureToggleEnabled(featuremgmt.FlagMysqlAnsiQuotes) || ss.Cfg.IsFeatureToggleEnabled(featuremgmt.FlagNewDBLibrary) { @@ -402,6 +402,14 @@ func (ss *SQLStore) initEngine(engine *xorm.Engine) error { if err != nil { return err } + // Only for MySQL or MariaDB, verify we can connect with the current connection string's system var for transaction isolation. + // If not, create a new engine with a compatible connection string. + if ss.dbCfg.Type == migrator.MySQL { + engine, err = ss.ensureTransactionIsolationCompatibility(engine, connectionString) + if err != nil { + return err + } + } } engine.SetMaxOpenConns(ss.dbCfg.MaxOpenConn) @@ -423,6 +431,32 @@ func (ss *SQLStore) initEngine(engine *xorm.Engine) error { return nil } +// The transaction_isolation system variable isn't compatible with MySQL < 5.7.20 or MariaDB. If we get an error saying this +// system variable is unknown, then replace it with it's older version tx_isolation which is compatible with MySQL < 5.7.20 and MariaDB. +func (ss *SQLStore) ensureTransactionIsolationCompatibility(engine *xorm.Engine, connectionString string) (*xorm.Engine, error) { + var result string + _, err := engine.SQL("SELECT 1").Get(&result) + + var mysqlError *mysql.MySQLError + if errors.As(err, &mysqlError) { + // if there was an error due to transaction isolation + if strings.Contains(mysqlError.Message, "Unknown system variable 'transaction_isolation'") { + ss.log.Debug("transaction_isolation system var is unknown, overriding in connection string with tx_isolation instead") + // replace with compatible system var for transaction isolation + connectionString = strings.Replace(connectionString, "&transaction_isolation", "&tx_isolation", -1) + // recreate the xorm engine with new connection string that is compatible + engine, err = xorm.NewEngine(ss.dbCfg.Type, connectionString) + if err != nil { + return nil, err + } + } + } else if err != nil { + return nil, err + } + + return engine, nil +} + // readConfig initializes the SQLStore from its configuration. func (ss *SQLStore) readConfig() error { sec := ss.Cfg.Raw.Section("database")