2020-09-02 01:07:31 -05:00
package sqlstore
import (
"context"
"fmt"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
)
2020-12-08 12:41:35 -06:00
// AnnotationCleanupService is responsible for cleaning old annotations.
2020-09-02 01:07:31 -05:00
type AnnotationCleanupService struct {
batchSize int64
log log . Logger
2022-04-25 12:07:11 -05:00
sqlstore * SQLStore
2020-09-02 01:07:31 -05:00
}
const (
alertAnnotationType = "alert_id <> 0"
dashboardAnnotationType = "dashboard_id <> 0 AND alert_id = 0"
apiAnnotationType = "alert_id = 0 AND dashboard_id = 0"
)
2020-12-08 12:41:35 -06:00
// CleanAnnotations deletes old annotations created by alert rules, API
// requests and human made in the UI. It subsequently deletes orphaned rows
// from the annotation_tag table. Cleanup actions are performed in batches
// so that no query takes too long to complete.
//
// Returns the number of annotation and annotation_tag rows deleted. If an
// error occurs, it returns the number of rows affected so far.
func ( acs * AnnotationCleanupService ) CleanAnnotations ( ctx context . Context , cfg * setting . Cfg ) ( int64 , int64 , error ) {
var totalCleanedAnnotations int64
affected , err := acs . cleanAnnotations ( ctx , cfg . AlertingAnnotationCleanupSetting , alertAnnotationType )
totalCleanedAnnotations += affected
2020-09-02 01:07:31 -05:00
if err != nil {
2020-12-08 12:41:35 -06:00
return totalCleanedAnnotations , 0 , err
2020-09-02 01:07:31 -05:00
}
2020-12-08 12:41:35 -06:00
affected , err = acs . cleanAnnotations ( ctx , cfg . APIAnnotationCleanupSettings , apiAnnotationType )
totalCleanedAnnotations += affected
2020-09-02 01:07:31 -05:00
if err != nil {
2020-12-08 12:41:35 -06:00
return totalCleanedAnnotations , 0 , err
2020-09-02 01:07:31 -05:00
}
2020-12-08 12:41:35 -06:00
affected , err = acs . cleanAnnotations ( ctx , cfg . DashboardAnnotationCleanupSettings , dashboardAnnotationType )
totalCleanedAnnotations += affected
if err != nil {
return totalCleanedAnnotations , 0 , err
}
2021-05-12 03:44:00 -05:00
if totalCleanedAnnotations > 0 {
affected , err = acs . cleanOrphanedAnnotationTags ( ctx )
}
2020-12-08 12:41:35 -06:00
return totalCleanedAnnotations , affected , err
2020-09-02 01:07:31 -05:00
}
2020-12-08 12:41:35 -06:00
func ( acs * AnnotationCleanupService ) cleanAnnotations ( ctx context . Context , cfg setting . AnnotationCleanupSettings , annotationType string ) ( int64 , error ) {
var totalAffected int64
2020-09-02 01:07:31 -05:00
if cfg . MaxAge > 0 {
cutoffDate := time . Now ( ) . Add ( - cfg . MaxAge ) . UnixNano ( ) / int64 ( time . Millisecond )
deleteQuery := ` DELETE FROM annotation WHERE id IN (SELECT id FROM (SELECT id FROM annotation WHERE %s AND created < %v ORDER BY id DESC %s) a) `
sql := fmt . Sprintf ( deleteQuery , annotationType , cutoffDate , dialect . Limit ( acs . batchSize ) )
2020-12-08 12:41:35 -06:00
affected , err := acs . executeUntilDoneOrCancelled ( ctx , sql )
totalAffected += affected
2020-09-02 01:07:31 -05:00
if err != nil {
2020-12-08 12:41:35 -06:00
return totalAffected , err
2020-09-02 01:07:31 -05:00
}
}
if cfg . MaxCount > 0 {
deleteQuery := ` DELETE FROM annotation WHERE id IN (SELECT id FROM (SELECT id FROM annotation WHERE %s ORDER BY id DESC %s) a) `
sql := fmt . Sprintf ( deleteQuery , annotationType , dialect . LimitOffset ( acs . batchSize , cfg . MaxCount ) )
2020-12-08 12:41:35 -06:00
affected , err := acs . executeUntilDoneOrCancelled ( ctx , sql )
totalAffected += affected
return totalAffected , err
2020-09-02 01:07:31 -05:00
}
2020-12-08 12:41:35 -06:00
return totalAffected , nil
2020-09-02 01:07:31 -05:00
}
2020-12-08 12:41:35 -06:00
func ( acs * AnnotationCleanupService ) cleanOrphanedAnnotationTags ( ctx context . Context ) ( int64 , error ) {
deleteQuery := ` DELETE FROM annotation_tag WHERE id IN ( SELECT id FROM (SELECT id FROM annotation_tag WHERE NOT EXISTS (SELECT 1 FROM annotation a WHERE annotation_id = a.id) %s) a) `
sql := fmt . Sprintf ( deleteQuery , dialect . Limit ( acs . batchSize ) )
return acs . executeUntilDoneOrCancelled ( ctx , sql )
}
func ( acs * AnnotationCleanupService ) executeUntilDoneOrCancelled ( ctx context . Context , sql string ) ( int64 , error ) {
var totalAffected int64
2020-09-02 01:07:31 -05:00
for {
select {
case <- ctx . Done ( ) :
2020-12-08 12:41:35 -06:00
return totalAffected , ctx . Err ( )
2020-09-02 01:07:31 -05:00
default :
var affected int64
2022-04-25 12:07:11 -05:00
err := withDbSession ( ctx , acs . sqlstore . engine , func ( session * DBSession ) error {
2020-09-02 01:07:31 -05:00
res , err := session . Exec ( sql )
if err != nil {
return err
}
affected , err = res . RowsAffected ( )
2020-12-08 12:41:35 -06:00
totalAffected += affected
2020-09-02 01:07:31 -05:00
return err
} )
if err != nil {
2020-12-08 12:41:35 -06:00
return totalAffected , err
2020-09-02 01:07:31 -05:00
}
if affected == 0 {
2020-12-08 12:41:35 -06:00
return totalAffected , nil
2020-09-02 01:07:31 -05:00
}
}
}
}