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 }