2020-09-02 08:07:31 +02:00
package sqlstore
import (
"context"
"fmt"
"time"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
)
2020-12-08 18:41:35 +00:00
// AnnotationCleanupService is responsible for cleaning old annotations.
2020-09-02 08:07:31 +02:00
type AnnotationCleanupService struct {
batchSize int64
log log . Logger
2022-04-25 13:07:11 -04:00
sqlstore * SQLStore
2020-09-02 08:07:31 +02: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 18:41:35 +00: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 08:07:31 +02:00
if err != nil {
2020-12-08 18:41:35 +00:00
return totalCleanedAnnotations , 0 , err
2020-09-02 08:07:31 +02:00
}
2020-12-08 18:41:35 +00:00
affected , err = acs . cleanAnnotations ( ctx , cfg . APIAnnotationCleanupSettings , apiAnnotationType )
totalCleanedAnnotations += affected
2020-09-02 08:07:31 +02:00
if err != nil {
2020-12-08 18:41:35 +00:00
return totalCleanedAnnotations , 0 , err
2020-09-02 08:07:31 +02:00
}
2020-12-08 18:41:35 +00:00
affected , err = acs . cleanAnnotations ( ctx , cfg . DashboardAnnotationCleanupSettings , dashboardAnnotationType )
totalCleanedAnnotations += affected
if err != nil {
return totalCleanedAnnotations , 0 , err
}
2021-05-12 04:44:00 -04:00
if totalCleanedAnnotations > 0 {
affected , err = acs . cleanOrphanedAnnotationTags ( ctx )
}
2020-12-08 18:41:35 +00:00
return totalCleanedAnnotations , affected , err
2020-09-02 08:07:31 +02:00
}
2020-12-08 18:41:35 +00:00
func ( acs * AnnotationCleanupService ) cleanAnnotations ( ctx context . Context , cfg setting . AnnotationCleanupSettings , annotationType string ) ( int64 , error ) {
var totalAffected int64
2020-09-02 08:07:31 +02: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 18:41:35 +00:00
affected , err := acs . executeUntilDoneOrCancelled ( ctx , sql )
totalAffected += affected
2020-09-02 08:07:31 +02:00
if err != nil {
2020-12-08 18:41:35 +00:00
return totalAffected , err
2020-09-02 08:07:31 +02: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 18:41:35 +00:00
affected , err := acs . executeUntilDoneOrCancelled ( ctx , sql )
totalAffected += affected
return totalAffected , err
2020-09-02 08:07:31 +02:00
}
2020-12-08 18:41:35 +00:00
return totalAffected , nil
2020-09-02 08:07:31 +02:00
}
2020-12-08 18:41:35 +00: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 08:07:31 +02:00
for {
select {
case <- ctx . Done ():
2020-12-08 18:41:35 +00:00
return totalAffected , ctx . Err ()
2020-09-02 08:07:31 +02:00
default :
var affected int64
2022-04-25 13:07:11 -04:00
err := withDbSession ( ctx , acs . sqlstore . engine , func ( session * DBSession ) error {
2020-09-02 08:07:31 +02:00
res , err := session . Exec ( sql )
if err != nil {
return err
}
affected , err = res . RowsAffected ()
2020-12-08 18:41:35 +00:00
totalAffected += affected
2020-09-02 08:07:31 +02:00
return err
})
if err != nil {
2020-12-08 18:41:35 +00:00
return totalAffected , err
2020-09-02 08:07:31 +02:00
}
if affected == 0 {
2020-12-08 18:41:35 +00:00
return totalAffected , nil
2020-09-02 08:07:31 +02:00
}
}
}
}