diff --git a/README.md b/README.md index e9e138334..4622693ae 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/nodejs-spanner/tre | Queryoptions | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/queryoptions.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/queryoptions.js,samples/README.md) | | Quickstart | [source code](https://github.com/googleapis/nodejs-spanner/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-spanner&page=editor&open_in_editor=samples/quickstart.js,samples/README.md) | | Read data with database role | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/read-data-with-database-role.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/read-data-with-database-role.js,samples/README.md) | +| Performs a read-write transaction with isolation level option | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/repeatable-reads.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/repeatable-reads.js,samples/README.md) | | Sets a request tag for a single query | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/request-tag.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/request-tag.js,samples/README.md) | | Run Batch update with RPC priority | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/rpc-priority-batch-dml.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/rpc-priority-batch-dml.js,samples/README.md) | | Run partitioned update with RPC priority | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/rpc-priority-partitioned-dml.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/rpc-priority-partitioned-dml.js,samples/README.md) | diff --git a/samples/README.md b/samples/README.md index b7670c8c7..be14882d0 100644 --- a/samples/README.md +++ b/samples/README.md @@ -116,6 +116,7 @@ and automatic, synchronous replication for high availability. * [Queryoptions](#queryoptions) * [Quickstart](#quickstart) * [Read data with database role](#read-data-with-database-role) + * [Performs a read-write transaction with isolation level option](#performs-a-read-write-transaction-with-isolation-level-option) * [Sets a request tag for a single query](#sets-a-request-tag-for-a-single-query) * [Run Batch update with RPC priority](#run-batch-update-with-rpc-priority) * [Run partitioned update with RPC priority](#run-partitioned-update-with-rpc-priority) @@ -1888,6 +1889,23 @@ __Usage:__ +### Performs a read-write transaction with isolation level option + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/repeatable-reads.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/repeatable-reads.js,samples/README.md) + +__Usage:__ + + +`node repeatable-reads.js ` + + +----- + + + + ### Sets a request tag for a single query View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/request-tag.js). diff --git a/samples/repeatable-reads.js b/samples/repeatable-reads.js new file mode 100644 index 000000000..2212fe1a8 --- /dev/null +++ b/samples/repeatable-reads.js @@ -0,0 +1,98 @@ +// Copyright 2025 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. + +// sample-metadata: +// title: Performs a read-write transaction with isolation level option +// usage: node repeatable-reads.js + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id', +) { + // [START spanner_isolation_level] + // Imports the Google Cloud Spanner client library + const {Spanner, protos} = require('@google-cloud/spanner'); + // The isolation level specified at the client-level will be applied + // to all RW transactions. + const isolationOptionsForClient = { + defaultTransactionOptions: { + isolationLevel: + protos.google.spanner.v1.TransactionOptions.IsolationLevel.SERIALIZABLE, + }, + }; + + // Instantiates a client with defaultTransactionOptions + const spanner = new Spanner({ + projectId: projectId, + defaultTransactionOptions: isolationOptionsForClient, + }); + + function runTransactionWithIsolationLevel() { + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + // The isolation level specified at the request level takes precedence over the isolation level configured at the client level. + const isolationOptionsForTransaction = { + isolationLevel: + protos.google.spanner.v1.TransactionOptions.IsolationLevel + .REPEATABLE_READ, + }; + + database.runTransaction( + isolationOptionsForTransaction, + async (err, transaction) => { + if (err) { + console.error(err); + return; + } + try { + const query = + 'SELECT AlbumTitle FROM Albums WHERE SingerId = 1 AND AlbumId = 1'; + const results = await transaction.run(query); + // Gets first album's title + const rows = results[0].map(row => row.toJSON()); + const albumTitle = rows[0].AlbumTitle; + console.log(`previous album title ${albumTitle}`); + + const update = + "UPDATE Albums SET AlbumTitle = 'New Album Title' WHERE SingerId = 1 AND AlbumId = 1"; + const [rowCount] = await transaction.runUpdate(update); + console.log( + `Successfully updated ${rowCount} record in Albums table.`, + ); + await transaction.commit(); + console.log( + 'Successfully executed read-write transaction with isolationLevel option.', + ); + } catch (err) { + console.error('ERROR:', err); + } finally { + transaction.end(); + // Close the database when finished. + await database.close(); + } + }, + ); + } + runTransactionWithIsolationLevel(); + // [END spanner_isolation_level] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/system-test/spanner.test.js b/samples/system-test/spanner.test.js index a7f93857f..8a049720b 100644 --- a/samples/system-test/spanner.test.js +++ b/samples/system-test/spanner.test.js @@ -677,6 +677,24 @@ describe('Autogenerated Admin Clients', () => { ); }); + // isolation_level_option + it('should run read-write transaction with isolation level option set', () => { + const output = execSync( + `node repeatable-reads.js ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}`, + ); + console.log(output); + assert.match( + output, + new RegExp('Successfully updated 1 record into the Singers table.'), + ); + assert.match( + output, + new RegExp( + 'Successfully executed read-write transaction with isolationLevel option.', + ), + ); + }); + // query with RPC priority for run command it('should use RPC priority from request options for run command', async () => { const output = execSync(