diff --git a/spanner/add-and-drop-new-database-role.js b/spanner/add-and-drop-new-database-role.js new file mode 100644 index 0000000000..6414913056 --- /dev/null +++ b/spanner/add-and-drop-new-database-role.js @@ -0,0 +1,98 @@ +// 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. + +// sample-metadata: +// title: Add and drop new database role +// usage: node add-and-drop-new-database-role.js + +'use strict'; + +async function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_add_and_drop_database_role] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + async function addAndDropNewDatabaseRole() { + // Creates a new user defined role and grant permissions + try { + const createRequest = [ + 'CREATE ROLE parent', + 'GRANT SELECT ON TABLE Singers TO ROLE parent', + 'CREATE ROLE child', + 'GRANT ROLE parent TO ROLE child', + ]; + const [createOperation] = await databaseAdminClient.updateDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + statements: createRequest, + }); + + console.log('Waiting for operation to complete...'); + await createOperation.promise(); + + console.log('Created roles child and parent and granted privileges'); + + // Revoke permissions and drop child role. + // A role can't be dropped until all its permissions are revoked. + const dropRequest = [ + 'REVOKE ROLE parent FROM ROLE child', + 'DROP ROLE child', + ]; + const [dropOperation] = await databaseAdminClient.updateDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + statements: dropRequest, + }); + + console.log('Waiting for operation to complete...'); + await dropOperation.promise(); + + console.log('Revoked privileges and dropped role child'); + } catch (err) { + console.error('Error adding or dropping database roles:', err); + } finally { + // Close the spanner client when finished. + // The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient. + spanner.close(); + } + } + await addAndDropNewDatabaseRole(); + // [END spanner_add_and_drop_database_role] +} + +main(...process.argv.slice(2)); diff --git a/spanner/create-instance-without-default-backup-schedules.js b/spanner/create-instance-without-default-backup-schedules.js new file mode 100644 index 0000000000..caa2f6ea3a --- /dev/null +++ b/spanner/create-instance-without-default-backup-schedules.js @@ -0,0 +1,75 @@ +/** + * 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. + */ + +'use strict'; + +function main(instanceId, projectId) { + async function createInstanceWithoutDefaultBackupSchedules() { + // [START spanner_create_instance_without_default_backup_schedule] + /** + * TODO(developer): Uncomment the following lines before running the sample. + **/ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + + // Imports the Google Cloud client library + const {Spanner, protos} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + // Creates a new instance + try { + const [operation] = await instanceAdminClient.createInstance({ + instanceId: instanceId, + parent: instanceAdminClient.projectPath(projectId), + instance: { + config: instanceAdminClient.instanceConfigPath( + projectId, + 'regional-me-central2' + ), + nodeCount: 1, + displayName: 'Display name for the instance.', + labels: { + cloud_spanner_samples: 'true', + created: Math.round(Date.now() / 1000).toString(), // current time + }, + defaultBackupScheduleType: + protos.google.spanner.admin.instance.v1.Instance + .DefaultBackupScheduleType.NONE, + }, + }); + await operation.promise(); + + console.log( + `Created instance ${instanceId} without default backup schedules.` + ); + } catch (err) { + console.error( + 'Error creating instance without default backup schedules:', + err + ); + } finally { + spanner.close(); + } + // [END spanner_create_instance_without_default_backup_schedule] + } + createInstanceWithoutDefaultBackupSchedules(); +} + +main(...process.argv.slice(2)); diff --git a/spanner/get-database-roles.js b/spanner/get-database-roles.js new file mode 100644 index 0000000000..1a4ac9838e --- /dev/null +++ b/spanner/get-database-roles.js @@ -0,0 +1,75 @@ +// 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. + +// sample-metadata: +// title: List database roles +// usage: node get-database-roles.js + +'use strict'; + +async function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_list_database_roles] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + async function getDatabaseRoles() { + try { + const dbPath = databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ); + + // Fetching database roles + const [databaseRoles] = await databaseAdminClient.listDatabaseRoles({ + parent: dbPath, + }); + + console.log(`Roles for Database: ${dbPath}`); + databaseRoles.forEach(role => { + console.log(`Role: ${role.name}`); + }); + } catch (err) { + console.error('Error listing database roles:', err); + } finally { + spanner.close(); + } + } + await getDatabaseRoles(); + // [END spanner_list_database_roles] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/instance-update.js b/spanner/instance-update.js new file mode 100644 index 0000000000..ec9a9f42bc --- /dev/null +++ b/spanner/instance-update.js @@ -0,0 +1,87 @@ +/** + * 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. + */ + +// sample-metadata: +// title: Updates an instance. +// usage: node instance-update.js + +'use strict'; + +function main(instanceId, projectId) { + async function updateInstance() { + // [START spanner_update_instance] + + // Imports the Google Cloud client library + const {Spanner, protos} = require('@google-cloud/spanner'); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + + // Updates an instance + try { + const instancePath = instanceAdminClient.instancePath( + projectId, + instanceId + ); + + console.log(`Updating instance ${instancePath}.`); + + const [operation] = await instanceAdminClient.updateInstance({ + instance: { + name: instancePath, + labels: { + updated: 'true', + created: Math.round(Date.now() / 1000).toString(), // current time + }, + edition: + protos.google.spanner.admin.instance.v1.Instance.Edition.ENTERPRISE, //optional + }, + // Field mask specifying fields that should get updated in an Instance + fieldMask: { + paths: ['labels', 'edition'], + }, + }); + + console.log(`Waiting for operation on ${instanceId} to complete...`); + await operation.promise(); + console.log(`Updated instance ${instanceId}.`); + const [metadata] = await instanceAdminClient.getInstance({ + name: instanceAdminClient.instancePath(projectId, instanceId), + }); + console.log( + `Instance ${instanceId} has been updated with the ${metadata.edition} ` + + 'edition.' + ); + } catch (err) { + console.error('Error updating instance:', err); + } finally { + spanner.close(); + } + // [END spanner_update_instance] + } + updateInstance(); +} + +main(...process.argv.slice(2)); diff --git a/spanner/instance-with-asymmetric-autoscaling-config.js b/spanner/instance-with-asymmetric-autoscaling-config.js new file mode 100644 index 0000000000..e626d0e142 --- /dev/null +++ b/spanner/instance-with-asymmetric-autoscaling-config.js @@ -0,0 +1,166 @@ +/** + * 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: Creates a instance with asymmetric autoscaling config. +// usage: node instance-with-asymmetric-autoscaling-config.js + +'use strict'; + +function main(instanceId = 'my-instance', projectId = 'my-project-id') { + async function createInstanceWithAsymmetricAutoscalingConfig() { + // [START spanner_create_instance_with_asymmetric_autoscaling_config] + // Imports the Google Cloud client library + const {Spanner, protos} = require('@google-cloud/spanner'); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + // Get the instance admin client + const instanceAdminClient = spanner.getInstanceAdminClient(); + + const autoscalingConfig = + protos.google.spanner.admin.instance.v1.AutoscalingConfig.create({ + // Only one of minNodes/maxNodes or minProcessingUnits/maxProcessingUnits can be set. + autoscalingLimits: + protos.google.spanner.admin.instance.v1.AutoscalingConfig.AutoscalingLimits.create( + { + minNodes: 1, + maxNodes: 2, + } + ), + // highPriorityCpuUtilizationPercent and storageUtilizationPercent are both + // percentages and must lie between 0 and 100. + autoscalingTargets: + protos.google.spanner.admin.instance.v1.AutoscalingConfig.AutoscalingTargets.create( + { + highPriorityCpuUtilizationPercent: 65, + storageUtilizationPercent: 95, + } + ), + // The read-only replicas listed in the asymmetric autoscaling options scale independently + // from other replicas. + asymmetricAutoscalingOptions: [ + protos.google.spanner.admin.instance.v1.AutoscalingConfig.AsymmetricAutoscalingOption.create( + { + replicaSelection: + protos.google.spanner.admin.instance.v1.ReplicaSelection.create( + { + location: 'europe-west1', + } + ), + } + ), + protos.google.spanner.admin.instance.v1.AutoscalingConfig.AsymmetricAutoscalingOption.create( + { + replicaSelection: + protos.google.spanner.admin.instance.v1.ReplicaSelection.create( + { + location: 'europe-west4', + } + ), + } + ), + protos.google.spanner.admin.instance.v1.AutoscalingConfig.AsymmetricAutoscalingOption.create( + { + replicaSelection: + protos.google.spanner.admin.instance.v1.ReplicaSelection.create( + { + location: 'asia-east1', + } + ), + } + ), + ], + }); + + // Creates a new instance with autoscaling configuration and asymmetric autoscaling option + // When autoscalingConfig is enabled, nodeCount and processingUnits fields + // need not be specified. + try { + const instancePath = instanceAdminClient.instancePath( + projectId, + instanceId + ); + + console.log(`Creating instance ${instancePath}.`); + + const [operation] = await instanceAdminClient.createInstance({ + instanceId: instanceId, + parent: instanceAdminClient.projectPath(projectId), + instance: { + config: instanceAdminClient.instanceConfigPath( + projectId, + 'nam-eur-asia3' + ), + displayName: 'Display name for the instance.', + autoscalingConfig: autoscalingConfig, + labels: { + cloud_spanner_samples: 'true', + created: Math.round(Date.now() / 1000).toString(), // current time + }, + // Feature MULTI_REGION is available only for ENTERPRISE_PLUS edition + edition: + protos.google.spanner.admin.instance.v1.Instance.Edition + .ENTERPRISE_PLUS, + }, + }); + + console.log(`Waiting for operation on ${instanceId} to complete...`); + await operation.promise(); + console.log(`Created instance ${instanceId}.`); + + // get instance metadata + const [metadata] = await instanceAdminClient.getInstance({ + name: instancePath, + }); + const asymmetricOptionsStr = + metadata.autoscalingConfig.asymmetricAutoscalingOptions?.length > 0 + ? metadata.autoscalingConfig.asymmetricAutoscalingOptions + .map(option => option.replicaSelection?.location || 'N/A') + .join(', ') + : 'None'; + + console.log( + `Autoscaling configurations of ${instanceId} are: ` + + '\n' + + `Min nodes: ${metadata.autoscalingConfig.autoscalingLimits.minNodes} nodes.\n` + + `Max nodes: ${metadata.autoscalingConfig.autoscalingLimits.maxNodes} nodes.\n` + + `High priority cpu utilization percent: ${metadata.autoscalingConfig.autoscalingTargets.highPriorityCpuUtilizationPercent}.\n` + + `Storage utilization percent: ${metadata.autoscalingConfig.autoscalingTargets.storageUtilizationPercent}.\n` + + `Asymmetric Autoscaling Options: ${asymmetricOptionsStr}` + ); + } catch (err) { + console.error( + 'Error creating instance with asymmetric autoscaling config:', + err + ); + } finally { + spanner.close(); + } + // [END spanner_create_instance_with_asymmetric_autoscaling_config] + } + createInstanceWithAsymmetricAutoscalingConfig(); +} + +main(...process.argv.slice(2)); diff --git a/spanner/instance-with-autoscaling-config.js b/spanner/instance-with-autoscaling-config.js new file mode 100644 index 0000000000..46cf8390dd --- /dev/null +++ b/spanner/instance-with-autoscaling-config.js @@ -0,0 +1,120 @@ +/** + * 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. + */ + +// sample-metadata: +// title: Creates a instance with autoscaling config. +// usage: node instance-with-autoscaling-config.js + +'use strict'; + +function main(instanceId = 'my-instance', projectId = 'my-project-id') { + async function createInstanceWithAutoscalingConfig() { + // [START spanner_create_instance_with_autoscaling_config] + // Imports the Google Cloud client library + const {Spanner, protos} = require('@google-cloud/spanner'); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + // Get the instance admin client + const instanceAdminClient = spanner.getInstanceAdminClient(); + + const autoscalingConfig = + protos.google.spanner.admin.instance.v1.AutoscalingConfig.create({ + // Only one of minNodes/maxNodes or minProcessingUnits/maxProcessingUnits can be set. + autoscalingLimits: + protos.google.spanner.admin.instance.v1.AutoscalingConfig.AutoscalingLimits.create( + { + minNodes: 1, + maxNodes: 2, + } + ), + // highPriorityCpuUtilizationPercent and storageUtilizationPercent are both + // percentages and must lie between 0 and 100. + autoscalingTargets: + protos.google.spanner.admin.instance.v1.AutoscalingConfig.AutoscalingTargets.create( + { + highPriorityCpuUtilizationPercent: 65, + storageUtilizationPercent: 95, + } + ), + }); + + // Creates a new instance with autoscaling configuration + // When autoscalingConfig is enabled, nodeCount and processingUnits fields + // need not be specified. + try { + const instancePath = instanceAdminClient.instancePath( + projectId, + instanceId + ); + + console.log(`Creating instance ${instancePath}.`); + + const [operation] = await instanceAdminClient.createInstance({ + instanceId: instanceId, + parent: instanceAdminClient.projectPath(projectId), + instance: { + config: instanceAdminClient.instanceConfigPath( + projectId, + 'regional-us-central1' + ), + displayName: 'Display name for the instance.', + autoscalingConfig: autoscalingConfig, + labels: { + cloud_spanner_samples: 'true', + created: Math.round(Date.now() / 1000).toString(), // current time + }, + // Managed autoscaler is available only for ENTERPRISE edition + edition: + protos.google.spanner.admin.instance.v1.Instance.Edition.ENTERPRISE, + }, + }); + + console.log(`Waiting for operation on ${instanceId} to complete...`); + await operation.promise(); + console.log(`Created instance ${instanceId}.`); + + // get instance metadata + const [metadata] = await instanceAdminClient.getInstance({ + name: instancePath, + }); + console.log( + `Autoscaling configurations of ${instanceId} are: ` + + '\n' + + `Min nodes: ${metadata.autoscalingConfig.autoscalingLimits.minNodes} nodes.\n` + + `Max nodes: ${metadata.autoscalingConfig.autoscalingLimits.maxNodes} nodes.\n` + + `High priority cpu utilization percent: ${metadata.autoscalingConfig.autoscalingTargets.highPriorityCpuUtilizationPercent}.\n` + + `Storage utilization percent: ${metadata.autoscalingConfig.autoscalingTargets.storageUtilizationPercent}.` + ); + } catch (err) { + console.error('Error creating instance with autoscaling config:', err); + } finally { + spanner.close(); + } + // [END spanner_create_instance_with_autoscaling_config] + } + createInstanceWithAutoscalingConfig(); +} + +main(...process.argv.slice(2)); diff --git a/spanner/instance-with-processing-units.js b/spanner/instance-with-processing-units.js new file mode 100644 index 0000000000..57d0b4c37c --- /dev/null +++ b/spanner/instance-with-processing-units.js @@ -0,0 +1,81 @@ +/** + * 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. + */ + +'use strict'; + +async function createInstanceWithProcessingUnits(instanceId, projectId) { + // [START spanner_create_instance_with_processing_units] + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + + // Creates a new instance + try { + const instancePath = instanceAdminClient.instancePath( + projectId, + instanceId + ); + console.log(`Creating instance ${instancePath}.`); + + const [operation] = await instanceAdminClient.createInstance({ + instanceId: instanceId, + instance: { + config: instanceAdminClient.instanceConfigPath( + projectId, + 'regional-us-central1' + ), + displayName: 'Display name for the instance.', + processingUnits: 500, + labels: { + cloud_spanner_samples: 'true', + created: Math.round(Date.now() / 1000).toString(), // current time + }, + }, + parent: instanceAdminClient.projectPath(projectId), + }); + + console.log(`Waiting for operation on ${instanceId} to complete...`); + await operation.promise(); + console.log(`Created instance ${instanceId}.`); + const [metadata] = await instanceAdminClient.getInstance({ + name: instancePath, + }); + console.log( + `Instance ${instanceId} has ${metadata.processingUnits} ` + + 'processing units.' + ); + } catch (err) { + console.error('Error creating instance with processing units:', err); + } finally { + spanner.close(); + } + // [END spanner_create_instance_with_processing_units] +} + +module.exports.createInstanceWithProcessingUnits = + createInstanceWithProcessingUnits; diff --git a/spanner/instance.js b/spanner/instance.js new file mode 100644 index 0000000000..08c8d1aa38 --- /dev/null +++ b/spanner/instance.js @@ -0,0 +1,102 @@ +/** + * 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. + */ + +'use strict'; + +// creates an instance using Instance Admin Client +async function createInstance(instanceId, projectId) { + // [START spanner_create_instance] + + // Imports the Google Cloud client library + const {Spanner, protos} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + /** + * TODO(developer): Uncomment the following lines before running the sample. + **/ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + + // Creates a new instance + try { + const instancePath = instanceAdminClient.instancePath( + projectId, + instanceId + ); + console.log(`Creating instance ${instancePath}.`); + + const [operation] = await instanceAdminClient.createInstance({ + instanceId: instanceId, + parent: instanceAdminClient.projectPath(projectId), + instance: { + config: instanceAdminClient.instanceConfigPath( + projectId, + 'regional-us-central1' + ), + nodeCount: 1, + displayName: 'Display name for the instance.', + labels: { + cloud_spanner_samples: 'true', + created: Math.round(Date.now() / 1000).toString(), // current time + }, + edition: + protos.google.spanner.admin.instance.v1.Instance.Edition.STANDARD, //optional + }, + }); + + console.log(`Waiting for operation on ${instanceId} to complete...`); + await operation.promise(); + + console.log(`Created instance ${instanceId}.`); + } catch (err) { + console.error('ERROR:', err); + } finally { + spanner.close(); + } + // [END spanner_create_instance] +} + +const { + createInstanceWithProcessingUnits, +} = require('./instance-with-processing-units'); + +require('yargs') + .demand(1) + .command( + 'createInstance ', + 'Creates an example instance in a Cloud Spanner instance using Instance Admin Client.', + {}, + opts => createInstance(opts.instanceName, opts.projectId) + ) + .example('node $0 createInstance "my-instance" "my-project-id"') + .command( + 'createInstanceWithProcessingUnits ', + 'Creates an example instance in a Cloud Spanner instance with processing units.', + {}, + opts => createInstanceWithProcessingUnits(opts.instanceName, opts.projectId) + ) + .example( + 'node $0 createInstanceWithProcessingUnits "my-instance" "my-project-id"' + ) + .wrap(120) + .recommendCommands() + .epilogue('For more information, see https://cloud.google.com/spanner/docs') + .strict() + .help().argv; diff --git a/spanner/read-data-with-database-role.js b/spanner/read-data-with-database-role.js new file mode 100644 index 0000000000..70eaf1a05d --- /dev/null +++ b/spanner/read-data-with-database-role.js @@ -0,0 +1,74 @@ +// Copyright 2022 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: Read data with database role +// usage: node read-data-with-database-role.js + +'use strict'; + +async function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_read_data_with_database_role] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function readDataWithDatabaseRole() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + // Connect to a database using the 'parent' database role. This means that the connection will only have the permissions that have explicitly been granted to the 'parent' role. + const options = { + databaseRole: 'parent', + }; + const database = instance.database(databaseId, options); + + try { + const query = { + sql: 'SELECT SingerId, FirstName, LastName FROM Singers', + }; + const [rows] = await database.run(query); + + for (const row of rows) { + const json = row.toJSON(); + + console.log( + `SingerId: ${json.SingerId}, FirstName: ${json.FirstName}, LastName: ${json.LastName}` + ); + } + } catch (err) { + console.error('Error reading data with database role:', err); + } finally { + // Close the database when finished. + await database.close(); + } + } + await readDataWithDatabaseRole(); + // [END spanner_read_data_with_database_role] +} + +main(...process.argv.slice(2)); diff --git a/spanner/read-lock-mode.js b/spanner/read-lock-mode.js new file mode 100644 index 0000000000..d1485f2f17 --- /dev/null +++ b/spanner/read-lock-mode.js @@ -0,0 +1,96 @@ +// Copyright 2026 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 read lock mode option +// usage: node read-lock-mode.js + +'use strict'; + +async function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_read_lock_mode] + // Imports the Google Cloud Spanner client library + const {Spanner, protos} = require('@google-cloud/spanner'); + // The read lock mode specified at the client-level will be applied + // to all RW transactions. + const defaultTransactionOptions = { + readLockMode: + protos.google.spanner.v1.TransactionOptions.ReadWrite.ReadLockMode + .OPTIMISTIC, + }; + + // Instantiates a client with defaultTransactionOptions + const spanner = new Spanner({ + projectId: projectId, + defaultTransactionOptions, + }); + + async function runTransactionWithReadLockMode() { + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + // The read lock mode specified at the request-level takes precedence over + // the read lock mode configured at the client-level. + const readLockModeOptionsForTransaction = { + readLockMode: + protos.google.spanner.v1.TransactionOptions.ReadWrite.ReadLockMode + .PESSIMISTIC, + }; + + try { + // Use runTransactionAsync to safely handle the transaction lifecycle + await database.runTransactionAsync( + readLockModeOptionsForTransaction, + async transaction => { + const query = + 'SELECT AlbumTitle FROM Albums WHERE SingerId = 2 AND AlbumId = 1'; + + const [rows] = await transaction.run(query); + + // Gets first album's title cleanly using native .toJSON() + const albumTitle = rows[0].toJSON().AlbumTitle; + console.log(`previous album title ${albumTitle}`); + + const update = + "UPDATE Albums SET AlbumTitle = 'New Album Title' WHERE SingerId = 2 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 readLockMode option.' + ); + } + ); + } catch (err) { + console.error( + 'Error executing read-write transaction with read lock mode:', + err + ); + } finally { + // Close the database when finished. + await database.close(); + } + } + await runTransactionWithReadLockMode(); + // [END spanner_read_lock_mode] +} + +main(...process.argv.slice(2)); diff --git a/spanner/repeatable-reads.js b/spanner/repeatable-reads.js new file mode 100644 index 0000000000..42ab774bd0 --- /dev/null +++ b/spanner/repeatable-reads.js @@ -0,0 +1,90 @@ +// 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'; + +async 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 defaultTransactionOptions = { + isolationLevel: + protos.google.spanner.v1.TransactionOptions.IsolationLevel.SERIALIZABLE, + }; + + // Instantiates a client with defaultTransactionOptions + const spanner = new Spanner({ + projectId: projectId, + defaultTransactionOptions, + }); + + async 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, + }; + try { + // Use runTransactionAsync to safely handle the transaction lifecycle + await database.runTransactionAsync( + isolationOptionsForTransaction, + async transaction => { + const query = + 'SELECT AlbumTitle FROM Albums WHERE SingerId = 1 AND AlbumId = 1'; + + const [rows] = await transaction.run(query); + + // Gets first album's title + const albumTitle = rows[0].toJSON().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 { + // Close the database when finished. + await database.close(); + } + } + await runTransactionWithIsolationLevel(); + // [END spanner_isolation_level] +} + +main(...process.argv.slice(2)); diff --git a/spanner/request-tag.js b/spanner/request-tag.js new file mode 100644 index 0000000000..87724251de --- /dev/null +++ b/spanner/request-tag.js @@ -0,0 +1,65 @@ +/** + * Copyright 2021 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: Sets a request tag for a single query +// usage: node request-tag.js + +'use strict'; + +function main(instanceId, databaseId, projectId) { + // [START spanner_set_request_tag] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function queryTags() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + try { + // Execute a query with a request tag. + const [albums] = await database.run({ + sql: 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums', + requestOptions: {requestTag: 'app=concert,env=dev,action=select'}, + json: true, + }); + albums.forEach(album => { + console.log( + `SingerId: ${album.SingerId}, AlbumId: ${album.AlbumId}, AlbumTitle: ${album.AlbumTitle}` + ); + }); + } catch (err) { + console.error('Error executing query with request tag:', err); + } finally { + await database.close(); + } + } + queryTags(); + // [END spanner_set_request_tag] +} + +main(...process.argv.slice(2)); diff --git a/spanner/statement-timeout.js b/spanner/statement-timeout.js new file mode 100644 index 0000000000..9bd061ccb8 --- /dev/null +++ b/spanner/statement-timeout.js @@ -0,0 +1,70 @@ +/** + * 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: Executes a read/write transaction with statement timeout +// usage: node statement-timeout.js + +'use strict'; + +async function main(instanceId, databaseId, projectId) { + // [START spanner_set_statement_timeout] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function executeSqlWithTimeout() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + try { + await database.runTransactionAsync(async tx => { + // NOTE: You can use gaxOptions to set a custom timeout for a single RPC + // invocation. This timeout can however ONLY BE SHORTER than the default timeout + // for the RPC. If you set a timeout that is longer than the default timeout, then + // the default timeout will be used. + const query = { + sql: "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES (110, 'George', 'Washington')", + gaxOptions: { + timeout: 60000, // 60 seconds timeout + }, + }; + const [, stats] = await tx.run(query); + console.log(`${stats.rowCountExact} record inserted.`); + await tx.commit(); + }); + } catch (err) { + console.error('ERROR:', err); + } finally { + await database.close(); + } + } + await executeSqlWithTimeout(); + // [END spanner_set_statement_timeout] +} + +main(...process.argv.slice(2)); diff --git a/spanner/system-test/spanner.test.js b/spanner/system-test/spanner.test.js index cdc76f49d5..b31bb249ea 100644 --- a/spanner/system-test/spanner.test.js +++ b/spanner/system-test/spanner.test.js @@ -1314,7 +1314,7 @@ describe('Autogenerated Admin Clients', () => { }); // query with request tag - it.skip('should execute a query with a request tag', async () => { + it('should execute a query with a request tag', async () => { const output = execSync( `${requestTagCommand} ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1322,7 +1322,7 @@ describe('Autogenerated Admin Clients', () => { }); // read_write_transaction with transaction tag - it.skip('should execute a read/write transaction with a transaction tag', async () => { + it('should execute a read/write transaction with a transaction tag', async () => { const output = execSync( `${transactionTagCommand} ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1330,7 +1330,7 @@ describe('Autogenerated Admin Clients', () => { }); // read_write_transaction with transaction timeout - it.skip('should execute a read/write transaction with a transaction timeout of 60 seconds', async () => { + it('should execute a read/write transaction with a transaction timeout of 60 seconds', async () => { const output = execSync( `${transactionTimeoutCommand} ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1338,7 +1338,7 @@ describe('Autogenerated Admin Clients', () => { }); // read_write_transaction with statement timeout - it.skip('should execute a read/write transaction with a statement timeout of 60 seconds', async () => { + it('should execute a read/write transaction with a statement timeout of 60 seconds', async () => { const output = execSync( `${statementTimeoutCommand} ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1346,7 +1346,7 @@ describe('Autogenerated Admin Clients', () => { }); // add_json_column - it.skip('should add a VenueDetails column to Venues example table', async () => { + it('should add a VenueDetails column to Venues example table', async () => { const output = execSync( `${datatypesCmd} addJsonColumn "${INSTANCE_ID}" "${DATABASE_ID}" ${PROJECT_ID}` ); @@ -1362,7 +1362,7 @@ describe('Autogenerated Admin Clients', () => { }); // update_data_with_json - it.skip('should update rows in Venues example table to add data in VenueDetails column', async () => { + it('should update rows in Venues example table to add data in VenueDetails column', async () => { const output = execSync( `${datatypesCmd} updateWithJsonData ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1370,7 +1370,7 @@ describe('Autogenerated Admin Clients', () => { }); // query_with_json_parameter - it.skip('should use a JSON query parameter to query records from the Venues example table', async () => { + it('should use a JSON query parameter to query records from the Venues example table', async () => { const output = execSync( `${datatypesCmd} queryWithJsonParameter ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1378,7 +1378,7 @@ describe('Autogenerated Admin Clients', () => { }); // isolation_level_option - it.skip('should run read-write transaction with isolation level option set', () => { + it('should run read-write transaction with isolation level option set', () => { const output = execSync( `node repeatable-reads.js ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1396,7 +1396,7 @@ describe('Autogenerated Admin Clients', () => { }); // read_lock_mode_option - it.skip('should run read-write transaction with read lock mode option set', () => { + it('should run read-write transaction with read lock mode option set', () => { const output = execSync( `node read-lock-mode.js ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1414,7 +1414,7 @@ describe('Autogenerated Admin Clients', () => { }); // add_and_drop_new_database_role - it.skip('should add and drop new database roles', async () => { + it('should add and drop new database roles', async () => { const output = execSync( `node add-and-drop-new-database-role.js ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1430,7 +1430,7 @@ describe('Autogenerated Admin Clients', () => { }); // read_data_with_database_role - it.skip('should read data with database role', async () => { + it('should read data with database role', async () => { const output = execSync( `node read-data-with-database-role.js ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1441,7 +1441,7 @@ describe('Autogenerated Admin Clients', () => { }); // get_database_roles - it.skip('should list database roles', async () => { + it('should list database roles', async () => { const output = execSync( `node get-database-roles.js ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); diff --git a/spanner/transaction-tag.js b/spanner/transaction-tag.js new file mode 100644 index 0000000000..b92835ea45 --- /dev/null +++ b/spanner/transaction-tag.js @@ -0,0 +1,90 @@ +/** + * Copyright 2021 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: Executes a read/write transaction with transaction and request tags +// usage: node transaction-tag.js + +'use strict'; + +function main(instanceId, databaseId, projectId) { + // [START spanner_set_transaction_tag] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function transactionTag() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // Run a transaction with a transaction tag that will automatically be + // included with each request in the transaction. + try { + await database.runTransactionAsync( + {requestOptions: {transactionTag: 'app=cart,env=dev'}}, + async tx => { + // Set the request tag to "app=concert,env=dev,action=update". + // This request tag will only be set on this request. + await tx.runUpdate({ + sql: 'UPDATE Venues SET Capacity = DIV(Capacity, 4) WHERE OutdoorVenue = false', + requestOptions: {requestTag: 'app=concert,env=dev,action=update'}, + }); + console.log('Updated capacity of all indoor venues to 1/4.'); + + await tx.runUpdate({ + sql: `INSERT INTO Venues (VenueId, VenueName, Capacity, OutdoorVenue, LastUpdateTime) + VALUES (@venueId, @venueName, @capacity, @outdoorVenue, PENDING_COMMIT_TIMESTAMP())`, + params: { + venueId: 81, + venueName: 'Venue 81', + capacity: 1440, + outdoorVenue: true, + }, + types: { + venueId: {type: 'int64'}, + venueName: {type: 'string'}, + capacity: {type: 'int64'}, + outdoorVenue: {type: 'bool'}, + }, + requestOptions: {requestTag: 'app=concert,env=dev,action=update'}, + }); + console.log('Inserted new outdoor venue'); + + await tx.commit(); + } + ); + } catch (err) { + console.error('ERROR:', err); + } finally { + await database.close(); + } + } + transactionTag(); + // [END spanner_set_transaction_tag] +} + +main(...process.argv.slice(2)); diff --git a/spanner/transaction-timeout.js b/spanner/transaction-timeout.js new file mode 100644 index 0000000000..abd6d8c5e9 --- /dev/null +++ b/spanner/transaction-timeout.js @@ -0,0 +1,75 @@ +/** + * 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: Executes a read/write transaction with transaction timeout +// usage: node transaction-timeout.js + +'use strict'; + +async function main(instanceId, databaseId, projectId) { + // [START spanner_transaction_timeout] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function executeTransactionWithTimeout() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + const options = { + timeout: 60000, // 60 seconds timeout + }; + + try { + await database.runTransactionAsync(options, async tx => { + const [results] = await tx.run( + 'SELECT SingerId, FirstName, LastName FROM Singers ORDER BY LastName, FirstName' + ); + results.forEach(result => { + const jsonRow = result.toJSON(); + console.log( + `SingerId: ${jsonRow.SingerId}, FirstName: ${jsonRow.FirstName}, LastName: ${jsonRow.LastName}` + ); + }); + const sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES (100, 'George', 'Washington')"; + const [rowCount] = await tx.runUpdate(sql); + console.log(`${rowCount} record inserted.`); + await tx.commit(); + }); + } catch (err) { + console.error('ERROR:', err); + } finally { + await database.close(); + } + } + await executeTransactionWithTimeout(); + // [END spanner_transaction_timeout] +} + +main(...process.argv.slice(2)); diff --git a/spanner/update-instance-default-backup-schedule-type.js b/spanner/update-instance-default-backup-schedule-type.js new file mode 100644 index 0000000000..603e3eda33 --- /dev/null +++ b/spanner/update-instance-default-backup-schedule-type.js @@ -0,0 +1,78 @@ +/** + * 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. + */ + +// sample-metadata: +// title: Updates an instance. +// usage: node instance-update.js + +'use strict'; + +function main(instanceId, projectId) { + async function updateInstanceDefaultBackupScheduleType() { + // [START spanner_update_instance_default_backup_schedule_type] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + + // Imports the Google Cloud client library + const {Spanner, protos} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + const instanceAdminClient = spanner.getInstanceAdminClient(); + + // Updates an instance + try { + const instancePath = instanceAdminClient.instancePath( + projectId, + instanceId + ); + + const [operation] = await instanceAdminClient.updateInstance({ + instance: { + name: instancePath, + defaultBackupScheduleType: + protos.google.spanner.admin.instance.v1.Instance + .DefaultBackupScheduleType.AUTOMATIC, // optional + }, + // Field mask specifying fields that should get updated in an Instance + fieldMask: { + paths: ['default_backup_schedule_type'], + }, + }); + + await operation.promise(); + const [metadata] = await instanceAdminClient.getInstance({ + name: instancePath, + }); + console.log( + `Instance ${instanceId} has been updated with the ${metadata.defaultBackupScheduleType}` + + ' default backup schedule type.' + ); + } catch (err) { + console.error('ERROR:', err); + } finally { + spanner.close(); + } + // [END spanner_update_instance_default_backup_schedule_type] + } + updateInstanceDefaultBackupScheduleType(); +} + +main(...process.argv.slice(2));