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  }