mirror of
https://github.com/grafana/grafana.git
synced 2024-11-28 19:54:10 -06:00
50244ed4a1
This adds a version of the SQLStore that includes a ReadReplica. The primary DB can be accessed directly - from the caller's standpoint, there is no difference between the SQLStore and ReplStore unless they wish to explicitly call the ReadReplica() and use that for the DB sessions. Currently only the stats service GetSystemStats and GetAdminStats are using the ReadReplica(); if it's misconfigured or if the databaseReadReplica feature flag is not turned on, it will fall back to the usual (SQLStore) behavior. Testing requires a database and read replica - the replication should already be configured. I have been testing this locally with a docker mysql setup (https://medium.com/@vbabak/docker-mysql-master-slave-replication-setup-2ff553fceef2) and the following config: [feature_toggles] databaseReadReplica = true [database] type = mysql name = grafana user = grafana password = password host = 127.0.0.1:3306 [database_replica] type = mysql name = grafana user = grafana password = password host = 127.0.0.1:3307
109 lines
3.8 KiB
Go
109 lines
3.8 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
|
|
"xorm.io/core"
|
|
"xorm.io/xorm"
|
|
|
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/session"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
type DB interface {
|
|
// WithTransactionalDbSession creates a new SQL transaction to ensure consistency
|
|
// for the database operations done within the [sqlstore.DBTransactionFunc].
|
|
// It's better to combine InTransaction and WithDbSession instead, as the context
|
|
// variable is not updated when using this method.
|
|
WithTransactionalDbSession(ctx context.Context, callback sqlstore.DBTransactionFunc) error
|
|
// WithDbSession runs database operations either in an existing transaction available
|
|
// through [context.Context] or if that's not present, as non-transactional database
|
|
// operations.
|
|
WithDbSession(ctx context.Context, callback sqlstore.DBTransactionFunc) error
|
|
// WithNewDbSession behaves like [DB.WithDbSession] without picking up a transaction
|
|
// from the context.
|
|
WithNewDbSession(ctx context.Context, callback sqlstore.DBTransactionFunc) error
|
|
// GetDialect returns an object that contains information about the peculiarities of
|
|
// the particular database type available to the runtime.
|
|
GetDialect() migrator.Dialect
|
|
// GetDBType returns the name of the database type available to the runtime.
|
|
GetDBType() core.DbType
|
|
// GetEngine returns the underlying xorm engine.
|
|
GetEngine() *xorm.Engine
|
|
// GetSqlxSession is an experimental extension to use sqlx instead of xorm to
|
|
// communicate with the database.
|
|
// NOTE: when using this session with mysql, the connection will *not* have:
|
|
// the expected parameters: "&sql_mode='ANSI_QUOTES" and "&parseTime=true"
|
|
// The sqlx session is useful, but be careful not to expect automagic date parsing
|
|
GetSqlxSession() *session.SessionDB
|
|
// InTransaction creates a new SQL transaction that is placed on the context.
|
|
// Use together with [DB.WithDbSession] to run database operations.
|
|
InTransaction(ctx context.Context, fn func(ctx context.Context) error) error
|
|
// Quote wraps an identifier so that it cannot be mistaken for an SQL keyword.
|
|
Quote(value string) string
|
|
// RecursiveQueriesAreSupported runs a dummy recursive query and it returns true
|
|
// if the query runs successfully or false if it fails with mysqlerr.ER_PARSE_ERROR error or any other error
|
|
RecursiveQueriesAreSupported() (bool, error)
|
|
}
|
|
|
|
type Session = sqlstore.DBSession
|
|
type InitTestDBOpt = sqlstore.InitTestDBOpt
|
|
|
|
var SetupTestDB = sqlstore.SetupTestDB
|
|
var CleanupTestDB = sqlstore.CleanupTestDB
|
|
var ProvideService = sqlstore.ProvideService
|
|
|
|
func InitTestDB(t sqlutil.ITestDB, opts ...InitTestDBOpt) *sqlstore.SQLStore {
|
|
db, _ := InitTestDBWithCfg(t, opts...)
|
|
return db
|
|
}
|
|
|
|
func InitTestReplDBWithCfg(t sqlutil.ITestDB, opts ...InitTestDBOpt) (*sqlstore.ReplStore, *setting.Cfg) {
|
|
return sqlstore.InitTestReplDB(t, opts...)
|
|
}
|
|
|
|
func InitTestReplDB(t sqlutil.ITestDB, opts ...InitTestDBOpt) *sqlstore.ReplStore {
|
|
db, _ := InitTestReplDBWithCfg(t, opts...)
|
|
return db
|
|
}
|
|
|
|
func InitTestDBWithCfg(t sqlutil.ITestDB, opts ...InitTestDBOpt) (*sqlstore.SQLStore, *setting.Cfg) {
|
|
return sqlstore.InitTestDB(t, opts...)
|
|
}
|
|
|
|
func IsTestDbSQLite() bool {
|
|
if db, present := os.LookupEnv("GRAFANA_TEST_DB"); !present || db == "sqlite" {
|
|
return true
|
|
}
|
|
|
|
return !IsTestDbMySQL() && !IsTestDbPostgres()
|
|
}
|
|
|
|
func IsTestDbMySQL() bool {
|
|
if db, present := os.LookupEnv("GRAFANA_TEST_DB"); present {
|
|
return db == migrator.MySQL
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func IsTestDbPostgres() bool {
|
|
if db, present := os.LookupEnv("GRAFANA_TEST_DB"); present {
|
|
return db == migrator.Postgres
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func IsTestDBMSSQL() bool {
|
|
if db, present := os.LookupEnv("GRAFANA_TEST_DB"); present {
|
|
return db == migrator.MSSQL
|
|
}
|
|
|
|
return false
|
|
}
|