Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions spanner/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
source "https://rubygems.org"

gem "google-cloud-spanner"
gem "mutex_m"

group :test do
gem "google-apis-iam_v1"
Expand Down
118 changes: 67 additions & 51 deletions spanner/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
base64 (0.2.0)
concurrent-ruby (1.2.3)
addressable (2.8.9)
public_suffix (>= 2.0.2, < 8.0)
base64 (0.3.0)
bigdecimal (3.3.1)
concurrent-ruby (1.3.6)
declarative (0.0.20)
diff-lcs (1.5.1)
faraday (2.9.0)
faraday-net_http (>= 2.0, < 3.2)
faraday-net_http (3.1.0)
net-http
faraday-retry (2.2.0)
faraday (2.14.1)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.2)
net-http (~> 0.5)
faraday-retry (2.4.0)
faraday (~> 2.0)
gapic-common (0.21.1)
gapic-common (1.3.0)
faraday (>= 1.9, < 3.a)
faraday-retry (>= 1.0, < 3.a)
google-protobuf (~> 3.18)
googleapis-common-protos (>= 1.4.0, < 2.a)
googleapis-common-protos-types (>= 1.11.0, < 2.a)
googleauth (~> 1.9)
grpc (~> 1.59)
google-cloud-env (~> 2.2)
google-logging-utils (~> 0.1)
google-protobuf (~> 4.26)
googleapis-common-protos (~> 1.6)
googleapis-common-protos-types (~> 1.15)
googleauth (~> 1.12)
grpc (~> 1.66)
google-apis-core (0.14.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 1.9)
Expand All @@ -31,59 +36,69 @@ GEM
rexml
google-apis-iam_v1 (0.54.0)
google-apis-core (>= 0.14.0, < 2.a)
google-cloud-core (1.6.1)
google-cloud-core (1.8.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (2.1.1)
google-cloud-env (2.3.1)
base64 (~> 0.2)
faraday (>= 1.0, < 3.a)
google-cloud-errors (1.3.1)
google-cloud-spanner (2.21.0)
google-cloud-errors (1.5.0)
google-cloud-spanner (2.35.0)
bigdecimal (~> 3.0)
concurrent-ruby (~> 1.0)
google-cloud-core (~> 1.5)
google-cloud-spanner-admin-database-v1 (~> 0.1)
google-cloud-spanner-admin-instance-v1 (~> 0.1)
google-cloud-spanner-v1 (~> 0.2)
google-cloud-spanner-admin-database-v1 (0.17.1)
gapic-common (>= 0.21.1, < 2.a)
google-cloud-core (~> 1.7)
google-cloud-spanner-admin-database-v1 (~> 1.4)
google-cloud-spanner-admin-instance-v1 (~> 1.6)
google-cloud-spanner-v1 (~> 1.6)
google-cloud-spanner-admin-database-v1 (1.11.1)
gapic-common (~> 1.2)
google-cloud-errors (~> 1.0)
grpc-google-iam-v1 (~> 1.1)
google-cloud-spanner-admin-instance-v1 (0.13.2)
gapic-common (>= 0.21.1, < 2.a)
grpc-google-iam-v1 (~> 1.11)
google-cloud-spanner-admin-instance-v1 (1.6.0)
gapic-common (>= 0.25.0, < 2.a)
google-cloud-errors (~> 1.0)
grpc-google-iam-v1 (~> 1.1)
google-cloud-spanner-v1 (0.24.0)
gapic-common (>= 0.21.1, < 2.a)
google-cloud-spanner-v1 (1.15.0)
gapic-common (~> 1.2)
google-cloud-errors (~> 1.0)
google-protobuf (3.25.3)
googleapis-common-protos (1.5.0)
google-protobuf (~> 3.18)
google-logging-utils (0.2.0)
google-protobuf (4.34.0)
bigdecimal
rake (~> 13.3)
googleapis-common-protos (1.7.0)
google-protobuf (>= 3.18, < 5.a)
googleapis-common-protos-types (~> 1.7)
grpc (~> 1.41)
googleapis-common-protos-types (1.13.0)
google-protobuf (~> 3.18)
googleauth (1.11.0)
googleapis-common-protos-types (1.22.0)
google-protobuf (~> 4.26)
googleauth (1.16.2)
faraday (>= 1.0, < 3.a)
google-cloud-env (~> 2.1)
jwt (>= 1.4, < 3.0)
google-cloud-env (~> 2.2)
google-logging-utils (~> 0.1)
jwt (>= 1.4, < 4.0)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
grpc (1.62.0)
google-protobuf (~> 3.25)
grpc (1.78.1)
google-protobuf (>= 3.25, < 5.0)
googleapis-common-protos-types (~> 1.0)
grpc-google-iam-v1 (1.7.0)
google-protobuf (~> 3.18)
googleapis-common-protos (~> 1.4)
grpc-google-iam-v1 (1.11.0)
google-protobuf (>= 3.18, < 5.a)
googleapis-common-protos (~> 1.7.0)
grpc (~> 1.41)
httpclient (2.8.3)
jwt (2.8.0)
json (2.19.1)
jwt (3.1.2)
base64
logger (1.7.0)
mini_mime (1.1.5)
multi_json (1.15.0)
net-http (0.4.1)
uri
multi_json (1.19.1)
mutex_m (0.3.0)
net-http (0.9.1)
uri (>= 0.11.1)
os (1.1.4)
public_suffix (5.0.4)
public_suffix (7.0.5)
rake (13.3.1)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
Expand All @@ -105,21 +120,22 @@ GEM
rspec-support (3.13.1)
rspec_junit_formatter (0.6.0)
rspec-core (>= 2, < 4, != 2.12.0)
signet (0.19.0)
signet (0.21.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
jwt (>= 1.5, < 4.0)
multi_json (~> 1.10)
trailblazer-option (0.1.2)
uber (0.1.0)
uri (0.13.0)
uri (1.1.1)

PLATFORMS
ruby

DEPENDENCIES
google-apis-iam_v1
google-cloud-spanner
mutex_m
retriable
rspec
rspec_junit_formatter
Expand Down
58 changes: 58 additions & 0 deletions spanner/spanner_batch_write.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# 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.

# [START spanner_batch_write_at_least_once]
require "google/cloud/spanner"

##
# This is a snippet for showcasing how to apply a batch of mutations groups.
# All mutations in a group are applied atomically.
#
# @param project_id [String] The ID of the Google Cloud project.
# @param instance_id [String] The ID of the spanner instance.
# @param database_id [String] The ID of the database.
#
def spanner_batch_write project_id:, instance_id:, database_id:
spanner = Google::Cloud::Spanner.new project: project_id
client = spanner.client instance_id, database_id

results = client.batch_write do |b|
# First mutation group
b.mutation_group do |mg|
mg.upsert "Singers", [{ SingerId: 16, FirstName: "Scarlet", LastName: "Terry" }]
end

# Second mutation group
b.mutation_group do |mg|
mg.upsert "Singers", [
{ SingerId: 17, FirstName: "Marc" },
{ SingerId: 18, FirstName: "Catalina", LastName: "Smith" }
]
mg.upsert "Albums", [
{ SingerId: 17, AlbumId: 1, AlbumTitle: "Total Junk" },
{ SingerId: 18, AlbumId: 2, AlbumTitle: "Go, Go, Go" }
]
end
end

results.each do |response|
if response.ok?
puts "Mutation group indexes applied: #{response.indexes}"
else
puts "Mutation group failed to apply: #{response.indexes}"
puts "Error: #{response.status.message}"
end
end
end
# [END spanner_batch_write_at_least_once]
39 changes: 39 additions & 0 deletions spanner/spec/spanner_batch_write_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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.

require "spec_helper"
require_relative "../spanner_batch_write"

describe "spanner_batch_write" do
before :all do
create_singers_albums_database
end

after :all do
cleanup_database_resources
end

it "applies mutation groups" do
expect {
spanner_batch_write project_id: @project_id,
instance_id: @instance_id,
database_id: @database_id
}.to output(/Mutation group indexes applied: (\[0, 1\]|\[0\].*\[1\])/m).to_stdout

# Verify that the records were inserted
client = @spanner.client @instance_id, @database_id
results = client.execute "SELECT COUNT(*) FROM Singers"
expect(results.rows.first[0]).to eq 3 # 16, 17, 18
end
end