Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spanner) : add support for client resource base routing #4548

Closed
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 36 additions & 0 deletions google-cloud-spanner/acceptance/spanner/client_test.rb
@@ -0,0 +1,36 @@
# Copyright 2020 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
#
# https://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 "spanner_helper"
require "concurrent"

describe "Spanner Client", :spanner do
let(:spanner) { $spanner }
let(:instance_id) { $spanner_instance_id }
let(:database_id) { $spanner_database_id }

it "create client connection without resource based routing" do
client = spanner.client instance_id, database_id
client.service.host.must_equal Google::Cloud::Spanner::V1::SpannerClient::SERVICE_ADDRESS
end

it "create client connection with resource based routing" do
client = spanner.client instance_id, database_id, enable_resource_based_routing: true
client.resource_based_routing_enabled?.must_equal true
instance = spanner.instance instance_id, fields: ["endpoint_uris"]
# Set to default if no endpoint uri present.
host = instance.endpoint_uris.first || Google::Cloud::Spanner::V1::SpannerClient::SERVICE_ADDRESS
client.service.host.must_equal host
end
end
24 changes: 24 additions & 0 deletions google-cloud-spanner/acceptance/spanner/instance_test.rb
Expand Up @@ -15,6 +15,8 @@
require "spanner_helper"

describe "Spanner Instances", :spanner do
let(:instance_id) { $spanner_instance_id }

it "lists and gets instances" do
all_instances = spanner.instances.all.to_a
all_instances.wont_be :empty?
Expand All @@ -26,6 +28,28 @@
first_instance.must_be_kind_of Google::Cloud::Spanner::Instance
end

describe "get instance" do
it "get all instance fields" do
instance = spanner.instance instance_id

instance.instance_id.must_equal instance_id
instance.path.wont_be_empty
instance.display_name.wont_be_empty
instance.nodes.must_be :>, 0
instance.state.must_equal :READY
end

it "get speicified instance fields" do
jiren marked this conversation as resolved.
Show resolved Hide resolved
instance = spanner.instance instance_id, fields: ["name"]

instance.instance_id.must_equal instance_id
instance.path.wont_be_empty
instance.display_name.must_be_empty
instance.nodes.must_equal 0
instance.state.must_equal :STATE_UNSPECIFIED
end
end

describe "IAM Policies and Permissions" do
let(:service_account) { spanner.service.credentials.client.issuer }

Expand Down
4 changes: 2 additions & 2 deletions google-cloud-spanner/acceptance/spanner_helper.rb
Expand Up @@ -254,9 +254,9 @@ def default_item_rows
fixture = Object.new
fixture.extend Acceptance::SpannerTest::Fixtures

instance = $spanner.instance "google-cloud-ruby-tests"
instance = $spanner.instance $spanner_instance_id
instance ||= begin
inst_job = $spanner.create_instance "google-cloud-ruby-tests", name: "google-cloud-ruby-tests", config: "regional-us-central1", nodes: 1
inst_job = $spanner.create_instance $spanner_instance_id, name: $spanner_instance_id, config: "regional-us-central1", nodes: 1
inst_job.wait_until_done!
fail GRPC::BadStatus.new(inst_job.error.code, inst_job.error.message) if inst_job.error?
inst_job.instance
Expand Down
30 changes: 25 additions & 5 deletions google-cloud-spanner/lib/google/cloud/spanner/batch_client.rb
Expand Up @@ -17,6 +17,7 @@
require "google/cloud/spanner/project"
require "google/cloud/spanner/session"
require "google/cloud/spanner/batch_snapshot"
require "google/cloud/spanner/resource_based_routing"

module Google
module Cloud
Expand Down Expand Up @@ -63,13 +64,32 @@ module Spanner
# new_partition
#
class BatchClient
# @!parse extend ResourceBasedRouting
include ResourceBasedRouting

## @private Service wrapper for batch data client api.
# @return [ClientServiceProxy]
attr_reader :service

##
# @private Creates a new Spanner BatchClient instance.
def initialize project, instance_id, database_id, session_labels: nil
def initialize \
project,
instance_id,
database_id,
session_labels: nil,
enable_resource_based_routing: false
@project = project
@instance_id = instance_id
@database_id = database_id
@session_labels = session_labels
@enable_resource_based_routing = enable_resource_based_routing

if resource_based_routing_enabled?
@service = resource_based_routing_service
end

@service ||= @project.service
end

# The unique identifier for the project.
Expand Down Expand Up @@ -184,7 +204,7 @@ def batch_snapshot strong: nil, timestamp: nil, read_timestamp: nil,

ensure_service!
snp_session = session
snp_grpc = @project.service.create_snapshot \
snp_grpc = @service.create_snapshot \
snp_session.path, strong: strong,
timestamp: (timestamp || read_timestamp),
staleness: (staleness || exact_staleness)
Expand Down Expand Up @@ -231,7 +251,7 @@ def batch_snapshot strong: nil, timestamp: nil, read_timestamp: nil,
def load_batch_snapshot serialized_snapshot
ensure_service!

BatchSnapshot.load serialized_snapshot, service: @project.service
BatchSnapshot.load serialized_snapshot, service: @service
end

##
Expand Down Expand Up @@ -404,12 +424,12 @@ def ensure_service!
# New session for each use.
def session
ensure_service!
grpc = @project.service.create_session \
grpc = @service.create_session \
Admin::Database::V1::DatabaseAdminClient.database_path(
project_id, instance_id, database_id
),
labels: @session_labels
Session.from_grpc grpc, @project.service
Session.from_grpc grpc, @service
end

