diff --git a/spanner/spanner_isolation_level.rb b/spanner/spanner_isolation_level.rb new file mode 100644 index 000000000..b1d3434e6 --- /dev/null +++ b/spanner/spanner_isolation_level.rb @@ -0,0 +1,36 @@ +# Copyright 2026 Google, Inc +# +# 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. + +# [START spanner_isolation_level] +require "google/cloud/spanner" + +def spanner_isolation_level project_id:, instance_id:, database_id: + # Instantiates a client with isolation_level: :SERIALIZABLE + spanner = Google::Cloud::Spanner.new project: project_id + client = spanner.client instance_id, database_id, isolation_level: :SERIALIZABLE + + # Overrides isolation_level to :REPEATABLE_READ at transaction level + client.transaction isolation_level: :REPEATABLE_READ do |tx| + results = tx.execute_query "SELECT AlbumTitle FROM Albums WHERE SingerId = 1 AND AlbumId = 1" + + results.rows.each do |row| + puts "AlbumTitle: #{row[:AlbumTitle]}" + end + + row_count = tx.execute_update "UPDATE Albums SET AlbumTitle = 'A New Title' WHERE SingerId = 1 AND AlbumId = 1" + + puts "#{row_count} records updated." + end +end +# [END spanner_isolation_level] diff --git a/spanner/spanner_read_lock_mode.rb b/spanner/spanner_read_lock_mode.rb new file mode 100644 index 000000000..85b8eaac4 --- /dev/null +++ b/spanner/spanner_read_lock_mode.rb @@ -0,0 +1,36 @@ +# Copyright 2026 Google, Inc +# +# 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. + +# [START spanner_read_lock_mode] +require "google/cloud/spanner" + +def spanner_read_lock_mode project_id:, instance_id:, database_id: + # Instantiates a client with read_lock_mode: :OPTIMISTIC + spanner = Google::Cloud::Spanner.new project: project_id + client = spanner.client instance_id, database_id, read_lock_mode: :OPTIMISTIC + + # Overrides read_lock_mode to :PESSIMISTIC at transaction level + client.transaction read_lock_mode: :PESSIMISTIC do |tx| + results = tx.execute_query "SELECT AlbumTitle FROM Albums WHERE SingerId = 2 AND AlbumId = 1" + + results.rows.each do |row| + puts "AlbumTitle: #{row[:AlbumTitle]}" + end + + row_count = tx.execute_update "UPDATE Albums SET AlbumTitle = 'A New Title' WHERE SingerId = 2 AND AlbumId = 1" + + puts "#{row_count} records updated." + end +end +# [END spanner_read_lock_mode] diff --git a/spanner/spec/spanner_isolation_level_spec.rb b/spanner/spec/spanner_isolation_level_spec.rb new file mode 100644 index 000000000..726c4f5e6 --- /dev/null +++ b/spanner/spec/spanner_isolation_level_spec.rb @@ -0,0 +1,51 @@ +# Copyright 2026 Google, Inc +# +# 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. + +require_relative "../spanner_isolation_level" +require "rspec" +require "google/cloud/spanner" + +describe "Spanner Isolation Level Options" do + it "runs isolation level snippet successfully" do + if ENV["GOOGLE_CLOUD_SPANNER_TEST_INSTANCE"].nil? || ENV["GOOGLE_CLOUD_SPANNER_PROJECT"].nil? + skip "GOOGLE_CLOUD_SPANNER_TEST_INSTANCE and/or GOOGLE_CLOUD_SPANNER_PROJECT not defined" + end + + @project_id = ENV["GOOGLE_CLOUD_SPANNER_PROJECT"] + @instance_id = ENV["GOOGLE_CLOUD_SPANNER_TEST_INSTANCE"] + @seed = SecureRandom.hex 8 + @database_id = "test_db_#{@seed}" + @spanner = Google::Cloud::Spanner.new project: @project_id + @instance = @spanner.instance @instance_id + + unless @instance.database @database_id + real_stdout = $stdout + $stdout = StringIO.new + create_database project_id: @project_id, + instance_id: @instance_id, + database_id: @database_id + $stdout = real_stdout + end + + client = @spanner.client @instance_id, @database_id + client.insert "Singers", [{ SingerId: 1, FirstName: "Test" }] + client.insert "Albums", [{ SingerId: 1, AlbumId: 1, AlbumTitle: "Old Title" }] + + expect { + spanner_isolation_level project_id: @project_id, instance_id: @instance_id, database_id: @database_id + }.to output(/AlbumTitle: Old Title\n1 records updated./).to_stdout + + @instance.database(@database_id).drop + end +end diff --git a/spanner/spec/spanner_read_lock_mode_spec.rb b/spanner/spec/spanner_read_lock_mode_spec.rb new file mode 100644 index 000000000..e5611c774 --- /dev/null +++ b/spanner/spec/spanner_read_lock_mode_spec.rb @@ -0,0 +1,51 @@ +# Copyright 2026 Google, Inc +# +# 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. + +require_relative "../spanner_read_lock_mode" +require "rspec" +require "google/cloud/spanner" + +describe "Spanner Read Lock Mode Options" do + it "runs read lock mode snippet successfully" do + if ENV["GOOGLE_CLOUD_SPANNER_TEST_INSTANCE"].nil? || ENV["GOOGLE_CLOUD_SPANNER_PROJECT"].nil? + skip "GOOGLE_CLOUD_SPANNER_TEST_INSTANCE and/or GOOGLE_CLOUD_SPANNER_PROJECT not defined" + end + + @project_id = ENV["GOOGLE_CLOUD_SPANNER_PROJECT"] + @instance_id = ENV["GOOGLE_CLOUD_SPANNER_TEST_INSTANCE"] + @seed = SecureRandom.hex 8 + @database_id = "test_db_#{@seed}" + @spanner = Google::Cloud::Spanner.new project: @project_id + @instance = @spanner.instance @instance_id + + unless @instance.database @database_id + real_stdout = $stdout + $stdout = StringIO.new + create_database project_id: @project_id, + instance_id: @instance_id, + database_id: @database_id + $stdout = real_stdout + end + + client = @spanner.client @instance_id, @database_id + client.insert "Singers", [{ SingerId: 2, FirstName: "Test" }] + client.insert "Albums", [{ SingerId: 2, AlbumId: 1, AlbumTitle: "Old Title" }] + + expect { + spanner_read_lock_mode project_id: @project_id, instance_id: @instance_id, database_id: @database_id + }.to output(/AlbumTitle: Old Title\n1 records updated./).to_stdout + + @instance.database(@database_id).drop + end +end