feat: inline BeginTransaction with first statement#1692
feat: inline BeginTransaction with first statement#1692surbhigarg92 merged 28 commits intogoogleapis:mainfrom
Conversation
| if (this.id) { | ||
| transaction.id = this.id as Uint8Array; | ||
| } else if (typeof this._options.readWrite !== 'undefined') { | ||
| transaction.begin = this._options; |
There was a problem hiding this comment.
Would you mind adding a test that verifies the following:
- A query is the first statement in a read/write transaction. A transaction ID is successfully returned by initial request.
- One or more
PartialResultSets are returned by the stream, with (at least) one of them returning a resume token. - The stream fails halfway with an
UNAVAILABLEerror and the stream is restarted with a resume token.
Step 3 should use the transaction ID that was returned by step 1, and not include a BeginTransaction option.
(https://github.com/googleapis/nodejs-spanner/pull/1253/files is a very old implementation for the same. Some of the tests there might be re-usable.)
There was a problem hiding this comment.
Added. thanks a lot for this comment: it helped to find a bug in the code
| @@ -1034,6 +1029,8 @@ export class Snapshot extends EventEmitter { | |||
| const transaction: spannerClient.spanner.v1.ITransactionSelector = {}; | |||
| if (this.id) { | |||
There was a problem hiding this comment.
This does not take into account that a transaction could in theory have multiple requests in flight at the same time. If a transaction starts out with sending two SELECT statements to the backend, it might very well be that the first that is sent has not yet returned a transaction id before the second is being sent. That will cause both requests to include a BeginTransaction option.
Consider the following test case (the getRowCountFromStreamingSql function is defined in test/Spanner.ts):
it('should only inline one begin transaction', async () => {
const database = newTestDatabase();
await database.runTransactionAsync(async tx => {
const rowCount1 = getRowCountFromStreamingSql(tx, {sql: selectSql});
const rowCount2 = getRowCountFromStreamingSql(tx, {sql: selectSql});
await Promise.all([rowCount1, rowCount2]);
await tx.commit();
});
await database.close();
let request = spannerMock.getRequests().find(val => {
return (val as v1.ExecuteSqlRequest).sql;
}) as v1.ExecuteSqlRequest;
assert.ok(request, 'no ExecuteSqlRequest found');
assert.deepStrictEqual(request.transaction!.begin!.readWrite, {});
assert.strictEqual(request.sql, selectSql);
request = spannerMock
.getRequests()
.slice()
.reverse()
.find(val => {
return (val as v1.ExecuteSqlRequest).sql;
}) as v1.ExecuteSqlRequest;
assert.ok(request, 'no ExecuteSqlRequest found');
assert.strictEqual(request.sql, selectSql);
assert.ok(request.transaction!.id, 'TransactionID is not set.');
const beginTxnRequest = spannerMock.getRequests().find(val => {
return (val as v1.BeginTransactionRequest).options?.readWrite;
}) as v1.BeginTransactionRequest;
assert.ok(!beginTxnRequest, 'beginTransaction was called');
});There was a problem hiding this comment.
You're absolutely right. Added a lock that prevents a multiple inline begin transaction at the same time. Thanks!
…on happens; make sure stream requests uses a transaction id from the first response even if the stream fails halfway with an UNAVAILABLE error
src/transaction.ts
Outdated
| json, | ||
| jsonOptions, | ||
| maxResumeRetries, | ||
| }).on('response', response => { |
There was a problem hiding this comment.
What would happen if the call fails?
As per the design we should make an explicit begin transaction call if the call fails.
cc: @olavloite
There was a problem hiding this comment.
if the call fails, the transaction is created explicitly with "BeginTransaction" rpc call.
It's covered in the test: "should use beginTransaction on retry".
There was a problem hiding this comment.
Is this done at the backend or do we explicitly call begin transaction from the client side?
There was a problem hiding this comment.
from the client side
src/transaction.ts
Outdated
|
|
||
| if (this.id) { | ||
| transaction.id = this.id as Uint8Array; | ||
| } else if (typeof this._options.readWrite !== 'undefined') { |
There was a problem hiding this comment.
Should this rather be else if(this._options.readWrite)
|
@ko3a4ok Can you please fix the failing test cases ? |
@surbhigarg92, unfortunately, if we just remove it, the explicit beginTransaction call won't be called on the retry. I updated to call it only after the first attempt. Please let me know if it makes sense, or is it better to move the call |
This looks fine. Only thing I am wondering is that with this implementation, we are calling |
Regarding 1.: in this case, we should not create any transaction at all because Regarding 2.: I completely forgot about that, thanks for bringing this up. Added in 2caaa0f |
|
Merge-on-green attempted to merge your PR for 6 hours, but it was not mergeable because either one of your required status checks failed, one of your required reviews was not approved, or there is a do not merge label. Learn more about your required status checks here: https://help.github.com/en/github/administering-a-repository/enabling-required-status-checks. You can remove and reapply the label to re-run the bot. |
feat: add request/response debug logging to gapics, update templates to gax 5 and node 18 (#1671) fix: add json files to tsconfig templates (#1692) (ba6be1d) PiperOrigin-RevId: 735896588 Source-Link: googleapis/googleapis@3419af7 Source-Link: googleapis/googleapis-gen@f35ba11 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZjM1YmExMTQyZjRlMTY4MjIyMzI3ZDg5MmI1ZjZlZTkwOGU1ZDQ2MSJ9
feat: add request/response debug logging to gapics, update templates to gax 5 and node 18 (#1671) fix: add json files to tsconfig templates (#1692) (ba6be1d) PiperOrigin-RevId: 735896588 Source-Link: googleapis/googleapis@3419af7 Source-Link: googleapis/googleapis-gen@f35ba11 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZjM1YmExMTQyZjRlMTY4MjIyMzI3ZDg5MmI1ZjZlZTkwOGU1ZDQ2MSJ9
* feat: add a last field in the PartialResultSet docs: A comment for field `rows` in message `.google.spanner.v1.ResultSet` is changed docs: A comment for field `stats` in message `.google.spanner.v1.ResultSet` is changed docs: A comment for field `precommit_token` in message `.google.spanner.v1.ResultSet` is changed docs: A comment for field `values` in message `.google.spanner.v1.PartialResultSet` is changed docs: A comment for field `chunked_value` in message `.google.spanner.v1.PartialResultSet` is changed docs: A comment for field `stats` in message `.google.spanner.v1.PartialResultSet` is changed docs: A comment for field `precommit_token` in message `.google.spanner.v1.PartialResultSet` is changed docs: A comment for message `ResultSetMetadata` is changed docs: A comment for field `row_type` in message `.google.spanner.v1.ResultSetMetadata` is changed docs: A comment for message `ResultSetStats` is changed docs: A comment for field `query_plan` in message `.google.spanner.v1.ResultSetStats` is changed docs: A comment for field `row_count_lower_bound` in message `.google.spanner.v1.ResultSetStats` is changed PiperOrigin-RevId: 730849734 Source-Link: googleapis/googleapis@fe0fa26 Source-Link: googleapis/googleapis-gen@16051b5 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMTYwNTFiNTkxN2I3NWY2MDNjY2I1ZjQ3N2UyYTQ2NDdiYTExZmE4MiJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * chore: update copyright year for auto-generated protos PiperOrigin-RevId: 731693666 Source-Link: googleapis/googleapis@fd510f8 Source-Link: googleapis/googleapis-gen@42097f7 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNDIwOTdmN2NjNzZlZTdiZGZlY2FlYWVhMTE1YWYyMzU3MTZmZGRiMSJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * chore: update copyright year for auto-generated protos PiperOrigin-RevId: 732130682 Source-Link: googleapis/googleapis@9415ba0 Source-Link: googleapis/googleapis-gen@2905f83 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMjkwNWY4MzM3NTZjMmIyMGIzMjgyYmU4NGI1MTFlMDQwZmU1NGYzMyJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * chore: update typescript gapic generator to 4.8.1 feat: add request/response debug logging to gapics, update templates to gax 5 and node 18 (#1671) fix: add json files to tsconfig templates (#1692) (ba6be1d) PiperOrigin-RevId: 735896588 Source-Link: googleapis/googleapis@3419af7 Source-Link: googleapis/googleapis-gen@f35ba11 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZjM1YmExMTQyZjRlMTY4MjIyMzI3ZDg5MmI1ZjZlZTkwOGU1ZDQ2MSJ9 * feat: await/catch promises, and update listOperationsAsync return type PiperOrigin-RevId: 738212310 Source-Link: googleapis/googleapis@803b234 Source-Link: googleapis/googleapis-gen@4f44bd2 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNGY0NGJkMmJhYThkZDVhNzFjYTBjZWJkYjE2NGMzYzM0MzQxZWQ4NyJ9 * chore: run post-processor locally * chore: migrate to node 18 * fix: imports * changes in tsconfig * fix: import errors * remove script * update tsconfig * fix: eslint errors * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * downgrade dependencies arrify, pqueue, time-span, stack-trace * add spanProcessors property and remove addSpanProcessor * fix: eslint issues and unit tests * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix: unit test * fix: presubmit errors * add node-18 directory * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * rename node-18 directory * update execa import * downgrade execa and p-limit package to support the tests run against emulator * downgrade uuid version * update system-test-against-emulator.yaml * add system-test-multiplexed-session.cfg in node18 * update image and digest in owlbot yaml * downgrade execa and p-limit * fix: presubmit error for tests * update coverage to node 18 version * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * update samples-test.sh * update sha and workflows * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * presubmit fix * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * refactor transaction and session-pool src --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com> Co-authored-by: Sofia Leon <sofialeon@google.com>
feat: add request/response debug logging to gapics, update templates to gax 5 and node 18 (#1671) fix: add json files to tsconfig templates (#1692) (ba6be1d) PiperOrigin-RevId: 735896588 Source-Link: googleapis/googleapis@3419af7 Source-Link: googleapis/googleapis-gen@f35ba11 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZjM1YmExMTQyZjRlMTY4MjIyMzI3ZDg5MmI1ZjZlZTkwOGU1ZDQ2MSJ9
Add inline BeginTransaction for the first read or executeSql request in a transaction.
Each transaction doesn't call
beginTransanctionbefore its start. Instead, all creation transaction parameters are passed within the first read or executeSql request.In case of a transaction restart,
beginTransactionis called explicitly.