Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6749936
docs: adding type annotations and fixing documentation
viacheslav-rostovtsev Oct 28, 2025
7f9466f
docs: update `validate_single_use_args!` in Client
viacheslav-rostovtsev Oct 28, 2025
569c377
chore: delete unused methods from `transaction`
viacheslav-rostovtsev Oct 28, 2025
a6fd67a
feat: add SessionCreationOptions, (Multiplexed) SessionCache, keeping…
viacheslav-rostovtsev Oct 28, 2025
0a0e571
chore: move session creation from Client into Pool and make Pool depe…
viacheslav-rostovtsev Oct 28, 2025
4c472b8
feat: Use SessionCache in both Client and BatchClient, deprecate pool…
viacheslav-rostovtsev Oct 28, 2025
7493cf0
chore: Remove `reload!` from sessions; fix: make keepalive multiplexe…
viacheslav-rostovtsev Oct 28, 2025
2723314
chore: slight refactor and docs in Snapshot; fix: client uses `direct…
viacheslav-rostovtsev Oct 28, 2025
56895f0
fix: do not explicitly create transactions during retry
viacheslav-rostovtsev Oct 28, 2025
41526a0
feat: precommit token to Transaction, using in Client, piping through…
viacheslav-rostovtsev Oct 28, 2025
3d016a8
feat: add precommit token notification when iterating through Results
viacheslav-rostovtsev Oct 28, 2025
f4060dc
feat: mutation key in transaction, usage in client, piping through se…
viacheslav-rostovtsev Oct 28, 2025
c061cc5
tests: deleted tests for deleted methods
viacheslav-rostovtsev Oct 28, 2025
7af16db
tests: moved test
viacheslav-rostovtsev Oct 28, 2025
bb415fb
tests: new test for precommit tokens
viacheslav-rostovtsev Oct 28, 2025
8b4c036
tests: messy rest of tests
viacheslav-rostovtsev Oct 28, 2025
e3120e0
chore: let's just have it here
viacheslav-rostovtsev Oct 31, 2025
6763886
feat: add previous_transaction_id to transaction, service, and session
viacheslav-rostovtsev Oct 31, 2025
b6575f3
feat: refactor client to always run the explicit transaction in Clien…
viacheslav-rostovtsev Oct 31, 2025
bb49f6f
test: always run explicit transaction
viacheslav-rostovtsev Oct 31, 2025
4d5a4f7
test: previous_transaction_id
viacheslav-rostovtsev Oct 31, 2025
fd3f4fd
feat: retry commit protocol, add in-place retry to Session#commit and…
viacheslav-rostovtsev Nov 4, 2025
a2d6399
fixup: pry position, yardocs
viacheslav-rostovtsev Nov 4, 2025
7c885c9
fix: broken acceptance tests
viacheslav-rostovtsev Nov 6, 2025
e3bc867
fixup: review comments
viacheslav-rostovtsev Nov 17, 2025
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 google-cloud-spanner/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ gem "minitest", "~> 5.25"
gem "minitest-autotest", "~> 1.0"
gem "minitest-focus", "~> 1.4"
gem "minitest-rg", "~> 5.3"
gem "pry", group: :development, require: false
gem "rake"
gem "redcarpet", "~> 3.0"
gem "simplecov", "~> 0.22"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@
/3:(No statements in batch DML request|Request must contain at least one DML statement)/
)
end

# Emulator does not return timestamp for multiplex empty transaction commits for some reason
_(timestamp).must_be_kind_of Time
end

Expand Down Expand Up @@ -149,9 +151,16 @@
end

it "raises BatchUpdateError when the first statement in Batch DML is a syntax error for #{dialect}" do
# ** When using the emulator with multiplexed sessions **
# the BatchUpdate transaction in this test will not get cleaned up and that will cause the
# "The emulator only supports one transaction at a time." failure.
skip if emulator_enabled?

prior_results = db[dialect].execute_sql "SELECT * FROM accounts"
_(prior_results.rows.count).must_equal 3
db[dialect].transaction do |tx|
# @type [::Google::Cloud::Spanner::Client]
dbd = db[dialect]
dbd.transaction do |tx|
begin
_(tx.no_existing_transaction?).must_equal true
tx.batch_update do |b|
Expand All @@ -160,10 +169,15 @@
rescue Google::Cloud::Spanner::BatchUpdateError => e
_(e.cause).must_be_kind_of Google::Cloud::InvalidArgumentError
_(e.cause.message).must_equal "Statement 0: 'UPDDDD accounts' is not valid DML."
rescue ::Google::Cloud::InternalError => e
# [TODO virost@ 2025-11] This is accomodating a temporary backend regression,
# after 2026-01 this rescue clause should be removed.
_(e.cause.code).must_equal 13
_(e.cause.details).must_equal "Internal error encountered."
end
_(tx.no_existing_transaction?).must_equal true
end
prior_results = db[dialect].execute_sql "SELECT * FROM accounts"
prior_results = dbd.execute_sql "SELECT * FROM accounts"
_(prior_results.rows.count).must_equal 3
end

Expand Down Expand Up @@ -198,6 +212,11 @@

describe "request options for #{dialect}" do
it "execute batch update with priority options for #{dialect}" do
# ** When using the emulator with multiplexed sessions **
# the BatchUpdate transaction in this test will not get cleaned up and that will cause the
# "The emulator only supports one transaction at a time." failure.
skip if emulator_enabled?

db[dialect].transaction do |tx|
row_counts = tx.batch_update request_options: { priority: :PRIORITY_HIGH } do |b|
b.batch_update insert_dml[dialect], params: insert_params[dialect]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
let(:db) { spanner_client }

it "closes all sessions and creates new sessions" do
_(db.reset).must_equal true
_(db.reset!).must_equal true
_(db.execute_query("SELECT 1").rows.first.values).must_equal [1]
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
skip if emulator_enabled?

error = assert_raises Google::Cloud::PermissionDeniedError do
db.client $spanner_instance_id, $spanner_database_id, database_role: "unknown"
db_client = db.client $spanner_instance_id, $spanner_database_id, database_role: "unknown"
(db_client.instance_variable_get :@pool).with_session { |s| } # rubocop:disable Lint/EmptyBlock
end

assert_includes error.message, "Role not found: unknown"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ class Job
##
# @private Creates a new Backup::Job instance.
def initialize
# @type [::Gapic::Operation, nil]
@grpc = nil
# @type [::Google::Cloud::Spanner::Service, nil]
@service = nil
end

Expand Down Expand Up @@ -268,7 +270,7 @@ def cancel_time
end

# Create a new Backup::Job from a `Gapic::Operation` object.
# @param grpc [::Gapic::Operation`] The wrapped `Gapic::Operation` object.
# @param grpc [::Gapic::Operation`] Underlying `Gapic::Operation` object.
# @param service [::Google::Cloud::Spanner::Service] A `Spanner::Service` reference.
# @private
# @return [::Google::Cloud::Spanner::Backup::Job]
Expand Down
25 changes: 17 additions & 8 deletions google-cloud-spanner/lib/google/cloud/spanner/batch_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
require "google/cloud/spanner/errors"
require "google/cloud/spanner/project"
require "google/cloud/spanner/session"
require "google/cloud/spanner/session_cache"
require "google/cloud/spanner/session_creation_options"
require "google/cloud/spanner/batch_snapshot"

