Skip to content

Commit 1ef9790

Browse files
authored
Merge pull request #283 from ydb-platform/stats
* Added `options.WithExecuteScanQueryStats` option
2 parents 4bdce2a + ab50922 commit 1ef9790

File tree

7 files changed

+100
-31
lines changed

7 files changed

+100
-31
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
* Added `options.WithExecuteScanQueryStats` option
2+
* Added to query stats plan and AST
3+
* Changed behaviour of `result.Stats()` (if query result have no stats - returns `nil`)
14
* Added context cancel with specific error
25

36
## v3.26.10

internal/table/scanner/result.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,18 @@ func (r *baseResult) CurrentResultSet() result.Set {
149149

150150
// Stats returns query execution queryStats.
151151
func (r *baseResult) Stats() stats.QueryStats {
152-
var s queryStats
153152
r.statsMtx.RLock()
154-
s.stats = r.stats
153+
stats := r.stats
155154
r.statsMtx.RUnlock()
156-
s.processCPUTime = time.Microsecond * time.Duration(s.stats.GetProcessCpuTimeUs())
157-
s.pos = 0
158-
return &s
155+
156+
if stats == nil {
157+
return nil
158+
}
159+
160+
return &queryStats{
161+
stats: stats,
162+
processCPUTime: time.Microsecond * time.Duration(stats.GetProcessCpuTimeUs()),
163+
}
159164
}
160165

161166
// Close closes the result, preventing further iteration.

internal/table/scanner/stats.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,32 @@ type queryStats struct {
1616
}
1717

1818
func (s *queryStats) ProcessCPUTime() time.Duration {
19-
if s == nil {
20-
return 0
21-
}
2219
return s.processCPUTime
2320
}
2421

2522
func (s *queryStats) Compilation() (c *stats.CompilationStats) {
26-
if s == nil || s.stats == nil || s.stats.Compilation == nil {
23+
if s.stats == nil || s.stats.Compilation == nil {
2724
return nil
2825
}
29-
x := s.stats.Compilation
30-
if x == nil {
31-
return
32-
}
3326
return &stats.CompilationStats{
34-
FromCache: x.FromCache,
35-
Duration: time.Microsecond * time.Duration(x.DurationUs),
36-
CPUTime: time.Microsecond * time.Duration(x.CpuTimeUs),
27+
FromCache: s.stats.Compilation.FromCache,
28+
Duration: time.Microsecond * time.Duration(s.stats.Compilation.DurationUs),
29+
CPUTime: time.Microsecond * time.Duration(s.stats.Compilation.CpuTimeUs),
3730
}
3831
}
3932

33+
func (s *queryStats) QueryPlan() string {
34+
return s.stats.GetQueryPlan()
35+
}
36+
37+
func (s *queryStats) QueryAST() string {
38+
return s.stats.GetQueryAst()
39+
}
40+
4041
// NextPhase returns next execution phase within query.
4142
// If ok flag is false, then there are no more phases and p is invalid.
4243
func (s *queryStats) NextPhase() (p stats.QueryPhase, ok bool) {
43-
if s.stats == nil || s.pos >= len(s.stats.QueryPhases) {
44+
if s.pos >= len(s.stats.QueryPhases) {
4445
return
4546
}
4647
x := s.stats.QueryPhases[s.pos]
@@ -54,6 +55,7 @@ func (s *queryStats) NextPhase() (p stats.QueryPhase, ok bool) {
5455
duration: time.Microsecond * time.Duration(x.DurationUs),
5556
cpuTime: time.Microsecond * time.Duration(x.CpuTimeUs),
5657
affectedShards: x.AffectedShards,
58+
literalPhase: x.LiteralPhase,
5759
}, true
5860
}
5961

@@ -64,6 +66,7 @@ type queryPhase struct {
6466
affectedShards uint64
6567
tables []*Ydb_TableStats.TableAccessStats
6668
pos int
69+
literalPhase bool
6770
}
6871

6972
// NextTableAccess returns next accessed table within query execution phase.
@@ -96,6 +99,10 @@ func (q *queryPhase) AffectedShards() uint64 {
9699
return q.affectedShards
97100
}
98101

102+
func (q *queryPhase) IsLiteralPhase() bool {
103+
return q.literalPhase
104+
}
105+
99106
func initOperationStats(x *Ydb_TableStats.OperationStats) stats.OperationStats {
100107
if x == nil {
101108
return stats.OperationStats{}

table/options/options.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,12 +583,42 @@ type (
583583
ExecuteScanQueryOption func(*ExecuteScanQueryDesc)
584584
)
585585

586+
// WithExecuteScanQueryMode defines scan query mode: execute or explain
586587
func WithExecuteScanQueryMode(m ExecuteScanQueryRequestMode) ExecuteScanQueryOption {
587588
return func(desc *ExecuteScanQueryDesc) {
588589
desc.Mode = m.toYDB()
589590
}
590591
}
591592

593+
// ExecuteScanQueryStatsType specified scan query mode
594+
type ExecuteScanQueryStatsType uint32
595+
596+
const (
597+
ExecuteScanQueryStatsTypeNone = iota
598+
ExecuteScanQueryStatsTypeBasic
599+
ExecuteScanQueryStatsTypeFull
600+
)
601+
602+
func (stats ExecuteScanQueryStatsType) toYDB() Ydb_Table.QueryStatsCollection_Mode {
603+
switch stats {
604+
case ExecuteScanQueryStatsTypeNone:
605+
return Ydb_Table.QueryStatsCollection_STATS_COLLECTION_NONE
606+
case ExecuteScanQueryStatsTypeBasic:
607+
return Ydb_Table.QueryStatsCollection_STATS_COLLECTION_BASIC
608+
case ExecuteScanQueryStatsTypeFull:
609+
return Ydb_Table.QueryStatsCollection_STATS_COLLECTION_FULL
610+
default:
611+
return Ydb_Table.QueryStatsCollection_STATS_COLLECTION_UNSPECIFIED
612+
}
613+
}
614+
615+
// WithExecuteScanQueryStats defines query statistics mode
616+
func WithExecuteScanQueryStats(stats ExecuteScanQueryStatsType) ExecuteScanQueryOption {
617+
return func(desc *ExecuteScanQueryDesc) {
618+
desc.CollectStats = stats.toYDB()
619+
}
620+
}
621+
592622
// Read table options
593623
type (
594624
ReadTableDesc Ydb_Table.ReadTableRequest

table/result/result.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ type result interface {
106106
ScanNamed(namedValues ...named.Value) error
107107

108108
// Stats returns query execution QueryStats.
109+
//
110+
// If query result have no stats - returns nil
109111
Stats() (s stats.QueryStats)
110112

111113
// Err return scanner error

table/stats/stats.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@ type QueryPhase interface {
1010
Duration() time.Duration
1111
CPUTime() time.Duration
1212
AffectedShards() uint64
13+
IsLiteralPhase() bool
1314
}
1415

1516
// QueryStats holds query execution statistics.
1617
type QueryStats interface {
1718
ProcessCPUTime() time.Duration
1819
Compilation() (c *CompilationStats)
20+
QueryPlan() string
21+
QueryAST() string
1922
// NextPhase returns next execution phase within query.
2023
// If ok flag is false, then there are no more phases and p is invalid.
2124
NextPhase() (p QueryPhase, ok bool)

table/table_e2e_test.go

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ func TestTable(t *testing.T) {
559559
func(ctx context.Context, s table.Session) (err error) {
560560
res, err := s.StreamExecuteScanQuery(
561561
ctx, `SELECT val FROM stream_query;`, table.NewQueryParameters(),
562+
options.WithExecuteScanQueryStats(options.ExecuteScanQueryStatsTypeFull),
562563
)
563564
if err != nil {
564565
return err
@@ -579,6 +580,21 @@ func TestTable(t *testing.T) {
579580
}
580581
checkSum += uint64(*val)
581582
}
583+
if stats := res.Stats(); stats != nil {
584+
t.Logf(" --- query stats: compilation: %v, process CPU time: %v, affected shards: %v\n",
585+
stats.Compilation(),
586+
stats.ProcessCPUTime(),
587+
func() (count uint64) {
588+
for {
589+
phase, ok := stats.NextPhase()
590+
if !ok {
591+
return
592+
}
593+
count += phase.AffectedShards()
594+
}
595+
}(),
596+
)
597+
}
582598
}
583599
if rowsCount != upsertRowsCount {
584600
return fmt.Errorf("wrong rows count: %v, exp: %v", rowsCount, upsertRowsCount)
@@ -695,27 +711,30 @@ func streamReadTable(ctx context.Context, t *testing.T, c table.Client, tableAbs
695711
if err = res.Err(); err != nil {
696712
return err
697713
}
698-
stats := res.Stats()
699-
for i := 0; ; i++ {
700-
phase, ok := stats.NextPhase()
701-
if !ok {
702-
break
703-
}
704-
t.Logf(
705-
"# phase #%d: took %s\n",
706-
i, phase.Duration(),
707-
)
708-
for {
709-
tbl, ok := phase.NextTableAccess()
714+
715+
if stats := res.Stats(); stats != nil {
716+
for i := 0; ; i++ {
717+
phase, ok := stats.NextPhase()
710718
if !ok {
711719
break
712720
}
713721
t.Logf(
714-
"# accessed %s: read=(%drows, %dbytes)\n",
715-
tbl.Name, tbl.Reads.Rows, tbl.Reads.Bytes,
722+
"# phase #%d: took %s\n",
723+
i, phase.Duration(),
716724
)
725+
for {
726+
tbl, ok := phase.NextTableAccess()
727+
if !ok {
728+
break
729+
}
730+
t.Logf(
731+
"# accessed %s: read=(%drows, %dbytes)\n",
732+
tbl.Name, tbl.Reads.Rows, tbl.Reads.Bytes,
733+
)
734+
}
717735
}
718736
}
737+
719738
return nil
720739
},
721740
)

0 commit comments

Comments
 (0)