Skip to content

Commit

Permalink
docs(samples): Query profiling feature samples (#1253)
Browse files Browse the repository at this point in the history
* Add the right files from the preview branch

Copy paste over the files from the preview branch

* Thread Query mode through the library

Thread the query mode through the client library. Allow calls to get made using the query mode.

* Create a basic workflow for testing profiling grpc

This code allows basic query profiling to work and creates a basic code snippet that can be used to make it work.

* This test makes a call to the mock server

This will be useful for debugging grpc issues.

* Now the test works with the mock server in repo

This mock server will be super useful for ensuring that the right data gets transmitted in private preview so that we can confirm the client library is working correctly.

* Delete js file

We use the ts file instead.

* Try explain

See if Explain gets passed along to the mock server.

* Update protos.json

Protos.json is needed to pass the plan along.

* Try an aggregate query

* Add test to catch breaking change to return type

Make sure changes don’t break the use of this callback by adding this test.

* Try redefining RunQueryCallback

Define it with a more general data structure.

* Revert Gapic changes to match head branch

The merge caused generated files to have a diff. The diff should not be in the PR.

* Remove only

only should not be used here

* Add data structures for return values

Data structures need to be added for the return values that contain stats.

* Add plumbing to send stats info back

Add stats to info if it is available. If there are no results then end the stream and send the info back. This way, stats will always be sent in info if they are available and the program won ’t break if there are no results.

* Set test to only that we intend to explore

* Add a comment about stats

Explain what happens when the result set is empty. Just send the stats back.

* Delete the mock server code

The mock server code was used for debugging and now we don’t need it since the service is working.

* Remove calls to nightly

Calls to nightly don’t have a second database to work with. Regular calls work now so nightly calls are not necessary.

* Introduce profiling tests again

Bring the query profiling tests back.

* Revert "Remove calls to nightly"

This reverts commit 040d0a5.

* Stats are optional

Stats do not necessarily come from the server.

* Write some tests to test each mode

Each query profiling mode needs to be explored.

* Add code for parsing the stats returned

Stats returned needs to be parsed by a special library that will remove the complexities of the Struct object.

* Add dependencies necessary for parsing

A library is needed for parsing the Struct values. Add the libraries and use them.

* Add assertions for the expected plan/stats

Expected plan and expected stats should be used in the tests. This ensures the tests check for the proper stats values.

* Refactor info with stats build and add info to cb

Add a specific type for the runAggregationQuery callback so that it can now support the info parameter. In order to allow runAggregationQuery to make use of creating info, we also refactor info construction into a separate function.

* Modify the parser for runAggregationQuery

The parser for runAggregationQuery should have a deterministic process for computing results.

* Add asserts for the return values of runAggregate

Make sure that the entities and the plan are correct.

* Complete tests for runQuery and aggregation query

The assertion checks for runQuery and runAggregationQuery should be done and they test each mode.

* Add tests for Query and AggregateQuery

Tests should ensure run functions work properly for the run function on both of these objects.

* Add initial transaction tests for runQuery

The runQuery tests have been added which get the right results. Next the right assert statements and info collection will be added to the tests.

* Add checks on info to the transaction tests

Checks against info are needed to be sure that stats are passed back to the caller properly.

* Fix tests for aggregate queries in transactions

Add tests for running aggregate queries inside transactions.

* Ran linter, added all tests for runQueryStream

Added a temporary test for runQueryStream. Also ran the linter.

* Change parsing of return values

Return values are going to look different for users. Change the code so that the parsing is done differently.

* Reformat the info function

This function is more readable if we eliminate some of the tertiary arguments and complex logic for building the info object.

* Change tests as a result of structure changes

The structure of the QueryInfo object is changed. Modify the tests to work with the new structure.

* Use import and not require

import is better for catching compile time errors and is more customary.

* Better spacing for imports

Change the spacing so that the imports are all in one place.

* Introduce a single function for checking the execution stats. Make sure all the tests use this function. Pull out the run query plan and the run aggregation query plan.

* Fix the tests so that they call the right fns

Add assertion checks to check the query plan against some expected value and make sure the right assertion checks are done for the right tests.

* Finish the tests for the streaming call

Finish the tests for specifying no mode, specifying normal mode, EXPLAIN mode and EXPLAIN_ANALYZE mode. Make sure the tests  pass.

* Delete code that will not be used anymore

There is a lot of boilerplate code that was needed for the streaming call. Get rid of it here.

* Make changes to match new proto

Code change to use new proto was made so that code will compile.

* Add Explain Metrics interface

Make slight change to withBeginTransaction so that code compiles under new structure. Also group plan and statistics under the new explainMetrics interface.

* Remove bytesReturned from test

Proto should not be passing along bytesReturned anymore.

* Fix system tests to use values matching new struct

* Remove calls to nightly

Feature is now fully ready so nightly tests should not be done because the feature is expected to work in production.

* Query profiling

Add test for runQuery. Send back plan summary and execution stats.

* Add a test for runAggregationQuery

runAggregationQuery needs a unit test to validate request/return data.

* Parameterize the query profiling tests

* run the linter

* Export Query Mode in index.ts

Query mode needs to be exported so that it can be accessed by the user.

* Query profiling samples

Add query profiling samples for runQuery and runAggregationQuery.

* Change data structure types to match return values

* Remove TODO

* remove import

* delete the query profiling samples

* Remove abstraction for RunQueryCallback

* Change the comment to describe new data types

* Remove TODO

* linting fixes

* Update type to include runAggregationQuery

* Put else back in

This change is actually simpler because it doesn’t introduce a let. It is also a much smaller diff.

* mode is not needed in sharedQueryOptions

* Revert "mode is not needed in sharedQueryOptions"

This reverts commit b8d0c63.

* Rearrange imports

rearrange the imports to simplify the diff.

* Undo old query profiling changes

* Remove dependencies that are not needed

* Pass analyze true in

* Fix the flakey test - sort values

* Move code out that is responsible for importing data into the datastore database in preparation for the samples.

Ensure query profiling output in the script matches what the sample tests expect.

* Write the sample for aggregate queries

* Add a description

* Rename the sample files

* Set files up so that they can be parsed

Rename the files too

* Add some scripts for just the explain option

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* Remove logging of entities from the samples

* Update samples/queryProfileExplain.js

Co-authored-by: kolea2 <45548808+kolea2@users.noreply.github.com>

* Update samples/queryProfileExplain.js

Co-authored-by: kolea2 <45548808+kolea2@users.noreply.github.com>

* Update descriptions

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* Don’t sort in the sample.

Make the tests indifferent about the order so that sorting isn’t required in the samples.

* Fix titles

* Add header

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
Co-authored-by: kolea2 <45548808+kolea2@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 19, 2024
1 parent fb82e1c commit 91237e0
Show file tree
Hide file tree
Showing 11 changed files with 377 additions and 44 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ Samples are in the [`samples/`](https://github.com/googleapis/nodejs-datastore/t
| Indexes.get | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/indexes.get.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/indexes.get.js,samples/README.md) |
| Indexes.list | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/indexes.list.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/indexes.list.js,samples/README.md) |
| Create a union between two filters | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/queryFilterOr.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/queryFilterOr.js,samples/README.md) |
| Run query explain (regular query) | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/queryProfileExplain.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/queryProfileExplain.js,samples/README.md) |
| Run query explain (aggregate query) | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/queryProfileExplainAggregation.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/queryProfileExplainAggregation.js,samples/README.md) |
| Run query explain analyze (regular query) | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/queryProfileExplainAnalyze.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/queryProfileExplainAnalyze.js,samples/README.md) |
| Run query explain analyze (aggregate query) | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/queryProfileExplainAnalyzeAggregation.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/queryProfileExplainAnalyzeAggregation.js,samples/README.md) |
| Quickstart | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/quickstart.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/quickstart.js,samples/README.md) |
| Add Task | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/tasks.add.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/tasks.add.js,samples/README.md) |
| Delete Task | [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/tasks.delete.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/tasks.delete.js,samples/README.md) |
Expand Down
80 changes: 80 additions & 0 deletions samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
* [Indexes.get](#indexes.get)
* [Indexes.list](#indexes.list)
* [Create a union between two filters](#create-a-union-between-two-filters)
* [Run query explain (regular query)](#run-query-explain-regular-query)
* [Run query explain (aggregate query)](#run-query-explain-aggregate-query)
* [Run query explain analyze (regular query)](#run-query-explain-analyze-regular-query)
* [Run query explain analyze (aggregate query)](#run-query-explain-analyze-aggregate-query)
* [Quickstart](#quickstart)
* [Add Task](#add-task)
* [Delete Task](#delete-task)
Expand Down Expand Up @@ -162,6 +166,82 @@ __Usage:__



### Run query explain (regular query)

Run query explain for a standard query and print query explain metrics

View the [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/queryProfileExplain.js).

[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/queryProfileExplain.js,samples/README.md)

__Usage:__


`node samples/queryProfileExplain.js`


-----




### Run query explain (aggregate query)

Run query explain for an aggregate query and print query explain metrics

View the [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/queryProfileExplainAggregation.js).

[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/queryProfileExplainAggregation.js,samples/README.md)

__Usage:__


`node samples/queryProfileExplainAggregation.js`


-----




### Run query explain analyze (regular query)

Run query explain analyze for a standard query and print query explain metrics

View the [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/queryProfileExplainAnalyze.js).

[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/queryProfileExplainAnalyze.js,samples/README.md)

__Usage:__


`node samples/queryProfileExplainAnalyze.js`


-----




### Run query explain analyze (aggregate query)

Run query explain analyze for an aggregate query and print query explain metrics

View the [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/queryProfileExplainAnalyzeAggregation.js).

[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-datastore&page=editor&open_in_editor=samples/queryProfileExplainAnalyzeAggregation.js,samples/README.md)

__Usage:__


`node samples/queryProfileExplainAnalyzeAggregation.js`


-----




### Quickstart

View the [source code](https://github.com/googleapis/nodejs-datastore/blob/main/samples/quickstart.js).
Expand Down
1 change: 0 additions & 1 deletion samples/queryFilterOr.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ async function main() {
);

const [entities] = await datastore.runQuery(query);
entities.sort();
for (const entity of entities) {
console.log(`Entity found: ${entity['description']}`);
}
Expand Down
38 changes: 38 additions & 0 deletions samples/queryProfileExplain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

// sample-metadata:
// title: Run query explain (regular query)
// description: Run query explain for a standard query and print query explain metrics

async function main() {
// [START datastore_query_explain_entity]

// Imports the Cloud Datastore
const {Datastore} = require('@google-cloud/datastore');

// Instantiate the Datastore
const datastore = new Datastore();
const q = datastore.createQuery('Task');
const [, info] = await datastore.runQuery(q, {
explainOptions: {analyze: false},
});
console.log(`info: ${Object.keys(info.explainMetrics)}`);
// [END datastore_query_explain_entity]
}

const args = process.argv.slice(2);
main(...args).catch(console.error);
41 changes: 41 additions & 0 deletions samples/queryProfileExplainAggregation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

// sample-metadata:
// title: Run query explain (aggregate query)
// description: Run query explain for an aggregate query and print query explain metrics

async function main() {
// [START datastore_query_explain_aggregation]

// Imports the Cloud Datastore
const {Datastore, AggregateField} = require('@google-cloud/datastore');

// Instantiate the Datastore
const datastore = new Datastore();
const q = datastore.createQuery('Task');
const aggregate = datastore
.createAggregationQuery(q)
.addAggregation(AggregateField.sum('created'));
const [, info] = await datastore.runAggregationQuery(aggregate, {
explainOptions: {analyze: false},
});
console.log(`info: ${Object.keys(info.explainMetrics)}`);
// [END datastore_query_explain_aggregation]
}

const args = process.argv.slice(2);
main(...args).catch(console.error);
41 changes: 41 additions & 0 deletions samples/queryProfileExplainAnalyze.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

// sample-metadata:
// title: Run query explain analyze (regular query)
// description: Run query explain analyze for a standard query and print query explain metrics

async function main() {
// [START datastore_query_explain_analyze_entity]

// Imports the Cloud Datastore
const {Datastore} = require('@google-cloud/datastore');

// Instantiate the Datastore
const datastore = new Datastore();
const q = datastore.createQuery('Task');
const [entities, info] = await datastore.runQuery(q, {
explainOptions: {analyze: true},
});
for (const entity of entities) {
console.log(`Entity found: ${entity['description']}`);
}
console.log(`info: ${Object.keys(info.explainMetrics)}`);
// [END datastore_query_explain_analyze_entity]
}

const args = process.argv.slice(2);
main(...args).catch(console.error);
45 changes: 45 additions & 0 deletions samples/queryProfileExplainAnalyzeAggregation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

// sample-metadata:
// title: Run query explain analyze (aggregate query)
// description: Run query explain analyze for an aggregate query and print query explain metrics

async function main() {
// [START datastore_query_explain_analyze_aggregation]

// Imports the Cloud Datastore
const {Datastore, AggregateField} = require('@google-cloud/datastore');

// Instantiate the Datastore
const datastore = new Datastore();
const q = datastore.createQuery('Task');
const aggregate = datastore
.createAggregationQuery(q)
.addAggregation(AggregateField.sum('created'));

const [entities, info] = await datastore.runAggregationQuery(aggregate, {
explainOptions: {analyze: true},
});
for (const entity of entities) {
console.log(`Entity found: ${JSON.stringify(entity)}`);
}
console.log(`info: ${Object.keys(info.explainMetrics)}`);
// [END datastore_query_explain_analyze_aggregation]
}

const args = process.argv.slice(2);
main(...args).catch(console.error);
56 changes: 56 additions & 0 deletions samples/test/helpers/populate-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/* eslint-disable */

/*
This module is used in various samples to set datastore data to a fixed state
so that the output of the sample is predictable.
*/

const {after, before} = require('mocha');
const {Datastore} = require('@google-cloud/datastore');
let taskKey1, taskKey2;
const datastore = new Datastore();

before(async () => {
taskKey1 = datastore.key('Task');
const entity1 = {
key: taskKey1,
data: {
description: 'Buy milk',
},
};

taskKey2 = datastore.key('Task');
const entity2 = {
key: taskKey2,
data: {
description: 'Feed cats',
},
};

// Ensure the datastore database has no existing data.
const query = datastore.createQuery('Task');
const [entities] = await datastore.runQuery(query);
await datastore.delete(entities.map(entity => entity[datastore.KEY]));

await datastore.upsert(entity1);
await datastore.upsert(entity2);
});

after(async () => {
await datastore.delete(taskKey1);
await datastore.delete(taskKey2);
});
22 changes: 22 additions & 0 deletions samples/test/helpers/sorting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This function is used for comparing console output when the
// order of the console output doesn't matter.

function sortConsoleOutput(output) {
return output.split('\n').sort().join('\n');
}

module.exports = sortConsoleOutput;
Loading

0 comments on commit 91237e0

Please sign in to comment.