github.com/matrixorigin/matrixone@v1.2.0/pkg/util/trace/impl/motrace/report_statement_test.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 motrace
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"github.com/matrixorigin/matrixone/pkg/util/trace/impl/motrace/statistic"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/google/uuid"
    26  	"github.com/matrixorigin/matrixone/pkg/common/util"
    27  	"github.com/prashantv/gostub"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestStatementInfo_Report_EndStatement(t *testing.T) {
    32  	type fields struct {
    33  		StatementID          [16]byte
    34  		TransactionID        [16]byte
    35  		SessionID            [16]byte
    36  		Account              string
    37  		User                 string
    38  		Host                 string
    39  		Database             string
    40  		Statement            string
    41  		StatementFingerprint string
    42  		StatementTag         string
    43  		RequestAt            time.Time
    44  		ExecPlan             SerializableExecPlan
    45  		Status               StatementInfoStatus
    46  		Error                error
    47  		ResponseAt           time.Time
    48  		Duration             time.Duration
    49  
    50  		doReport bool
    51  		doExport bool
    52  	}
    53  	type args struct {
    54  		ctx context.Context
    55  		err error
    56  		fun func()
    57  	}
    58  
    59  	tests := []struct {
    60  		name          string
    61  		fields        fields
    62  		args          args
    63  		wantReportCnt int
    64  		// check after call EndStatement
    65  		wantReportCntAfterEnd int
    66  	}{
    67  		{
    68  			name: "Report_Export_EndStatement",
    69  			fields: fields{
    70  				doReport: true,
    71  				doExport: true,
    72  			},
    73  			args: args{
    74  				ctx: context.Background(),
    75  				err: nil,
    76  			},
    77  			wantReportCnt:         1,
    78  			wantReportCntAfterEnd: 2,
    79  		},
    80  		{
    81  			name: "Report_EndStatement",
    82  			fields: fields{
    83  				doReport: true,
    84  				doExport: false,
    85  			},
    86  			args: args{
    87  				ctx: context.Background(),
    88  				err: nil,
    89  			},
    90  			wantReportCnt:         1,
    91  			wantReportCntAfterEnd: 1,
    92  		},
    93  		{
    94  			name: "just_EndStatement",
    95  			fields: fields{
    96  				doReport: false,
    97  				doExport: false,
    98  			},
    99  			args: args{
   100  				ctx: context.Background(),
   101  				err: nil,
   102  			},
   103  			wantReportCnt:         0,
   104  			wantReportCntAfterEnd: 1,
   105  		},
   106  		{
   107  			name: "skip_running_stmt",
   108  			fields: fields{
   109  				Status:   StatementStatusRunning,
   110  				doReport: false,
   111  				doExport: false,
   112  			},
   113  			args: args{
   114  				ctx: context.Background(),
   115  				err: nil,
   116  				fun: func() {
   117  					GetTracerProvider().skipRunningStmt = true
   118  				},
   119  			},
   120  			wantReportCnt:         0,
   121  			wantReportCntAfterEnd: 1,
   122  		},
   123  	}
   124  
   125  	gotCnt := 0
   126  	dummyReportStmFunc := func(ctx context.Context, s *StatementInfo) error {
   127  		s.reported = true
   128  		gotCnt++
   129  		return nil
   130  	}
   131  	stub := gostub.Stub(&ReportStatement, dummyReportStmFunc)
   132  	defer stub.Reset()
   133  
   134  	dummyExport := func(s *StatementInfo) {
   135  		s.mux.Lock()
   136  		s.exported = true
   137  		s.mux.Unlock()
   138  	}
   139  
   140  	for _, tt := range tests {
   141  		t.Run(tt.name, func(t *testing.T) {
   142  			gotCnt = 0
   143  			s := &StatementInfo{
   144  				StatementID:          tt.fields.StatementID,
   145  				TransactionID:        tt.fields.TransactionID,
   146  				SessionID:            tt.fields.SessionID,
   147  				Account:              tt.fields.Account,
   148  				User:                 tt.fields.User,
   149  				Host:                 tt.fields.Host,
   150  				Database:             tt.fields.Database,
   151  				Statement:            tt.fields.Statement,
   152  				StatementFingerprint: tt.fields.StatementFingerprint,
   153  				StatementTag:         tt.fields.StatementTag,
   154  				RequestAt:            tt.fields.RequestAt,
   155  				ExecPlan:             tt.fields.ExecPlan,
   156  				Status:               tt.fields.Status,
   157  				Error:                tt.fields.Error,
   158  				ResponseAt:           tt.fields.ResponseAt,
   159  				Duration:             tt.fields.Duration,
   160  			}
   161  			if tt.args.fun != nil {
   162  				tt.args.fun()
   163  			}
   164  			if tt.fields.doExport && !tt.fields.doReport {
   165  				t.Errorf("export(%v) need report(%v) first.", tt.fields.doExport, tt.fields.doReport)
   166  			}
   167  			if tt.fields.doReport {
   168  				s.Report(tt.args.ctx)
   169  			}
   170  			require.Equal(t, tt.wantReportCnt, gotCnt)
   171  			if tt.fields.doExport {
   172  				dummyExport(s)
   173  			}
   174  			require.Equal(t, tt.fields.doReport, s.reported)
   175  			require.Equal(t, tt.fields.doExport, s.exported)
   176  
   177  			stmCtx := ContextWithStatement(tt.args.ctx, s)
   178  			EndStatement(stmCtx, tt.args.err, 0, 0, 0)
   179  			require.Equal(t, tt.wantReportCntAfterEnd, gotCnt)
   180  		})
   181  	}
   182  }
   183  
   184  var dummyNoExecPlanJsonResult = `{"code":200,"message":"no exec plan"}`
   185  var dummyNoExecPlanJsonResult2 = `{"func":"dummy2","code":200,"message":"no exec plan"}`
   186  
   187  var dummyStatsArray = *statistic.NewStatsArray().WithTimeConsumed(1).WithMemorySize(2).WithS3IOInputCount(3).WithS3IOOutputCount(4).
   188  	WithOutTrafficBytes(5).WithCU(44.0161)
   189  
   190  var dummySerializeExecPlan = func(_ context.Context, plan any, _ uuid.UUID) ([]byte, statistic.StatsArray, Statistic) {
   191  	if plan == nil {
   192  		return []byte(dummyNoExecPlanJsonResult), statistic.DefaultStatsArray, Statistic{}
   193  	}
   194  	json, err := json.Marshal(plan)
   195  	if err != nil {
   196  		return []byte(fmt.Sprintf(`{"err": %q}`, err.Error())), statistic.DefaultStatsArray, Statistic{}
   197  	}
   198  	return json, dummyStatsArray, Statistic{RowsRead: 1, BytesScan: 1}
   199  }
   200  
   201  var dummySerializeExecPlan2 = func(_ context.Context, plan any, _ uuid.UUID) ([]byte, statistic.StatsArray, Statistic) {
   202  	if plan == nil {
   203  		return []byte(dummyNoExecPlanJsonResult2), statistic.DefaultStatsArray, Statistic{}
   204  	}
   205  	json, err := json.Marshal(plan)
   206  	if err != nil {
   207  		return []byte(fmt.Sprintf(`{"func":"dymmy2","err": %q}`, err.Error())), statistic.DefaultStatsArray, Statistic{}
   208  	}
   209  	val := fmt.Sprintf(`{"func":"dummy2","result":%s}`, json)
   210  	return []byte(val), dummyStatsArray, Statistic{}
   211  }
   212  
   213  func TestStatementInfo_ExecPlan2Json(t *testing.T) {
   214  	type args struct {
   215  		ExecPlan          any
   216  		SerializeExecPlan SerializeExecPlanFunc
   217  	}
   218  
   219  	dummyExecPlan := map[string]any{"key": "val", "int": 1}
   220  	dummyEPJson := `{"func":"dummy2","result":{"int":1,"key":"val"}}`
   221  
   222  	tests := []struct {
   223  		name          string
   224  		args          args
   225  		want          string
   226  		wantStatsByte []byte
   227  	}{
   228  		{
   229  			name: "dummyDefault_ep_Serialize",
   230  			args: args{
   231  				ExecPlan:          dummyExecPlan,
   232  				SerializeExecPlan: dummySerializeExecPlan2,
   233  			},
   234  			want:          dummyEPJson,
   235  			wantStatsByte: dummyStatsArray.ToJsonString(),
   236  		},
   237  		{
   238  			name: "nil_ep_Serialize",
   239  			args: args{
   240  				ExecPlan:          dummyExecPlan,
   241  				SerializeExecPlan: dummySerializeExecPlan2,
   242  			},
   243  			want:          dummyEPJson,
   244  			wantStatsByte: dummyStatsArray.ToJsonString(),
   245  		},
   246  		{
   247  			name: "dummyDefault_nil_Serialize",
   248  			args: args{
   249  				ExecPlan:          nil,
   250  				SerializeExecPlan: dummySerializeExecPlan2,
   251  			},
   252  			want:          dummyNoExecPlanJsonResult2,
   253  			wantStatsByte: statistic.DefaultStatsArrayJsonString,
   254  		},
   255  		{
   256  			name: "nil_nil_Serialize",
   257  			args: args{
   258  				ExecPlan:          nil,
   259  				SerializeExecPlan: dummySerializeExecPlan2,
   260  			},
   261  			want:          dummyNoExecPlanJsonResult2,
   262  			wantStatsByte: statistic.DefaultStatsArrayJsonString,
   263  		},
   264  	}
   265  
   266  	ctx := DefaultContext()
   267  	for _, tt := range tests {
   268  		t.Run(tt.name, func(t *testing.T) {
   269  			s := &StatementInfo{}
   270  			p := &dummySerializableExecPlan{
   271  				plan: tt.args.ExecPlan,
   272  				f:    tt.args.SerializeExecPlan,
   273  			}
   274  			s.SetSerializableExecPlan(p)
   275  			got := s.ExecPlan2Json(ctx)
   276  			err := s.ExecPlan2Stats(ctx)
   277  			require.Nil(t, err)
   278  			stats := s.GetStatsArrayBytes()
   279  			require.Equal(t, tt.want, util.UnsafeBytesToString(got), "ExecPlan2Json()")
   280  			require.Equal(t, tt.wantStatsByte, stats, "want, got: %s, %s", tt.wantStatsByte, stats)
   281  			mapper := new(map[string]any)
   282  			err = json.Unmarshal([]byte(got), mapper)
   283  			require.Nil(t, err, "jons.Unmarshal failed")
   284  		})
   285  	}
   286  }
   287  
   288  type dummySerializableExecPlan struct {
   289  	plan any
   290  	f    SerializeExecPlanFunc
   291  	uuid uuid.UUID
   292  }
   293  
   294  func NewDummySerializableExecPlan(plan any, f SerializeExecPlanFunc, uuid2 uuid.UUID) *dummySerializableExecPlan {
   295  	return &dummySerializableExecPlan{
   296  		plan: plan,
   297  		f:    f,
   298  		uuid: uuid2,
   299  	}
   300  }
   301  
   302  func (p *dummySerializableExecPlan) Marshal(ctx context.Context) []byte {
   303  	res, _, _ := p.f(ctx, p.plan, p.uuid)
   304  	return res
   305  }
   306  func (p *dummySerializableExecPlan) Free() {}
   307  
   308  func (p *dummySerializableExecPlan) Stats(ctx context.Context) (statistic.StatsArray, Statistic) {
   309  	_, statByte, stats := p.f(ctx, p.plan, p.uuid)
   310  	return statByte, stats
   311  }
   312  
   313  func TestMergeStats(t *testing.T) {
   314  	e := &StatementInfo{}
   315  	e.statsArray.Init().WithTimeConsumed(80335).WithMemorySize(1800).WithS3IOInputCount(1).WithS3IOOutputCount(0).WithConnType(statistic.ConnTypeInternal).WithOutPacketCount(1)
   316  
   317  	n := &StatementInfo{}
   318  	n.statsArray.Init().WithTimeConsumed(147960).WithMemorySize(1800).WithS3IOInputCount(0).WithS3IOOutputCount(0).WithOutPacketCount(2)
   319  
   320  	err := mergeStats(e, n)
   321  	if err != nil {
   322  		t.Fatalf("mergeStats failed: %v", err)
   323  	}
   324  
   325  	wantBytes := []byte("[4,228295,3600.000,1,0,0,1,3,0]")
   326  	require.Equal(t, wantBytes, e.statsArray.ToJsonString())
   327  
   328  	n = &StatementInfo{}
   329  	n.statsArray.Init().WithTimeConsumed(1).WithMemorySize(1).WithS3IOInputCount(0).WithS3IOOutputCount(0).WithOutPacketCount(10).WithCU(1.1234)
   330  
   331  	err = mergeStats(e, n)
   332  	if err != nil {
   333  		t.Fatalf("mergeStats failed: %v", err)
   334  	}
   335  
   336  	wantBytes = []byte("[4,228296,3601.000,1,0,0,1,13,1.1234]")
   337  	require.Equal(t, wantBytes, e.statsArray.ToJsonString())
   338  
   339  }
   340  
   341  func TestCalculateAggrMemoryBytes(t *testing.T) {
   342  	type fields struct {
   343  		dividend float64
   344  		divisor  float64
   345  	}
   346  
   347  	tests := []struct {
   348  		name   string
   349  		fields fields
   350  		want   float64
   351  	}{
   352  		{
   353  			name: "normal",
   354  			fields: fields{
   355  				dividend: 1.0,
   356  				divisor:  3.0,
   357  			},
   358  			want: 0.333333,
   359  		},
   360  		{
   361  			name: "1/8",
   362  			fields: fields{
   363  				dividend: 1.0,
   364  				divisor:  8.0,
   365  			},
   366  			want: 0.125,
   367  		},
   368  	}
   369  	for _, tt := range tests {
   370  		t.Run(tt.name, func(t *testing.T) {
   371  
   372  			val1 := mustDecimal128(convertFloat64ToDecimal128(tt.fields.dividend))
   373  			gotVal := calculateAggrMemoryBytes(val1, tt.fields.divisor)
   374  			require.Equal(t, tt.want, gotVal)
   375  		})
   376  	}
   377  }