github.com/matrixorigin/matrixone@v0.7.0/pkg/util/metric/cron_task.go (about) 1 // Copyright 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package metric 16 17 import ( 18 "context" 19 "fmt" 20 "path" 21 "strings" 22 "sync/atomic" 23 "time" 24 25 "github.com/matrixorigin/matrixone/pkg/common/runtime" 26 "github.com/matrixorigin/matrixone/pkg/pb/task" 27 "github.com/matrixorigin/matrixone/pkg/taskservice" 28 ie "github.com/matrixorigin/matrixone/pkg/util/internalExecutor" 29 "github.com/matrixorigin/matrixone/pkg/util/trace" 30 31 "go.uber.org/zap" 32 ) 33 34 const ( 35 LoggerName = "MetricTask" 36 LoggerNameMetricStorage = "MetricStorage" 37 38 StorageUsageCronTask = "StorageUsage" 39 StorageUsageTaskCronExpr = ExprEvery05Min 40 41 ExprEvery05Min = "0 */1 * * * *" 42 ParamSeparator = " " 43 ) 44 45 // TaskMetadata handle args like: "{db_tbl_name} [date, default: today]" 46 func TaskMetadata(jobName string, id task.TaskCode, args ...string) task.TaskMetadata { 47 return task.TaskMetadata{ 48 ID: path.Join(jobName, path.Join(args...)), 49 Executor: id, 50 Context: []byte(strings.Join(args, ParamSeparator)), 51 Options: task.TaskOptions{Concurrency: 1}, 52 } 53 } 54 55 // CreateCronTask should init once in/with schema-init. 56 func CreateCronTask(ctx context.Context, executorID task.TaskCode, taskService taskservice.TaskService) error { 57 var err error 58 ctx, span := trace.Start(ctx, "MetricCreateCronTask") 59 defer span.End() 60 logger := runtime.ProcessLevelRuntime().Logger().WithContext(ctx).Named(LoggerName) 61 logger.Info(fmt.Sprintf("init metric task with CronExpr: %s", StorageUsageTaskCronExpr)) 62 if err = taskService.CreateCronTask(ctx, TaskMetadata(StorageUsageCronTask, executorID), StorageUsageTaskCronExpr); err != nil { 63 return err 64 } 65 return nil 66 } 67 68 // GetMetricStorageUsageExecutor collect metric server_storage_usage 69 func GetMetricStorageUsageExecutor(sqlExecutor func() ie.InternalExecutor) func(ctx context.Context, task task.Task) error { 70 f := func(ctx context.Context, task task.Task) error { 71 return CalculateStorageUsage(ctx, sqlExecutor) 72 } 73 return f 74 } 75 76 const ( 77 ShowAccountSQL = "SHOW ACCOUNTS;" 78 ColumnAccountName = "account_name" 79 ColumnSize = "size" 80 ) 81 82 var gUpdateStorageUsageInterval = defaultUpdateInterval() 83 84 func defaultUpdateInterval() *atomic.Int64 { 85 v := new(atomic.Int64) 86 v.Store(int64(time.Minute)) 87 return v 88 } 89 90 func SetUpdateStorageUsageInterval(interval time.Duration) { 91 gUpdateStorageUsageInterval.Store(int64(interval)) 92 } 93 94 func GetUpdateStorageUsageInterval() time.Duration { 95 return time.Duration(gUpdateStorageUsageInterval.Load()) 96 } 97 98 var QuitableWait = func(ctx context.Context) (*time.Ticker, error) { 99 next := time.NewTicker(GetUpdateStorageUsageInterval()) 100 return next, nil 101 } 102 103 func CalculateStorageUsage(ctx context.Context, sqlExecutor func() ie.InternalExecutor) (err error) { 104 ctx, span := trace.Start(ctx, "MetricStorageUsage") 105 defer span.End() 106 logger := runtime.ProcessLevelRuntime().Logger().WithContext(ctx).Named(LoggerNameMetricStorage) 107 defer func() { 108 logger.Info("finished", zap.Error(err)) 109 }() 110 111 next := time.NewTicker(time.Second) 112 113 for { 114 select { 115 case <-ctx.Done(): 116 logger.Info("receive context signal", zap.Error(ctx.Err())) 117 StorageUsageFactory.Reset() // clean CN data for next cron task. 118 return ctx.Err() 119 case <-next.C: 120 logger.Info("start next round") 121 } 122 123 // main 124 // +-----------------+------------+---------------------+--------+----------------+----------+-------------+-----------+-------+----------------+ 125 // | account_name | admin_name | created | status | suspended_time | db_count | table_count | row_count | size | comment | 126 // +-----------------+------------+---------------------+--------+----------------+----------+-------------+-----------+-------+----------------+ 127 // | sys | root | 2023-01-17 09:56:10 | open | NULL | 6 | 56 | 2082 | 0.341 | system account | 128 // | query_tae_table | admin | 2023-01-17 09:56:26 | open | NULL | 6 | 34 | 792 | 0.036 | | 129 // +-----------------+------------+---------------------+--------+----------------+----------+-------------+-----------+-------+----------------+ 130 executor := sqlExecutor() 131 logger.Info("query storage size") 132 result := executor.Query(ctx, ShowAccountSQL, ie.NewOptsBuilder().Finish()) 133 err = result.Error() 134 if err != nil { 135 return err 136 } 137 138 cnt := result.RowCount() 139 if cnt == 0 { 140 next = time.NewTicker(time.Minute) 141 logger.Warn("got empty account info, wait shortly") 142 continue 143 } 144 logger.Info("collect storage_usage cnt", zap.Uint64("cnt", cnt)) 145 StorageUsageFactory.Reset() 146 for rowIdx := uint64(0); rowIdx < result.RowCount(); rowIdx++ { 147 148 account, err := result.StringValueByName(ctx, rowIdx, ColumnAccountName) 149 if err != nil { 150 return err 151 } 152 153 sizeMB, err := result.Float64ValueByName(ctx, rowIdx, ColumnSize) 154 if err != nil { 155 return err 156 } 157 158 logger.Info("storage_usage", zap.String("account", account), zap.Float64("sizeMB", sizeMB)) 159 StorageUsage(account).Set(sizeMB) 160 } 161 162 // next round 163 next, err = QuitableWait(ctx) 164 if err != nil { 165 return err 166 } 167 logger.Info("wait next round") 168 } 169 }