module Google
Expand Down Expand Up @@ -83,6 +85,16 @@ def initialize project, instance_id, database_id, session_labels: nil,
@session_labels = session_labels
@query_options = query_options
@directed_read_options = directed_read_options

session_creation_options = SessionCreationOptions.new(
database_path: Admin::Database::V1::DatabaseAdmin::Paths.database_path(
project: @project.service.project, instance: instance_id, database: database_id
),
session_labels: @session_labels,
query_options: @query_options
)

@session_cache = SessionCache.new @project.service, session_creation_options
end

# The unique identifier for the project.
Expand Down Expand Up @@ -110,13 +122,15 @@ def project
end

# The Spanner instance connected to.
# @return [Instance]
# @deprecated Use {Google::Cloud::Spanner::Admin::Instance#instance_admin} instead.
# @return [::Google::Cloud::Spanner::Instance]
def instance
@project.instance instance_id
end

# The Spanner database connected to.
# @return [Database]
# @deprecated Use {Google::Cloud::Spanner::Admin::Database#database_admin} instead.
# @return [::Google::Cloud::Spanner::Database]
def database
@project.database instance_id, database_id
end
Expand Down Expand Up @@ -428,12 +442,7 @@ def ensure_service!
# @return [::Google::Cloud::Spanner::Session]
def session
ensure_service!
grpc = @project.service.create_session \
V1::Spanner::Paths.database_path(
project: project_id, instance: instance_id, database: database_id
),
labels: @session_labels
Session.from_grpc grpc, @project.service, query_options: @query_options
@session_cache.session
end

