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: support RPC priority #1282

Merged
merged 18 commits into from
Apr 8, 2021
Merged

Conversation

olavloite
Copy link
Contributor

@olavloite olavloite commented Dec 4, 2020

Adds support for setting RPC priority.
This PR also uses some of the changes that are introduced in #1254.

@olavloite olavloite added the do not merge Indicates a pull request not ready for merge, due to either quality or timing. label Dec 4, 2020
@google-cla google-cla bot added the cla: yes This human has signed the Contributor License Agreement. label Dec 4, 2020
@product-auto-label product-auto-label bot added the api: spanner Issues related to the googleapis/nodejs-spanner API. label Dec 4, 2020
@olavloite olavloite changed the title [WIP] feat: support RPC priority feat: support RPC priority Dec 4, 2020
@olavloite olavloite marked this pull request as ready for review December 4, 2020 17:29
@olavloite olavloite requested review from a team as code owners December 4, 2020 17:29
@skuruppu
Copy link
Contributor

skuruppu commented Dec 7, 2020

Also adding @AVaksman who is working on tagging which may touch similar code paths.

ORDER BY AlbumTitle`;

try {
const [rows] = await database.run({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to add a comment to say that we're explicitly lowering the priority of this query by setting this option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -1253,15 +1261,15 @@ export class Transaction extends Dml {

batchUpdate(
queries: Array<string | Statement>,
gaxOptions?: CallOptions
options?: BatchUpdateOptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of changes like this, we may actually have to make the type changes separately in time for the breaking change release we'll do in early January. Do you think that would be ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll extract this change to a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following @JustinBeckwith's suggestion in #1278 (comment), we could also change this parameter (and others) to BatchUpdateOptions | CallOptions. The definition of this and the other methods would then look something like this:

  batchUpdate(
    queries: Array<string | Statement>,
    optionsOrCallback?: BatchUpdateOptions | CallOptions | BatchUpdateCallback,
    cb?: BatchUpdateCallback
  ): Promise<BatchUpdateResponse> | void {
    const options =
      typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
    const callback =
      typeof optionsOrCallback === 'function' ? optionsOrCallback : cb!;
    const gaxOpts =
      'gaxOptions' in options
        ? (options as BatchUpdateOptions).gaxOptions
        : options;

  ...

  }

(@JustinBeckwith Please feel free to correct me if I misunderstood your comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JustinBeckwith Friendly ping on this. Would you mind giving us your input/opinion on the above?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this out for CommitStats. It works to avoid the breaking change. But there is a caveat that @AVaksman pointed at which is that if options doesn't have gaxOptions then we pass the options as a whole even if it is not a CallOptions.

Is this ok?

I tried to use a Discriminating Union but then we need the user to specify the descriminator field, which would end up being a breaking change anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume there is no other way to do this than the idea you suggested @olavloite . I'll be going ahead with this implementation for CommitStats. So if you could do the same in this PR, then that works.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - we really want to avoid breaking changes in these library unless it's unreasonable to avoid. We're starting to evaluate the possibility of backporting fixes to prior major releases, so the cost of these is going to go waaaaay up. I would recommend avoiding the break if you can.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the different options arguments to BatchUpdateOptions | CallOptions (and similar) and added type checking where necessary. That should make this a non-breaking change.


'use strict';

async function queryWithRpcPriority(instanceId, databaseId, projectId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no region tags here, so these samples won't be usable within cloud.google.com. Should we add them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, eventually, but they are not known yet.

ORDER BY AlbumTitle`;

