Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(datastore): Add query profiling #9200

Merged
merged 23 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
730d947
feat(datastore): Query profiling
bhshkh Dec 18, 2023
5825267
feat(datastore): remove comment
bhshkh Dec 18, 2023
bf802dc
Merge branch 'main' into feature/ds-query-profiling
bhshkh Dec 27, 2023
4d8a1f1
feat(datastore): Refactor test
bhshkh Dec 27, 2023
1f507c6
Merge branch 'main' into feature/ds-query-profiling
bhshkh Jan 10, 2024
4276f61
feat(datastore): Refactor integration test
bhshkh Jan 17, 2024
8420179
Merge branch 'main' into feature/ds-query-profiling
bhshkh Feb 13, 2024
21923dc
feat(datastore): Use ExplainOptions instead of QueryMode
bhshkh Feb 14, 2024
365c625
feat(datastore): Refactoring code
bhshkh Feb 14, 2024
5033f50
feat(datastore): Additional comments
bhshkh Feb 14, 2024
ac60830
feat(datastore): Resolving vet failures
bhshkh Feb 14, 2024
252f0e9
Merge branch 'googleapis:main' into feature/ds-query-profiling
bhshkh Mar 18, 2024
f0ba17f
feat(datastore): Updating to match latest protos
bhshkh Mar 18, 2024
edb28f2
Merge branch 'main' into feature/ds-query-profiling
bhshkh Apr 8, 2024
54e092d
feat(datastore): Add RunWithOptions
bhshkh Apr 9, 2024
1cfdc14
Merge branch 'main' into feature/ds-query-profiling
bhshkh Apr 17, 2024
6f9f76a
fix(datastore): Resolving vet failures
bhshkh Apr 18, 2024
cf4823b
Merge branch 'main' into feature/ds-query-profiling
bhshkh Apr 18, 2024
0ceff9f
Merge branch 'main' into feature/ds-query-profiling
bhshkh Apr 18, 2024
6d23653
doc(datastore): Adde comment for preview feature
bhshkh Apr 18, 2024
700e3a1
feat(datastore): Correcting the error message
bhshkh Apr 22, 2024
144e73e
Merge branch 'main' into feature/ds-query-profiling
bhshkh May 7, 2024
4730d3e
feat(datastore): Resolving test failures
bhshkh May 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(datastore): Updating to match latest protos
  • Loading branch information
bhshkh committed Mar 18, 2024
commit f0ba17f6f85766e5662ed1717414c0123b4acfdd
121 changes: 69 additions & 52 deletions datastore/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1023,11 +1023,13 @@ func TestIntegration_RunAggregationQueryWithOptions(t *testing.T) {
{
desc: "ExplainOptions.Analyze is false",
wantRes: AggregationWithOptionsResult{
Plan: &Plan{
IndexesUsed: []*map[string]interface{}{
{
"properties": "(T ASC, I ASC, __name__ ASC)",
"query_scope": "Includes Ancestors",
ExplainMetrics: &ExplainMetrics{
PlanSummary: &PlanSummary{
IndexesUsed: []*map[string]interface{}{
{
"properties": "(T ASC, I ASC, __name__ ASC)",
"query_scope": "Includes Ancestors",
},
},
},
},
Expand All @@ -1038,19 +1040,22 @@ func TestIntegration_RunAggregationQueryWithOptions(t *testing.T) {
desc: "ExplainOptions.Analyze is true",
wantRes: AggregationWithOptionsResult{
Result: wantAggResult,
Plan: &Plan{
IndexesUsed: []*map[string]interface{}{
{
"properties": "(T ASC, I ASC, __name__ ASC)",
"query_scope": "Includes Ancestors",
ExplainMetrics: &ExplainMetrics{
PlanSummary: &PlanSummary{
IndexesUsed: []*map[string]interface{}{
{
"properties": "(T ASC, I ASC, __name__ ASC)",
"query_scope": "Includes Ancestors",
},
},
},
},
ExecutionStats: &ExecutionStats{
ResultsReturned: 1,
DebugStats: &map[string]interface{}{
"documents_scanned": "0",
"index_entries_scanned": "3",
ExecutionStats: &ExecutionStats{
ReadOperations: 1,
ResultsReturned: 1,
DebugStats: &map[string]interface{}{
"documents_scanned": "0",
"index_entries_scanned": "3",
},
},
},
},
Expand All @@ -1065,17 +1070,14 @@ func TestIntegration_RunAggregationQueryWithOptions(t *testing.T) {
r.Errorf("err: got %v, want: nil", gotErr)
}

if gotErr == nil && !reflect.DeepEqual(gotRes.Result, testcase.wantRes.Result) {
if gotErr == nil && !testutil.Equal(gotRes.Result, testcase.wantRes.Result,
cmpopts.IgnoreFields(ExplainMetrics{})) {
r.Errorf("%q: Mismatch in aggregation result got: %v, want: %v", testcase.desc, gotRes, testcase.wantRes)
return
}

if !testutil.Equal(gotRes.Plan, testcase.wantRes.Plan) {
t.Errorf("%v Plan: got: %+v, want: %+v", testcase.desc, gotRes.Plan, testcase.wantRes.Plan)
}

if err := isEqualExecutionStats(gotRes.ExecutionStats, testcase.wantRes.ExecutionStats); err != nil {
r.Errorf("%q: Mismatch in stats %+v", testcase.desc, err)
if err := cmpExplainMetrics(gotRes.ExplainMetrics, testcase.wantRes.ExplainMetrics); err != nil {
r.Errorf("%q: Mismatch in ExplainMetrics %+v", testcase.desc, err)
}
})
}
Expand Down Expand Up @@ -1427,8 +1429,7 @@ func createTestEntities(ctx context.Context, t *testing.T, client *Client, parti
type runWithOptionsTestcase struct {
desc string
wantKeys []*Key
wantExecutionStats *ExecutionStats
wantPlan *Plan
wantExplainMetrics *ExplainMetrics
wantEntities []SQChild
opts []RunOption
}
Expand All @@ -1444,11 +1445,13 @@ func getRunWithOptionsTestcases(ctx context.Context, t *testing.T, client *Clien
{
desc: "ExplainOptions.Analyze is false",
opts: []RunOption{ExplainOptions{}},
wantPlan: &Plan{
IndexesUsed: []*map[string]interface{}{
{
"properties": "(T ASC, I ASC, __name__ ASC)",
"query_scope": "Includes Ancestors",
wantExplainMetrics: &ExplainMetrics{
PlanSummary: &PlanSummary{
IndexesUsed: []*map[string]interface{}{
{
"properties": "(T ASC, I ASC, __name__ ASC)",
"query_scope": "Includes Ancestors",
},
},
},
},
Expand All @@ -1457,18 +1460,21 @@ func getRunWithOptionsTestcases(ctx context.Context, t *testing.T, client *Clien
desc: "ExplainOptions.Analyze is true",
opts: []RunOption{ExplainOptions{Analyze: true}},
wantKeys: keys,
wantExecutionStats: &ExecutionStats{
ResultsReturned: 1,
DebugStats: &map[string]interface{}{
"documents_scanned": "3",
"index_entries_scanned": "3",
wantExplainMetrics: &ExplainMetrics{
ExecutionStats: &ExecutionStats{
ReadOperations: int64(count),
ResultsReturned: int64(count),
DebugStats: &map[string]interface{}{
"documents_scanned": fmt.Sprint(count),
"index_entries_scanned": fmt.Sprint(count),
},
},
},
wantPlan: &Plan{
IndexesUsed: []*map[string]interface{}{
{
"properties": "(T ASC, I ASC, __name__ ASC)",
"query_scope": "Includes Ancestors",
PlanSummary: &PlanSummary{
IndexesUsed: []*map[string]interface{}{
{
"properties": "(T ASC, I ASC, __name__ ASC)",
"query_scope": "Includes Ancestors",
},
},
},
},
Expand Down Expand Up @@ -1496,10 +1502,7 @@ func TestIntegration_GetAllWithOptions(t *testing.T) {
if !testutil.Equal(gotRes.Keys, testcase.wantKeys) {
t.Errorf("%v keys: got: %+v, want: %+v", testcase.desc, gotRes.Keys, testcase.wantKeys)
}
if !testutil.Equal(gotRes.Plan, testcase.wantPlan) {
t.Errorf("%v Plan: got: %+v, want: %+v", testcase.desc, gotRes.Plan, testcase.wantPlan)
}
if err := isEqualExecutionStats(gotRes.ExecutionStats, testcase.wantExecutionStats); err != nil {
if err := cmpExplainMetrics(gotRes.ExplainMetrics, testcase.wantExplainMetrics); err != nil {
t.Errorf("%v %+v", testcase.desc, err)
}
}
Expand Down Expand Up @@ -1529,16 +1532,30 @@ func TestIntegration_RunWithOptions(t *testing.T) {
if !testutil.Equal(gotSQChildsFromRun, testcase.wantEntities) {
t.Errorf("%v entities: got: %+v, want: %+v", testcase.desc, gotSQChildsFromRun, testcase.wantEntities)
}
if !testutil.Equal(iter.Plan, testcase.wantPlan) {
t.Errorf("%v Plan: got: %+v, want: %+v", testcase.desc, iter.Plan, testcase.wantPlan)
}
if err := isEqualExecutionStats(iter.ExecutionStats, testcase.wantExecutionStats); err != nil {

if err := cmpExplainMetrics(iter.ExplainMetrics, testcase.wantExplainMetrics); err != nil {
t.Errorf("%v %+v", testcase.desc, err)
}
}
}

func isEqualExecutionStats(got *ExecutionStats, want *ExecutionStats) error {
func cmpExplainMetrics(got *ExplainMetrics, want *ExplainMetrics) error {
if (got != nil && want == nil) || (got == nil && want != nil) {
return fmt.Errorf("ExplainMetrics: got: %+v, want: %+v", got, want)
}
if got == nil {
return nil
}
if !testutil.Equal(got.PlanSummary, want.PlanSummary) {
return fmt.Errorf("Plan: got: %+v, want: %+v", got.PlanSummary, want.PlanSummary)
}
if err := cmpExecutionStats(got.ExecutionStats, want.ExecutionStats); err != nil {
return err
}
return nil
}

func cmpExecutionStats(got *ExecutionStats, want *ExecutionStats) error {
if (got != nil && want == nil) || (got == nil && want != nil) {
return fmt.Errorf("ExecutionStats: got: %+v, want: %+v", got, want)
}
Expand All @@ -1547,8 +1564,8 @@ func isEqualExecutionStats(got *ExecutionStats, want *ExecutionStats) error {
}

// Compare all fields except DebugStats
if !testutil.Equal(want, got, cmpopts.IgnoreFields(&ExecutionStats{}, "DebugStats")) {
return fmt.Errorf("ExecutionStats: mismatch (-want +got):\n%s", testutil.Diff(want, got, cmpopts.IgnoreFields(&ExecutionStats{}, "DebugStats")))
if !testutil.Equal(want, got, cmpopts.IgnoreFields(ExecutionStats{}, "DebugStats", "ExecutionDuration")) {
return fmt.Errorf("ExecutionStats: mismatch (-want +got):\n%s", testutil.Diff(want, got, cmpopts.IgnoreFields(ExecutionStats{}, "DebugStats")))
}

// Compare DebugStats
Expand Down
80 changes: 40 additions & 40 deletions datastore/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -673,8 +673,18 @@ func newRunQuerySettings(opts []RunOption) (*runQuerySettings, error) {
return s, nil
}

// Plan represents planning phase information for the query.
type Plan struct {
// ExplainMetrics for the query.
type ExplainMetrics struct {
// Planning phase information for the query.
PlanSummary *PlanSummary

// Aggregated stats from the execution of the query. Only present when
// ExplainOptions.Analyze is set to true.
ExecutionStats *ExecutionStats
}

// PlanSummary represents planning phase information for the query.
type PlanSummary struct {
// The indexes selected for the query. For example:
//
// [
Expand All @@ -689,8 +699,6 @@ type ExecutionStats struct {
// Total number of results returned, including documents, projections,
// aggregation results, keys.
ResultsReturned int64
// Total number of the bytes of the results returned.
BytesReturned int64
// Total time to execute the query in the backend.
ExecutionDuration *time.Duration
// Total billable read operations.
Expand All @@ -715,13 +723,8 @@ type ExecutionStats struct {
type GetAllWithOptionsResult struct {
Keys []*Key

// Planning phase information for the query.
// This is only present when ExplainOptions is used
Plan *Plan

// Aggregated stats from the execution of the query.
// This is only present when ExplainOptions with Analyze as true is used
ExecutionStats *ExecutionStats
// Query explain metrics. This is only present when ExplainOptions is provided.
ExplainMetrics *ExplainMetrics
}

// GetAll runs the provided query in the given context and returns all keys
Expand Down Expand Up @@ -777,8 +780,7 @@ func (c *Client) GetAllWithOptions(ctx context.Context, q *Query, dst interface{

for t := c.Run(ctx, q, opts...); ; {
k, e, err := t.next()
res.ExecutionStats = t.ExecutionStats
res.Plan = t.Plan
res.ExplainMetrics = t.ExplainMetrics
if err == iterator.Done {
break
}
Expand Down Expand Up @@ -934,7 +936,7 @@ func (c *Client) RunAggregationQueryWithOptions(ctx context.Context, aq *Aggrega
return ar, err
}

if req.ExplainOptions != nil && req.ExplainOptions.Analyze {
if req.ExplainOptions == nil || req.ExplainOptions.Analyze {
ar.Result = make(AggregationResult)
// TODO(developer): change batch parsing logic if other aggregations are supported.
for _, a := range resp.Batch.AggregationResults {
Expand All @@ -943,9 +945,8 @@ func (c *Client) RunAggregationQueryWithOptions(ctx context.Context, aq *Aggrega
}
}
}
ar.ExecutionStats = fromPbExecutionStats(resp.ExecutionStats)
ar.Plan = fromPbPlan(resp.Plan)

ar.ExplainMetrics = fromPbExplainMetrics(resp.GetExplainMetrics())
return ar, nil
}

Expand Down Expand Up @@ -1012,13 +1013,8 @@ type Iterator struct {
// entityCursor is the compiled cursor of the next result.
entityCursor []byte

// Planning phase information for the query.
// This is only present when ExplainOptions is used
Plan *Plan

// Aggregated stats from the execution of the query.
// This is only present when ExplainOptions with Analyze as true is used
ExecutionStats *ExecutionStats
// Query explain metrics. This is only present when ExplainOptions is used.
ExplainMetrics *ExplainMetrics

// trans records the transaction in which the query was run
// Currently, this value is set but unused
Expand Down Expand Up @@ -1102,7 +1098,7 @@ func (t *Iterator) nextBatch() error {
if t.req.ExplainOptions != nil && !t.req.ExplainOptions.Analyze {
// No results to process
t.limit = 0
t.Plan = fromPbPlan(resp.Plan)
t.ExplainMetrics = fromPbExplainMetrics(resp.GetExplainMetrics())
return nil
}

Expand Down Expand Up @@ -1145,25 +1141,35 @@ func (t *Iterator) nextBatch() error {
t.pageCursor = resp.Batch.EndCursor

t.results = resp.Batch.EntityResults
t.ExecutionStats = fromPbExecutionStats(resp.ExecutionStats)
t.Plan = fromPbPlan(resp.Plan)
t.ExplainMetrics = fromPbExplainMetrics(resp.GetExplainMetrics())
return nil
}

func fromPbPlan(pbplan *pb.Plan) *Plan {
if pbplan == nil {
func fromPbExplainMetrics(pbExplainMetrics *pb.ExplainMetrics) *ExplainMetrics {
if pbExplainMetrics == nil {
return nil
}
explainMetrics := &ExplainMetrics{
PlanSummary: fromPbPlanSummary(pbExplainMetrics.PlanSummary),
ExecutionStats: fromPbExecutionStats(pbExplainMetrics.ExecutionStats),
}
return explainMetrics
}

plan := &Plan{}
func fromPbPlanSummary(pbPlanSummary *pb.PlanSummary) *PlanSummary {
if pbPlanSummary == nil {
return nil
}

planSummary := &PlanSummary{}
indexesUsed := []*map[string]interface{}{}
for _, pbIndexUsed := range pbplan.GetIndexesUsed() {
for _, pbIndexUsed := range pbPlanSummary.GetIndexesUsed() {
indexUsed := protostruct.DecodeToMap(pbIndexUsed)
indexesUsed = append(indexesUsed, &indexUsed)
}

plan.IndexesUsed = indexesUsed
return plan
planSummary.IndexesUsed = indexesUsed
return planSummary
}

func fromPbExecutionStats(pbstats *pb.ExecutionStats) *ExecutionStats {
Expand All @@ -1173,7 +1179,6 @@ func fromPbExecutionStats(pbstats *pb.ExecutionStats) *ExecutionStats {

executionStats := &ExecutionStats{
ResultsReturned: pbstats.GetResultsReturned(),
BytesReturned: pbstats.GetBytesReturned(),
ReadOperations: pbstats.GetReadOperations(),
}

Expand Down Expand Up @@ -1321,11 +1326,6 @@ type AggregationResult map[string]interface{}
type AggregationWithOptionsResult struct {
Result AggregationResult

// Planning phase information for the query.
// This is only present when ExplainOptions is used
Plan *Plan

// Aggregated stats from the execution of the query.
// This is only present when ExplainOptions with Analyze as true is used
ExecutionStats *ExecutionStats
// Query explain metrics. This is only present when ExplainOptions is provided.
ExplainMetrics *ExplainMetrics
}
Loading