diff --git a/pkg/api/admin.go b/pkg/api/admin.go index 88a5c165267..e2e0c39eb3a 100644 --- a/pkg/api/admin.go +++ b/pkg/api/admin.go @@ -22,7 +22,7 @@ func (hs *HTTPServer) AdminGetSettings(c *models.ReqContext) response.Response { func AdminGetStats(c *models.ReqContext) response.Response { statsQuery := models.GetAdminStatsQuery{} - if err := bus.Dispatch(&statsQuery); err != nil { + if err := bus.DispatchCtx(c.Req.Context(), &statsQuery); err != nil { return response.Error(500, "Failed to get admin stats from database", err) } diff --git a/pkg/infra/usagestats/service/service.go b/pkg/infra/usagestats/service/service.go index 823b3ddba8b..6ec6165b87d 100644 --- a/pkg/infra/usagestats/service/service.go +++ b/pkg/infra/usagestats/service/service.go @@ -49,7 +49,7 @@ func ProvideService(cfg *setting.Cfg, bus bus.Bus, sqlStore *sqlstore.SQLStore, } func (uss *UsageStats) Run(ctx context.Context) error { - uss.updateTotalStats() + uss.updateTotalStats(ctx) // try to load last sent time from kv store lastSent := time.Now() @@ -97,7 +97,7 @@ func (uss *UsageStats) Run(ctx context.Context) error { callback() } case <-updateStatsTicker.C: - uss.updateTotalStats() + uss.updateTotalStats(ctx) case <-ctx.Done(): return ctx.Err() } diff --git a/pkg/infra/usagestats/service/usage_stats.go b/pkg/infra/usagestats/service/usage_stats.go index 5bdbaa13184..f9e20c347e4 100644 --- a/pkg/infra/usagestats/service/usage_stats.go +++ b/pkg/infra/usagestats/service/usage_stats.go @@ -38,7 +38,7 @@ func (uss *UsageStats) GetUsageReport(ctx context.Context) (usagestats.Report, e } statsQuery := models.GetSystemStatsQuery{} - if err := uss.Bus.Dispatch(&statsQuery); err != nil { + if err := uss.Bus.DispatchCtx(ctx, &statsQuery); err != nil { uss.log.Error("Failed to get system stats", "error", err) return report, err } @@ -110,7 +110,7 @@ func (uss *UsageStats) GetUsageReport(ctx context.Context) (usagestats.Report, e metrics["stats.avg_auth_token_per_user.count"] = avgAuthTokensPerUser dsStats := models.GetDataSourceStatsQuery{} - if err := uss.Bus.Dispatch(&dsStats); err != nil { + if err := uss.Bus.DispatchCtx(ctx, &dsStats); err != nil { uss.log.Error("Failed to get datasource stats", "error", err) return report, err } @@ -129,7 +129,7 @@ func (uss *UsageStats) GetUsageReport(ctx context.Context) (usagestats.Report, e metrics["stats.ds.other.count"] = dsOtherCount esDataSourcesQuery := models.GetDataSourcesByTypeQuery{Type: models.DS_ES} - if err := uss.Bus.Dispatch(&esDataSourcesQuery); err != nil { + if err := uss.Bus.DispatchCtx(ctx, &esDataSourcesQuery); err != nil { uss.log.Error("Failed to get elasticsearch json data", "error", err) return report, err } @@ -152,7 +152,7 @@ func (uss *UsageStats) GetUsageReport(ctx context.Context) (usagestats.Report, e // fetch datasource access stats dsAccessStats := models.GetDataSourceAccessStatsQuery{} - if err := uss.Bus.Dispatch(&dsAccessStats); err != nil { + if err := uss.Bus.DispatchCtx(ctx, &dsAccessStats); err != nil { uss.log.Error("Failed to get datasource access stats", "error", err) return report, err } @@ -287,13 +287,13 @@ var sendUsageStats = func(uss *UsageStats, data *bytes.Buffer) { }() } -func (uss *UsageStats) updateTotalStats() { +func (uss *UsageStats) updateTotalStats(ctx context.Context) { if !uss.Cfg.MetricsEndpointEnabled || uss.Cfg.MetricsEndpointDisableTotalStats { return } statsQuery := models.GetSystemStatsQuery{} - if err := uss.Bus.Dispatch(&statsQuery); err != nil { + if err := uss.Bus.DispatchCtx(ctx, &statsQuery); err != nil { uss.log.Error("Failed to get system stats", "error", err) return } @@ -317,7 +317,7 @@ func (uss *UsageStats) updateTotalStats() { metrics.StatsTotalLibraryVariables.Set(float64(statsQuery.Result.LibraryVariables)) dsStats := models.GetDataSourceStatsQuery{} - if err := uss.Bus.Dispatch(&dsStats); err != nil { + if err := uss.Bus.DispatchCtx(ctx, &dsStats); err != nil { uss.log.Error("Failed to get datasource stats", "error", err) return } diff --git a/pkg/infra/usagestats/service/usage_stats_test.go b/pkg/infra/usagestats/service/usage_stats_test.go index 7fc54de8ff0..3b46008a01a 100644 --- a/pkg/infra/usagestats/service/usage_stats_test.go +++ b/pkg/infra/usagestats/service/usage_stats_test.go @@ -411,7 +411,7 @@ func TestMetrics(t *testing.T) { t.Run("When metrics is disabled and total stats is enabled, stats should not be updated", func(t *testing.T) { uss.Cfg.MetricsEndpointEnabled = false uss.Cfg.MetricsEndpointDisableTotalStats = false - uss.updateTotalStats() + uss.updateTotalStats(context.Background()) assert.False(t, getSystemStatsWasCalled) }) @@ -420,7 +420,7 @@ func TestMetrics(t *testing.T) { uss.Cfg.MetricsEndpointEnabled = true uss.Cfg.MetricsEndpointDisableTotalStats = true - uss.updateTotalStats() + uss.updateTotalStats(context.Background()) assert.False(t, getSystemStatsWasCalled) }) @@ -429,7 +429,7 @@ func TestMetrics(t *testing.T) { uss.Cfg.MetricsEndpointEnabled = false uss.Cfg.MetricsEndpointDisableTotalStats = true - uss.updateTotalStats() + uss.updateTotalStats(context.Background()) assert.False(t, getSystemStatsWasCalled) }) @@ -438,7 +438,7 @@ func TestMetrics(t *testing.T) { uss.Cfg.MetricsEndpointEnabled = true uss.Cfg.MetricsEndpointDisableTotalStats = false - uss.updateTotalStats() + uss.updateTotalStats(context.Background()) assert.True(t, getSystemStatsWasCalled) }) diff --git a/pkg/services/sqlstore/dashboard_test.go b/pkg/services/sqlstore/dashboard_test.go index fbcdeba190e..cecb1464f04 100644 --- a/pkg/services/sqlstore/dashboard_test.go +++ b/pkg/services/sqlstore/dashboard_test.go @@ -386,13 +386,13 @@ func TestDashboardDataAccess(t *testing.T) { Convey("Given two dashboards, one is starred dashboard by user 10, other starred by user 1", func() { starredDash := insertTestDashboard(t, sqlStore, "starred dash", 1, 0, false) - err := StarDashboard(&models.StarDashboardCommand{ + err := sqlStore.StarDashboard(context.Background(), &models.StarDashboardCommand{ DashboardId: starredDash.Id, UserId: 10, }) So(err, ShouldBeNil) - err = StarDashboard(&models.StarDashboardCommand{ + err = sqlStore.StarDashboard(context.Background(), &models.StarDashboardCommand{ DashboardId: savedDash.Id, UserId: 1, }) diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index a2f9cb75074..1a851fd9369 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -116,6 +116,7 @@ func newSQLStore(cfg *setting.Cfg, cacheService *localcache.CacheService, bus bu ss.addDashboardACLQueryAndCommandHandlers() ss.addQuotaQueryAndCommandHandlers() ss.addOrgUsersQueryAndCommandHandlers() + ss.addStarQueryAndCommandHandlers() // if err := ss.Reset(); err != nil { // return nil, err diff --git a/pkg/services/sqlstore/star.go b/pkg/services/sqlstore/star.go index cd2cde78eb7..d7b03c0b6c8 100644 --- a/pkg/services/sqlstore/star.go +++ b/pkg/services/sqlstore/star.go @@ -7,17 +7,17 @@ import ( "github.com/grafana/grafana/pkg/models" ) -func init() { - bus.AddHandler("sql", StarDashboard) - bus.AddHandler("sql", UnstarDashboard) - bus.AddHandler("sql", GetUserStars) - bus.AddHandlerCtx("sql", IsStarredByUserCtx) +func (ss *SQLStore) addStarQueryAndCommandHandlers() { + bus.AddHandlerCtx("sql", ss.StarDashboard) + bus.AddHandlerCtx("sql", ss.UnstarDashboard) + bus.AddHandlerCtx("sql", ss.GetUserStars) + bus.AddHandlerCtx("sql", ss.IsStarredByUserCtx) } -func IsStarredByUserCtx(ctx context.Context, query *models.IsStarredByUserQuery) error { - return withDbSession(ctx, x, func(dbSession *DBSession) error { +func (ss *SQLStore) IsStarredByUserCtx(ctx context.Context, query *models.IsStarredByUserQuery) error { + return ss.WithDbSession(ctx, func(sess *DBSession) error { rawSQL := "SELECT 1 from star where user_id=? and dashboard_id=?" - results, err := dbSession.Query(rawSQL, query.UserId, query.DashboardId) + results, err := sess.Query(rawSQL, query.UserId, query.DashboardId) if err != nil { return err @@ -33,12 +33,12 @@ func IsStarredByUserCtx(ctx context.Context, query *models.IsStarredByUserQuery) }) } -func StarDashboard(cmd *models.StarDashboardCommand) error { +func (ss *SQLStore) StarDashboard(ctx context.Context, cmd *models.StarDashboardCommand) error { if cmd.DashboardId == 0 || cmd.UserId == 0 { return models.ErrCommandValidationFailed } - return inTransaction(func(sess *DBSession) error { + return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error { entity := models.Star{ UserId: cmd.UserId, DashboardId: cmd.DashboardId, @@ -49,26 +49,28 @@ func StarDashboard(cmd *models.StarDashboardCommand) error { }) } -func UnstarDashboard(cmd *models.UnstarDashboardCommand) error { +func (ss *SQLStore) UnstarDashboard(ctx context.Context, cmd *models.UnstarDashboardCommand) error { if cmd.DashboardId == 0 || cmd.UserId == 0 { return models.ErrCommandValidationFailed } - return inTransaction(func(sess *DBSession) error { + return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error { var rawSQL = "DELETE FROM star WHERE user_id=? and dashboard_id=?" _, err := sess.Exec(rawSQL, cmd.UserId, cmd.DashboardId) return err }) } -func GetUserStars(query *models.GetUserStarsQuery) error { - var stars = make([]models.Star, 0) - err := x.Where("user_id=?", query.UserId).Find(&stars) +func (ss *SQLStore) GetUserStars(ctx context.Context, query *models.GetUserStarsQuery) error { + return ss.WithDbSession(ctx, func(dbSession *DBSession) error { + var stars = make([]models.Star, 0) + err := dbSession.Where("user_id=?", query.UserId).Find(&stars) - query.Result = make(map[int64]bool) - for _, star := range stars { - query.Result[star.DashboardId] = true - } + query.Result = make(map[int64]bool) + for _, star := range stars { + query.Result[star.DashboardId] = true + } - return err + return err + }) } diff --git a/pkg/services/sqlstore/stars_test.go b/pkg/services/sqlstore/stars_test.go index 116f9f804a1..30250a49884 100644 --- a/pkg/services/sqlstore/stars_test.go +++ b/pkg/services/sqlstore/stars_test.go @@ -8,36 +8,36 @@ import ( "testing" "github.com/grafana/grafana/pkg/models" - . "github.com/smartystreets/goconvey/convey" + "github.com/stretchr/testify/require" ) func TestUserStarsDataAccess(t *testing.T) { - Convey("Testing User Stars Data Access", t, func() { - InitTestDB(t) + t.Run("Testing User Stars Data Access", func(t *testing.T) { + sqlStore := InitTestDB(t) - Convey("Given saved star", func() { + t.Run("Given saved star", func(t *testing.T) { cmd := models.StarDashboardCommand{ DashboardId: 10, UserId: 12, } - err := StarDashboard(&cmd) - So(err, ShouldBeNil) + err := sqlStore.StarDashboard(context.Background(), &cmd) + require.NoError(t, err) - Convey("IsStarredByUser should return true when starred", func() { + t.Run("IsStarredByUser should return true when starred", func(t *testing.T) { query := models.IsStarredByUserQuery{UserId: 12, DashboardId: 10} - err := IsStarredByUserCtx(context.Background(), &query) - So(err, ShouldBeNil) + err := sqlStore.IsStarredByUserCtx(context.Background(), &query) + require.NoError(t, err) - So(query.Result, ShouldBeTrue) + require.True(t, query.Result) }) - Convey("IsStarredByUser should return false when not starred", func() { + t.Run("IsStarredByUser should return false when not starred", func(t *testing.T) { query := models.IsStarredByUserQuery{UserId: 12, DashboardId: 12} - err := IsStarredByUserCtx(context.Background(), &query) - So(err, ShouldBeNil) + err := sqlStore.IsStarredByUserCtx(context.Background(), &query) + require.NoError(t, err) - So(query.Result, ShouldBeFalse) + require.False(t, query.Result) }) }) }) diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go index 00d121d3530..bf61f893ec2 100644 --- a/pkg/services/sqlstore/stats.go +++ b/pkg/services/sqlstore/stats.go @@ -10,10 +10,10 @@ import ( ) func init() { - bus.AddHandler("sql", GetSystemStats) - bus.AddHandler("sql", GetDataSourceStats) - bus.AddHandler("sql", GetDataSourceAccessStats) - bus.AddHandler("sql", GetAdminStats) + bus.AddHandlerCtx("sql", GetSystemStats) + bus.AddHandlerCtx("sql", GetDataSourceStats) + bus.AddHandlerCtx("sql", GetDataSourceAccessStats) + bus.AddHandlerCtx("sql", GetAdminStats) bus.AddHandlerCtx("sql", GetAlertNotifiersUsageStats) bus.AddHandlerCtx("sql", GetSystemUserCountStats) } @@ -28,21 +28,21 @@ func GetAlertNotifiersUsageStats(ctx context.Context, query *models.GetAlertNoti return err } -func GetDataSourceStats(query *models.GetDataSourceStatsQuery) error { +func GetDataSourceStats(ctx context.Context, query *models.GetDataSourceStatsQuery) error { var rawSQL = `SELECT COUNT(*) AS count, type FROM ` + dialect.Quote("data_source") + ` GROUP BY type` query.Result = make([]*models.DataSourceStats, 0) err := x.SQL(rawSQL).Find(&query.Result) return err } -func GetDataSourceAccessStats(query *models.GetDataSourceAccessStatsQuery) error { +func GetDataSourceAccessStats(ctx context.Context, query *models.GetDataSourceAccessStatsQuery) error { var rawSQL = `SELECT COUNT(*) AS count, type, access FROM ` + dialect.Quote("data_source") + ` GROUP BY type, access` query.Result = make([]*models.DataSourceAccessStats, 0) err := x.SQL(rawSQL).Find(&query.Result) return err } -func GetSystemStats(query *models.GetSystemStatsQuery) error { +func GetSystemStats(ctx context.Context, query *models.GetSystemStatsQuery) error { sb := &SQLBuilder{} sb.Write("SELECT ") sb.Write(`(SELECT COUNT(*) FROM ` + dialect.Quote("user") + `) AS users,`) @@ -92,7 +92,7 @@ func GetSystemStats(query *models.GetSystemStatsQuery) error { sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("library_element")+` WHERE kind = ?) AS library_panels,`, models.PanelElement) sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("library_element")+` WHERE kind = ?) AS library_variables,`, models.VariableElement) - sb.Write(roleCounterSQL()) + sb.Write(roleCounterSQL(ctx)) var stats models.SystemStats _, err := x.SQL(sb.GetSQLString(), sb.params...).Get(&stats) @@ -105,9 +105,9 @@ func GetSystemStats(query *models.GetSystemStatsQuery) error { return nil } -func roleCounterSQL() string { +func roleCounterSQL(ctx context.Context) string { const roleCounterTimeout = 20 * time.Second - ctx, cancel := context.WithTimeout(context.Background(), roleCounterTimeout) + ctx, cancel := context.WithTimeout(ctx, roleCounterTimeout) defer cancel() _ = updateUserRoleCountsIfNecessary(ctx, false) sqlQuery := @@ -136,7 +136,7 @@ func viewersPermissionsCounterSQL(statName string, isFolder bool, permission mod ) AS ` + statName + `, ` } -func GetAdminStats(query *models.GetAdminStatsQuery) error { +func GetAdminStats(ctx context.Context, query *models.GetAdminStatsQuery) error { activeEndDate := time.Now().Add(-activeUserTimeLimit) dailyActiveEndDate := time.Now().Add(-dailyActiveUserTimeLimit) @@ -185,7 +185,7 @@ func GetAdminStats(query *models.GetAdminStatsQuery) error { SELECT COUNT(*) FROM ` + dialect.Quote("user") + ` WHERE last_seen_at > ? ) AS daily_active_users, - ` + roleCounterSQL() + `, + ` + roleCounterSQL(ctx) + `, ( SELECT COUNT(*) FROM ` + dialect.Quote("user_auth_token") + ` WHERE rotated_at > ? diff --git a/pkg/services/sqlstore/stats_integration_test.go b/pkg/services/sqlstore/stats_integration_test.go index af70c238d71..a560d07e035 100644 --- a/pkg/services/sqlstore/stats_integration_test.go +++ b/pkg/services/sqlstore/stats_integration_test.go @@ -4,6 +4,7 @@ package sqlstore import ( + "context" "testing" "github.com/grafana/grafana/pkg/models" @@ -14,6 +15,6 @@ func TestIntegration_GetAdminStats(t *testing.T) { InitTestDB(t) query := models.GetAdminStatsQuery{} - err := GetAdminStats(&query) + err := GetAdminStats(context.Background(), &query) require.NoError(t, err) } diff --git a/pkg/services/sqlstore/stats_test.go b/pkg/services/sqlstore/stats_test.go index be688b41f6b..88ec3e1b6fb 100644 --- a/pkg/services/sqlstore/stats_test.go +++ b/pkg/services/sqlstore/stats_test.go @@ -19,7 +19,7 @@ func TestStatsDataAccess(t *testing.T) { t.Run("Get system stats should not results in error", func(t *testing.T) { query := models.GetSystemStatsQuery{} - err := GetSystemStats(&query) + err := GetSystemStats(context.Background(), &query) require.NoError(t, err) assert.Equal(t, int64(3), query.Result.Users) assert.Equal(t, int64(0), query.Result.Editors) @@ -37,13 +37,13 @@ func TestStatsDataAccess(t *testing.T) { t.Run("Get datasource stats should not results in error", func(t *testing.T) { query := models.GetDataSourceStatsQuery{} - err := GetDataSourceStats(&query) + err := GetDataSourceStats(context.Background(), &query) assert.NoError(t, err) }) t.Run("Get datasource access stats should not results in error", func(t *testing.T) { query := models.GetDataSourceAccessStatsQuery{} - err := GetDataSourceAccessStats(&query) + err := GetDataSourceAccessStats(context.Background(), &query) assert.NoError(t, err) }) @@ -55,7 +55,7 @@ func TestStatsDataAccess(t *testing.T) { t.Run("Get admin stats should not result in error", func(t *testing.T) { query := models.GetAdminStatsQuery{} - err := GetAdminStats(&query) + err := GetAdminStats(context.Background(), &query) assert.NoError(t, err) }) }