try {
const [rows] = await database.run({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using an async function like this, we expect a wrapping main function, and an inner async function that's inside the region tag. Here is a good example:
https://github.com/googleapis/nodejs-bigquery/blob/master/samples/createJob.js

For the full description of how these work and why, check out:
https://g3doc.corp.google.com/company/teams/cloud-devrel/dpe/samples/nodejs.md?cl=head

@skuruppu it looks like every sample in this repo needs to be checked for conformance against that rubric :/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack @JustinBeckwith, I didn't realize. We'll do this as a separate cleanup effort. Tracked in #1295.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the information. I've updated this specific sample based on the example you provided. I unfortunately am not allowed to access the documentation that you linked.


rows.forEach(row => {
const json = row.toJSON();
const marketingBudget = json.MarketingBudget
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious - why is it important this value is null vs undefined or ''?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, it's not (lazy copy-paste from a different sample). I removed that specific part.

}
}

require('yargs')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With all of our new samples, we no longer use yargs and instead rely on positional parameters. See the rubric.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to positional parameters.

@@ -1253,15 +1261,15 @@ export class Transaction extends Dml {

batchUpdate(
queries: Array<string | Statement>,
gaxOptions?: CallOptions
options?: BatchUpdateOptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - we really want to avoid breaking changes in these library unless it's unreasonable to avoid. We're starting to evaluate the possibility of backporting fixes to prior major releases, so the cost of these is going to go waaaaay up. I would recommend avoiding the break if you can.

@codecov
Copy link

codecov bot commented Feb 12, 2021

Codecov Report

Merging #1282 (5658ff4) into master (5659e40) will increase coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #1282   +/-   ##
=======================================
  Coverage   98.60%   98.60%           
=======================================
  Files          23       23           
  Lines       21942    21960   +18     
  Branches     1229     1235    +6     
=======================================
+ Hits        21636    21654   +18     
  Misses        297      297           
  Partials        9        9           
Impacted Files Coverage Δ
src/index.ts 100.00% <100.00%> (ø)
src/transaction.ts 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5659e40...5658ff4. Read the comment docs.

@olavloite olavloite mentioned this pull request Feb 26, 2021
3 tasks
@@ -60,6 +61,7 @@ export interface RequestOptions {
}

export interface CommitOptions {
requestOptions?: IRequestOptions;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IRequestOptions interface will contain RPC priority options and the tagging options. However CommitOptions should not support tagging options as a parameter.
I am suggesting either to define a separate interface with just a priority option or another way would be to use some ts magic

requestOptions?: Pick<IRequestOptions, 'priority'>;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need to think of a different name, since we have a same name parameter in ExecuteSqlRequest and BatchUpdateOptions interfaces, in which "requestOptions" will support priority and a requestTag.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps "commitRequestOptions" and "transactionRequetOptions", any suggestions are most welcome.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think doing something like:

requestOptions?: Omit<IRequestOptions, 'request_tag'>;

would be my preference.

I'm not sure about having to name the request options differently in each interface though. We want to keep our interfaces mirroring the protos as much possible. I think it's sufficient to use Pick/Omit to make it clear which of the IRequestOptions fields are available for use or not. In the worst case, we can just add documentation to make it clear which fields are supported.

@@ -123,6 +126,7 @@ export interface BatchUpdateCallback {
response?: spannerClient.spanner.v1.ExecuteBatchDmlResponse
): void;
}
export type BatchUpdateOptions = CommitOptions;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also to have in mind that "requestOptions" (or "transactionRequetOptions" or better name) of BatchUpdateOptions will support requestTag in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once proto changes for tagging is merged something like this could be used

requestOptions?: Omit<IRequestOptions, 'transactionTag'>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that means we have to explicitly create a BatchUpdateOptions interface as it's not the same as CommitOptions.

@@ -76,6 +78,7 @@ export interface ExecuteSqlRequest extends Statement, RequestOptions {
partitionToken?: Uint8Array | string;
seqno?: number;
queryOptions?: IQueryOptions;
requestOptions?: IRequestOptions;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should not use the same property name as in CommitOptions interface since CommitOptions.requestOptions will only support priority parameter and in both ExecuteSqlRequest and BatchUpdateOptions same property name will support requestTag parameter

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine as long as we use Pick/Omit to make it clear which fields are supported/not supported.

@skuruppu
Copy link
Contributor

I merged the codegen PR so once we rebase, we can continue the discussion.

@skuruppu
Copy link
Contributor

skuruppu commented Apr 1, 2021

@olavloite if you could make the changes such that this PR can be merged before #1286, that would be great.

@olavloite
Copy link
Contributor Author

I merged the codegen PR so once we rebase, we can continue the discussion.

@skuruppu

I think that there's something strange going on with that codegen. The RequestOptions message has been added to the proto files, but it has not actually regenerated the proto Typescript and Javascript files. So the build is currently failing because it cannot find the RequestOptions definition.

It seems that the regenerate step is missing the actual protoc step. Any idea why?

@AVaksman
Copy link
Contributor

AVaksman commented Apr 1, 2021

I merged the codegen PR so once we rebase, we can continue the discussion.

@skuruppu

I think that there's something strange going on with that codegen. The RequestOptions message has been added to the proto files, but it has not actually regenerated the proto Typescript and Javascript files. So the build is currently failing because it cannot find the RequestOptions definition.

It seems that the regenerate step is missing the actual protoc step. Any idea why?

I've tried to regenerate locally and this is what I get

DEBUG:synthtool:Compiling protos...
(node:14631) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open 'protos/google/rpc/error_details.proto'
    at Object.openSync (fs.js:462:3)
    at Object.readFileSync (fs.js:364:35)
    at fetch (/www/nodejs-spanner/node_modules/protobufjs/src/root.js:172:34)
    at Root.load (/www/nodejs-spanner/node_modules/protobufjs/src/root.js:206:13)
    at Root.loadSync (/www/nodejs-spanner/node_modules/protobufjs/src/root.js:247:17)
    at main (/www/nodejs-spanner/node_modules/protobufjs/cli/pbjs.js:235:18)
    at internal/util.js:297:30
    at new Promise (<anonymous>)
    at main (internal/util.js:296:12)
    at compileProtos (/www/nodejs-spanner/node_modules/google-gax/build/tools/compileProtos.js:212:11)
(node:14631) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:14631) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

@skuruppu
Copy link
Contributor

skuruppu commented Apr 6, 2021

Hmmmm I'm not sure what's going on. I asked the Node folks for help.

@skuruppu
Copy link
Contributor

skuruppu commented Apr 6, 2021

The issue should be fixed now, please rebase again.

@olavloite olavloite removed the do not merge Indicates a pull request not ready for merge, due to either quality or timing. label Apr 6, 2021
@olavloite
Copy link
Contributor Author

@skuruppu Thanks for getting the proto changes fixed and merged. It works now.

@AVaksman I've rebased and updated the PR based on our latest discussions. Would you mind taking a look and see if this would work for you as well for tagging?

@@ -61,7 +61,7 @@ export interface RequestOptions {
}

export interface CommitOptions {
requestOptions?: IRequestOptions;
requestOptions?: Omit<IRequestOptions, 'requestTag'>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be

requestOptions?: Pick<IRequestOptions, 'priority'>;

here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think that's correct. CommitOptions are used for blind writes such as database.table('some-table').insert(..), and those should support setting a transaction tag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(After our conversation earlier today): I've removed transaction-tag from CommitOptions.

projectId: projectId,
});

async function queryWithRpcPriority(instanceId, databaseId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to mention this earlier. There are no code samples for this feature that will get pulled in from docs. So there's a question of whether we should remove the sample and just add an integration test. There's no way to test if the priority is being accepted by the backend but at least you're able to set it.

What do you think?

Comment on lines +494 to +495
// TODO: Enable when RPC Priority has been released.
it.skip('should use request options', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we choose to keep the sample then you can unskip this now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh. I missed this one and went ahead and merged without it. I've opened a new PR for enabling it here: #1338

@olavloite olavloite merged commit 8c82694 into googleapis:master Apr 8, 2021
@olavloite olavloite deleted the rpc-priority branch April 8, 2021 05:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: spanner Issues related to the googleapis/nodejs-spanner API. cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants