contribute/backend/database.md
Grafana uses databases to persist settings between restarts. If you don't specify one, Grafana creates a SQLite3 database file on your local disk. This guide explains how to store and retrieve data from the default or other databases.
Grafana supports the following databases:
Grafana uses the XORM framework for persisting objects to the database. For more information on how to use XORM, refer to the documentation.
Services don't use XORM directly. Instead, services use the SQL store, a special type of service that provides an abstraction for the database layer. There are two ways of using the sqlstore: using sqlstore handlers, and using the SQLStore instance.
sqlstore handlersDeprecated: We are deprecating
sqlstorehandlers in favor of using theSQLStoreobject directly in each service. Since most services still use thesqlstorehandlers, we still want to explain how they work.
The sqlstore package allows you to register command handlers that either store or retrieve objects from the database. The sqlstore handlers are similar to services:
sqlstore handlers are command handlers that access the database.sqlstore handlerDeprecated: Refer to the deprecation note for
sqlstorehandlers.
To register a handler:
myrepo.go, in the sqlstore package.init function:func init() {
bus.AddHandlerCtx("sql", DeleteDashboard)
}
func DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
return inTransactionCtx(ctx, func(sess *DBSession) error {
_, err := sess.Exec("DELETE FROM dashboards WHERE dashboard_id=?", cmd.DashboardID)
return err
})
}
Here, inTransactionCtx is a helper function in the sqlstore package that provides a session, that lets you execute SQL statements.
SQLStoreAs opposed to a sqlstore handler, the SQLStore is a service itself. Like the handler, the SQLStore is responsible for storing and retrieving objects, to and from the database.
To use the SQLStore, inject it in your service struct:
type MyService struct {
SQLStore *sqlstore.SQLStore `inject:""`
}
You can now make SQL queries in any of your command handlers or event listeners:
func (s *MyService) DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
if err := s.SQLStore.WithDbSession(ctx, func(sess *db.Session) error {
_, err := sess.Exec("DELETE FROM dashboards WHERE dashboard_id=?", cmd.DashboardID)
return err
})
}
For transactions, use the WithTransactionalDbSession method instead.
As your use of Grafana evolves, you may need to create schema migrations for one or more database tables.
To see all the types of migrations you can add, refer to migrations.go.
Before you add a migration, make sure that you:
main.Treat a new migration as a last resort. Resources are moving to the app platform, so schema you add to the legacy SQL store is a dead end that has to be migrated again later. On top of that, every migration runs once on every Grafana instance that upgrades, against databases of every size that we don't control, and it can never be rolled back or edited afterwards — the only way to correct it is another migration. Prefer a change that leaves the schema alone.
Because of that, the set of migrations registered by the migrations package is pinned to a golden file, testdata/migration_ids.txt. Adding a migration fails TestOSSMigrationIDsGolden until you regenerate it:
go test ./pkg/services/sqlstore/migrations/ -run TestOSSMigrationIDsGolden -update-golden
Important: A human must run this command. Coding agents must not regenerate the golden file on their own, and must instead stop and ask. The file exists so that a person consciously decided the migration is warranted; an agent quietly regenerating it defeats the whole purpose.
Commit the regenerated file together with your migration, so that the new migration shows up in the pull request diff and gets reviewed deliberately.
Add a migration using one of the following methods:
migrations package.DatabaseMigrator for the service.Important: If there are previous migrations for a service, use that method. Don't add migrations using both methods or you risk running migrations in the wrong order.
migrations packageMost services have their migrations located in the migrations package.
To add a migration:
Open the migrations.go file.
In the AddMigration method, find the addXxxMigration function for the service you want to create a migration for.
At the end of the addXxxMigration function, register your migration (refer to the following example).
Note: We no longer recommend putting migrations behind feature flags because this could cause the migration to skip integration testing.
DatabaseMigratorDuring initialization, SQL store queries the service registry, and runs migrations for every service that implements the DatabaseMigrator interface.
To add a migration:
AddMigration(mg *migrator.Migrator) method to the service.AddMigration method, register your migration:func (s *MyService) AddMigration(mg *migrator.Migrator) {
// ...
mg.AddMigration("Add column age", NewAddColumnMigration(table, &Column{
Name: "age",
Type: migrator.DB_BigInt,
Nullable: true,
}))
}