##
Expand Down
63 changes: 48 additions & 15 deletions google-cloud-spanner/lib/google/cloud/spanner/client.rb
Expand Up @@ -23,6 +23,7 @@
require "google/cloud/spanner/range"
require "google/cloud/spanner/column_value"
require "google/cloud/spanner/convert"
require "google/cloud/spanner/resource_based_routing"

module Google
module Cloud
Expand All @@ -49,22 +50,57 @@ module Spanner
# end
# end
#
# @example Enable resource based routing.
#
# require "google/cloud"
#
# spanner = Google::Cloud::Spanner.new
#
# db = spanner.client(
# "my-instance",
# "my-database",
# enable_resource_based_routing: true
# )
#
# db.transaction do |tx|
# results = tx.execute_query "SELECT * FROM users"
#
# results.rows.each do |row|
# puts "User #{row[:id]} is #{row[:name]}"
# end
# end
#
class Client
# @!parse extend ResourceBasedRouting
include ResourceBasedRouting

##
# @private The Service object.
# @return [Spanner::Service]
attr_reader :service

##
# @private Creates a new Spanner Client instance.
def initialize project, instance_id, database_id, session_labels: nil,
pool_opts: {}
pool_opts: {}, enable_resource_based_routing: false
@project = project
@instance_id = instance_id
@database_id = database_id
@session_labels = session_labels
@enable_resource_based_routing = enable_resource_based_routing

if resource_based_routing_enabled?
@service = resource_based_routing_service
end

@service ||= @project.service
@pool = Pool.new self, pool_opts
end

# The unique identifier for the project.
# @return [String]
def project_id
@project.service.project
@service.project
end

# The unique identifier for the instance.
Expand Down Expand Up @@ -920,8 +956,7 @@ def commit &block
end
end

# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize,Metrics/MethodLength

##
# Creates a transaction for reads and writes that execute atomically at
Expand Down Expand Up @@ -994,7 +1029,7 @@ def transaction deadline: 120
begin
Thread.current[:transaction_id] = tx.transaction_id
yield tx
commit_resp = @project.service.commit \
commit_resp = @service.commit \
tx.session.path, tx.mutations, transaction_id: tx.transaction_id
return Convert.timestamp_to_time commit_resp.commit_timestamp
rescue GRPC::Aborted, Google::Cloud::AbortedError => err
Expand Down Expand Up @@ -1022,9 +1057,7 @@ def transaction deadline: 120
end
end
end

# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize,Metrics/MethodLength

##
# Creates a snapshot read-only transaction for reads that execute
Expand Down Expand Up @@ -1093,7 +1126,7 @@ def snapshot strong: nil, timestamp: nil, read_timestamp: nil,

@pool.with_session do |session|
begin
snp_grpc = @project.service.create_snapshot \
snp_grpc = @service.create_snapshot \
session.path, strong: strong,
timestamp: (timestamp || read_timestamp),
staleness: (staleness || exact_staleness)
Expand Down Expand Up @@ -1286,12 +1319,12 @@ def reset
# Creates a new session object every time.
def create_new_session
ensure_service!
grpc = @project.service.create_session \
grpc = @service.create_session \
Admin::Database::V1::DatabaseAdminClient.database_path(
project_id, instance_id, database_id
),
labels: @session_labels
Session.from_grpc grpc, @project.service
Session.from_grpc grpc, @service
end

##
Expand All @@ -1314,13 +1347,13 @@ def batch_create_new_sessions total
#
def batch_create_sessions session_count
ensure_service!
resp = @project.service.batch_create_sessions \
resp = @service.batch_create_sessions \
Admin::Database::V1::DatabaseAdminClient.database_path(
project_id, instance_id, database_id
),
session_count,
labels: @session_labels
resp.session.map { |grpc| Session.from_grpc grpc, @project.service }
resp.session.map { |grpc| Session.from_grpc grpc, @service }
end

# @private
Expand All @@ -1340,7 +1373,7 @@ def inspect
# @private Raise an error unless an active connection to the service is
# available.
def ensure_service!
raise "Must have active connection to service" unless @project.service
raise "Must have active connection to service" unless @service
end

##
Expand Down Expand Up @@ -1385,7 +1418,7 @@ def single_use_transaction opts
end

def pdml_transaction session
pdml_tx_grpc = @project.service.create_pdml session.path
pdml_tx_grpc = @service.create_pdml session.path
Google::Spanner::V1::TransactionSelector.new id: pdml_tx_grpc.id
end

Expand Down
14 changes: 14 additions & 0 deletions google-cloud-spanner/lib/google/cloud/spanner/instance.rb
Expand Up @@ -188,6 +188,20 @@ def labels= labels
)
end

##
# The endpoint URIs based on the instance config.
# For example, instances located in a specific cloud region
# (or multi region) such as nam3, would have a nam3 specific
# endpoint URI. These endpoints are intended to optimize the network
# routing between the client and the instance's serving resources.
# If multiple endpoints are present, client may establish connections
# using any of the given URLs.
#
# @return [Array<String>] The list of URIs.
def endpoint_uris
@grpc.endpoint_uris
end

def save
job_grpc = service.update_instance @grpc
Instance::Job.from_grpc job_grpc, service
Expand Down