##
Expand Down
22 changes: 18 additions & 4 deletions google-cloud-spanner/lib/google/cloud/spanner/batch_snapshot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,15 @@ class BatchSnapshot
# @private Directed Read Options
attr_reader :directed_read_options

##
# @private Creates a BatchSnapshot object.
# Creates a new `Spanner::BatchSnapshot` instance.
# @param grpc [::Google::Cloud::Spanner::V1::Transaction]
# Underlying `V1::Transaction` object.
# @param session [::Google::Cloud::Spanner::Session] A `Spanner::Session` reference.
# @param directed_read_options [::Hash, nil] Optional. Client options used to set
# the `directed_read_options` for all ReadRequests and ExecuteSqlRequests.
# Converts to `V1::DirectedReadOptions`. Example option: `:exclude_replicas`.
# @private
# @return [::Google::Cloud::Spanner::BatchSnapshot]
def initialize grpc, session, directed_read_options: nil
@grpc = grpc
@session = session
Expand Down Expand Up @@ -852,9 +859,16 @@ def self.load data, service: nil, query_options: nil
from_grpc transaction_grpc, Session.from_grpc(session_grpc, service, query_options: query_options)
end

##
# @private Creates a new BatchSnapshot instance from a
# Creates a new BatchSnapshot instance from a
# `Google::Cloud::Spanner::V1::Transaction`.
# @param grpc [::Google::Cloud::Spanner::V1::Transaction]
# Underlying `V1::Transaction` object.
# @param session [::Google::Cloud::Spanner::Session] A `Spanner::Session` reference.
# @param directed_read_options [::Hash, nil] Optional. Client options used to set
# the `directed_read_options` for all ReadRequests and ExecuteSqlRequests.
# Converts to `V1::DirectedReadOptions`. Example option: `:exclude_replicas`.
# @private
# @return [::Google::Cloud::Spanner::BatchSnapshot]
def self.from_grpc grpc, session, directed_read_options: nil
new grpc, session, directed_read_options: directed_read_options
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ module Spanner
##
# @private Helper class to process BatchDML response
class BatchUpdateResults
## Object of type
# Google::Cloud::Spanner::V1::ExecuteBatchDmlResponse
# The `V1::ExecuteBatchDmlResponse` object to process
# @private
# @return [::Google::Cloud::Spanner::V1::ExecuteBatchDmlResponse]
attr_reader :grpc

# Initializes the `BatchUpdateResults` helper
# @private
# @param grpc [::Google::Cloud::Spanner::V1::ExecuteBatchDmlResponse]
# The response from `ExecuteBatchDml` rpc to process in this helper.
def initialize grpc
@grpc = grpc
end
Expand Down
Loading