google-cloud-spanner 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/google-cloud-spanner.rb +106 -0
- data/lib/google/cloud/spanner.rb +382 -0
- data/lib/google/cloud/spanner/admin/database/v1.rb +17 -0
- data/lib/google/cloud/spanner/admin/database/v1/database_admin_client.rb +703 -0
- data/lib/google/cloud/spanner/admin/database/v1/database_admin_client_config.json +73 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/iam/v1/policy.rb +139 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/protobuf/any.rb +114 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/rpc/status.rb +83 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/spanner/admin/database/v1/spanner_database_admin.rb +188 -0
- data/lib/google/cloud/spanner/admin/instance/v1.rb +17 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/iam/v1/policy.rb +139 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/any.rb +114 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/field_mask.rb +223 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/rpc/status.rb +83 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/spanner/admin/instance/v1/spanner_instance_admin.rb +268 -0
- data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client.rb +868 -0
- data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client_config.json +78 -0
- data/lib/google/cloud/spanner/client.rb +1034 -0
- data/lib/google/cloud/spanner/commit.rb +351 -0
- data/lib/google/cloud/spanner/convert.rb +311 -0
- data/lib/google/cloud/spanner/credentials.rb +32 -0
- data/lib/google/cloud/spanner/data.rb +199 -0
- data/lib/google/cloud/spanner/database.rb +377 -0
- data/lib/google/cloud/spanner/database/job.rb +179 -0
- data/lib/google/cloud/spanner/database/list.rb +171 -0
- data/lib/google/cloud/spanner/errors.rb +73 -0
- data/lib/google/cloud/spanner/fields.rb +252 -0
- data/lib/google/cloud/spanner/instance.rb +472 -0
- data/lib/google/cloud/spanner/instance/config.rb +99 -0
- data/lib/google/cloud/spanner/instance/config/list.rb +171 -0
- data/lib/google/cloud/spanner/instance/job.rb +197 -0
- data/lib/google/cloud/spanner/instance/list.rb +167 -0
- data/lib/google/cloud/spanner/policy.rb +201 -0
- data/lib/google/cloud/spanner/pool.rb +279 -0
- data/lib/google/cloud/spanner/project.rb +480 -0
- data/lib/google/cloud/spanner/range.rb +99 -0
- data/lib/google/cloud/spanner/results.rb +280 -0
- data/lib/google/cloud/spanner/service.rb +458 -0
- data/lib/google/cloud/spanner/session.rb +565 -0
- data/lib/google/cloud/spanner/snapshot.rb +260 -0
- data/lib/google/cloud/spanner/transaction.rb +533 -0
- data/lib/google/cloud/spanner/v1.rb +17 -0
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/duration.rb +77 -0
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/struct.rb +73 -0
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/timestamp.rb +81 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/keys.rb +148 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/mutation.rb +80 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/query_plan.rb +120 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/result_set.rb +175 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/spanner.rb +206 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/transaction.rb +351 -0
- data/lib/google/cloud/spanner/v1/spanner_client.rb +850 -0
- data/lib/google/cloud/spanner/v1/spanner_client_config.json +78 -0
- data/lib/google/cloud/spanner/version.rb +22 -0
- data/lib/google/spanner/admin/database/v1/spanner_database_admin_pb.rb +85 -0
- data/lib/google/spanner/admin/database/v1/spanner_database_admin_services_pb.rb +95 -0
- data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_pb.rb +106 -0
- data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_services_pb.rb +180 -0
- data/lib/google/spanner/v1/keys_pb.rb +33 -0
- data/lib/google/spanner/v1/mutation_pb.rb +38 -0
- data/lib/google/spanner/v1/query_plan_pb.rb +47 -0
- data/lib/google/spanner/v1/result_set_pb.rb +43 -0
- data/lib/google/spanner/v1/spanner_pb.rb +90 -0
- data/lib/google/spanner/v1/spanner_services_pb.rb +131 -0
- data/lib/google/spanner/v1/transaction_pb.rb +51 -0
- data/lib/google/spanner/v1/type_pb.rb +43 -0
- metadata +309 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
require "delegate"
|
|
17
|
+
|
|
18
|
+
module Google
|
|
19
|
+
module Cloud
|
|
20
|
+
module Spanner
|
|
21
|
+
class Instance
|
|
22
|
+
##
|
|
23
|
+
# Instance::List is a special case Array with additional
|
|
24
|
+
# values.
|
|
25
|
+
class List < DelegateClass(::Array)
|
|
26
|
+
##
|
|
27
|
+
# If not empty, indicates that there are more records that match
|
|
28
|
+
# the request and this value should be passed to continue.
|
|
29
|
+
attr_accessor :token
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# @private Create a new Instance::List with an array of
|
|
33
|
+
# Instance instances.
|
|
34
|
+
def initialize arr = []
|
|
35
|
+
super arr
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# Whether there is a next page of instances.
|
|
40
|
+
#
|
|
41
|
+
# @return [Boolean]
|
|
42
|
+
#
|
|
43
|
+
# @example
|
|
44
|
+
# require "google/cloud/spanner"
|
|
45
|
+
#
|
|
46
|
+
# spanner = Google::Cloud::Spanner.new
|
|
47
|
+
#
|
|
48
|
+
# instances = spanner.instances
|
|
49
|
+
# if instances.next?
|
|
50
|
+
# next_instances = instances.next
|
|
51
|
+
# end
|
|
52
|
+
def next?
|
|
53
|
+
!token.nil?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Retrieve the next page of instances.
|
|
58
|
+
#
|
|
59
|
+
# @return [Instance::List] The list of instances.
|
|
60
|
+
#
|
|
61
|
+
# @example
|
|
62
|
+
# require "google/cloud/spanner"
|
|
63
|
+
#
|
|
64
|
+
# spanner = Google::Cloud::Spanner.new
|
|
65
|
+
#
|
|
66
|
+
# instances = spanner.instances
|
|
67
|
+
# if instances.next?
|
|
68
|
+
# next_instances = instances.next
|
|
69
|
+
# end
|
|
70
|
+
def next
|
|
71
|
+
return nil unless next?
|
|
72
|
+
ensure_service!
|
|
73
|
+
options = { token: token, max: @max }
|
|
74
|
+
grpc = @service.list_instances options
|
|
75
|
+
self.class.from_grpc grpc, @service, @max
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
##
|
|
79
|
+
# Retrieves remaining results by repeatedly invoking {#next} until
|
|
80
|
+
# {#next?} returns `false`. Calls the given block once for each
|
|
81
|
+
# result, which is passed as the argument to the block.
|
|
82
|
+
#
|
|
83
|
+
# An Enumerator is returned if no block is given.
|
|
84
|
+
#
|
|
85
|
+
# This method will make repeated API calls until all remaining results
|
|
86
|
+
# are retrieved. (Unlike `#each`, for example, which merely iterates
|
|
87
|
+
# over the results returned by a single API call.) Use with caution.
|
|
88
|
+
#
|
|
89
|
+
# @param [Integer] request_limit The upper limit of API requests to
|
|
90
|
+
# make to load all instances. Default is no limit.
|
|
91
|
+
# @yield [instance] The block for accessing each instance.
|
|
92
|
+
# @yieldparam [Instance] instance The instance object.
|
|
93
|
+
#
|
|
94
|
+
# @return [Enumerator]
|
|
95
|
+
#
|
|
96
|
+
# @example Iterating each instance by passing a block:
|
|
97
|
+
# require "google/cloud/spanner"
|
|
98
|
+
#
|
|
99
|
+
# spanner = Google::Cloud::Spanner.new
|
|
100
|
+
#
|
|
101
|
+
# spanner.instances.all do |instance|
|
|
102
|
+
# puts instance.instance_id
|
|
103
|
+
# end
|
|
104
|
+
#
|
|
105
|
+
# @example Using the enumerator by not passing a block:
|
|
106
|
+
# require "google/cloud/spanner"
|
|
107
|
+
#
|
|
108
|
+
# spanner = Google::Cloud::Spanner.new
|
|
109
|
+
#
|
|
110
|
+
# all_instance_ids = spanner.instances.all.map do |instance|
|
|
111
|
+
# instance.instance_id
|
|
112
|
+
# end
|
|
113
|
+
#
|
|
114
|
+
# @example Limit the number of API calls made:
|
|
115
|
+
# require "google/cloud/spanner"
|
|
116
|
+
#
|
|
117
|
+
# spanner = Google::Cloud::Spanner.new
|
|
118
|
+
#
|
|
119
|
+
# spanner.instances.all(request_limit: 10) do |instance|
|
|
120
|
+
# puts instance.instance_id
|
|
121
|
+
# end
|
|
122
|
+
#
|
|
123
|
+
def all request_limit: nil
|
|
124
|
+
request_limit = request_limit.to_i if request_limit
|
|
125
|
+
unless block_given?
|
|
126
|
+
return enum_for(:all, request_limit: request_limit)
|
|
127
|
+
end
|
|
128
|
+
results = self
|
|
129
|
+
loop do
|
|
130
|
+
results.each { |r| yield r }
|
|
131
|
+
if request_limit
|
|
132
|
+
request_limit -= 1
|
|
133
|
+
break if request_limit < 0
|
|
134
|
+
end
|
|
135
|
+
break unless results.next?
|
|
136
|
+
results = results.next
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
##
|
|
141
|
+
# @private New Instance::List from a
|
|
142
|
+
# Google::Spanner::Admin::Instance::V1::ListInstancesResponse
|
|
143
|
+
# object.
|
|
144
|
+
def self.from_grpc grpc, service, max = nil
|
|
145
|
+
instances = List.new(Array(grpc.instances).map do |instance|
|
|
146
|
+
Instance.from_grpc instance, service
|
|
147
|
+
end)
|
|
148
|
+
token = grpc.next_page_token
|
|
149
|
+
token = nil if token == ""
|
|
150
|
+
instances.instance_variable_set :@token, token
|
|
151
|
+
instances.instance_variable_set :@service, service
|
|
152
|
+
instances.instance_variable_set :@max, max
|
|
153
|
+
instances
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
protected
|
|
157
|
+
|
|
158
|
+
##
|
|
159
|
+
# Raise an error unless an active service is available.
|
|
160
|
+
def ensure_service!
|
|
161
|
+
fail "Must have active connection" unless @service
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
require "google/cloud/spanner/errors"
|
|
17
|
+
|
|
18
|
+
module Google
|
|
19
|
+
module Cloud
|
|
20
|
+
module Spanner
|
|
21
|
+
##
|
|
22
|
+
# # Policy
|
|
23
|
+
#
|
|
24
|
+
# Represents a Cloud IAM Policy for the Spanner service.
|
|
25
|
+
#
|
|
26
|
+
# A common pattern for updating a resource's metadata, such as its Policy,
|
|
27
|
+
# is to read the current data from the service, update the data locally,
|
|
28
|
+
# and then send the modified data for writing. This pattern may result in
|
|
29
|
+
# a conflict if two or more processes attempt the sequence simultaneously.
|
|
30
|
+
# IAM solves this problem with the {Google::Cloud::Spanner::Policy#etag}
|
|
31
|
+
# property, which is used to verify whether the policy has changed since
|
|
32
|
+
# the last request. When you make a request to with an `etag` value, Cloud
|
|
33
|
+
# IAM compares the `etag` value in the request with the existing `etag`
|
|
34
|
+
# value associated with the policy. It writes the policy only if the
|
|
35
|
+
# `etag` values match.
|
|
36
|
+
#
|
|
37
|
+
# When you update a policy, first read the policy (and its current `etag`)
|
|
38
|
+
# from the service, then modify the policy locally, and then write the
|
|
39
|
+
# modified policy to the service. See
|
|
40
|
+
# {Google::Cloud::Spanner::Instance#policy} and
|
|
41
|
+
# {Google::Cloud::Spanner::Instance#policy=} and
|
|
42
|
+
# {Google::Cloud::Spanner::Database#policy} and
|
|
43
|
+
# {Google::Cloud::Spanner::Database#policy=}.
|
|
44
|
+
#
|
|
45
|
+
# @see https://cloud.google.com/iam/docs/managing-policies Managing
|
|
46
|
+
# policies
|
|
47
|
+
# @see https://cloud.google.com/spanner/reference/rpc/google.iam.v1#google.iam.v1.Policy
|
|
48
|
+
# google.iam.v1.IAMPolicy
|
|
49
|
+
#
|
|
50
|
+
# @attr [String] etag Used to verify whether the policy has changed since
|
|
51
|
+
# the last request. The policy will be written only if the `etag` values
|
|
52
|
+
# match.
|
|
53
|
+
# @attr [Hash{String => Array<String>}] roles The bindings that associate
|
|
54
|
+
# roles with an array of members. See [Understanding
|
|
55
|
+
# Roles](https://cloud.google.com/iam/docs/understanding-roles) for a
|
|
56
|
+
# listing of primitive and curated roles.
|
|
57
|
+
# See [Binding](https://cloud.google.com/spanner/reference/rpc/google.iam.v1#google.iam.v1.Binding)
|
|
58
|
+
# for a listing of values and patterns for members.
|
|
59
|
+
#
|
|
60
|
+
# @example
|
|
61
|
+
# require "google/cloud/spanner"
|
|
62
|
+
#
|
|
63
|
+
# spanner = Google::Cloud::Spanner.new
|
|
64
|
+
# instance = spanner.instance "my-instance"
|
|
65
|
+
#
|
|
66
|
+
# policy = instance.policy do |p|
|
|
67
|
+
# p.remove "roles/owner", "user:owner@example.com"
|
|
68
|
+
# p.add "roles/owner", "user:newowner@example.com"
|
|
69
|
+
# p.roles["roles/viewer"] = ["allUsers"]
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
class Policy
|
|
73
|
+
attr_reader :etag, :roles
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# @private Creates a Policy object.
|
|
77
|
+
def initialize etag, roles
|
|
78
|
+
@etag = etag
|
|
79
|
+
@roles = roles
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
##
|
|
83
|
+
# Convenience method for adding a member to a binding on this policy.
|
|
84
|
+
# See [Understanding
|
|
85
|
+
# Roles](https://cloud.google.com/iam/docs/understanding-roles) for a
|
|
86
|
+
# listing of primitive and curated roles.
|
|
87
|
+
# See [Binding](https://cloud.google.com/spanner/reference/rpc/google.iam.v1#google.iam.v1.Binding)
|
|
88
|
+
# for a listing of values and patterns for members.
|
|
89
|
+
#
|
|
90
|
+
# @param [String] role_name A Cloud IAM role, such as
|
|
91
|
+
# `"roles/spanner.admin"`.
|
|
92
|
+
# @param [String] member A Cloud IAM identity, such as
|
|
93
|
+
# `"user:owner@example.com"`.
|
|
94
|
+
#
|
|
95
|
+
# @example
|
|
96
|
+
# require "google/cloud/spanner"
|
|
97
|
+
#
|
|
98
|
+
# spanner = Google::Cloud::Spanner.new
|
|
99
|
+
# instance = spanner.instance "my-instance"
|
|
100
|
+
#
|
|
101
|
+
# policy = instance.policy do |p|
|
|
102
|
+
# p.add "roles/owner", "user:newowner@example.com"
|
|
103
|
+
# end
|
|
104
|
+
#
|
|
105
|
+
def add role_name, member
|
|
106
|
+
role(role_name) << member
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
##
|
|
110
|
+
# Convenience method for removing a member from a binding on this
|
|
111
|
+
# policy. See [Understanding
|
|
112
|
+
# Roles](https://cloud.google.com/iam/docs/understanding-roles) for a
|
|
113
|
+
# listing of primitive and curated roles. See
|
|
114
|
+
# [Binding](https://cloud.google.com/spanner/reference/rpc/google.iam.v1#google.iam.v1.Binding)
|
|
115
|
+
# for a listing of values and patterns for members.
|
|
116
|
+
#
|
|
117
|
+
# @param [String] role_name A Cloud IAM role, such as
|
|
118
|
+
# `"roles/spanner.admin"`.
|
|
119
|
+
# @param [String] member A Cloud IAM identity, such as
|
|
120
|
+
# `"user:owner@example.com"`.
|
|
121
|
+
#
|
|
122
|
+
# @example
|
|
123
|
+
# require "google/cloud/spanner"
|
|
124
|
+
#
|
|
125
|
+
# spanner = Google::Cloud::Spanner.new
|
|
126
|
+
# instance = spanner.instance "my-instance"
|
|
127
|
+
#
|
|
128
|
+
# policy = instance.policy do |p|
|
|
129
|
+
# p.remove "roles/owner", "user:owner@example.com"
|
|
130
|
+
# end
|
|
131
|
+
#
|
|
132
|
+
def remove role_name, member
|
|
133
|
+
role(role_name).delete member
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
##
|
|
137
|
+
# Convenience method returning the array of members bound to a role in
|
|
138
|
+
# this policy, or an empty array if no value is present for the role in
|
|
139
|
+
# {#roles}. See [Understanding
|
|
140
|
+
# Roles](https://cloud.google.com/iam/docs/understanding-roles) for a
|
|
141
|
+
# listing of primitive and curated roles. See
|
|
142
|
+
# [Binding](https://cloud.google.com/spanner/reference/rpc/google.iam.v1#google.iam.v1.Binding)
|
|
143
|
+
# for a listing of values and patterns for members.
|
|
144
|
+
#
|
|
145
|
+
# @return [Array<String>] The members strings, or an empty array.
|
|
146
|
+
#
|
|
147
|
+
# @example
|
|
148
|
+
# require "google/cloud/spanner"
|
|
149
|
+
#
|
|
150
|
+
# spanner = Google::Cloud::Spanner.new
|
|
151
|
+
# instance = spanner.instance "my-instance"
|
|
152
|
+
#
|
|
153
|
+
# policy = instance.policy do |p|
|
|
154
|
+
# p.role("roles/viewer") << "user:viewer@example.com"
|
|
155
|
+
# end
|
|
156
|
+
#
|
|
157
|
+
def role role_name
|
|
158
|
+
roles[role_name] ||= []
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
##
|
|
162
|
+
# Returns a deep copy of the policy.
|
|
163
|
+
#
|
|
164
|
+
# @return [Policy]
|
|
165
|
+
#
|
|
166
|
+
def deep_dup
|
|
167
|
+
dup.tap do |p|
|
|
168
|
+
roles_dup = p.roles.each_with_object({}) do |(k, v), memo|
|
|
169
|
+
memo[k] = v.dup rescue value
|
|
170
|
+
end
|
|
171
|
+
p.instance_variable_set "@roles", roles_dup
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
##
|
|
176
|
+
# @private Convert the Policy to a Google::Iam::V1::Policy object.
|
|
177
|
+
def to_grpc
|
|
178
|
+
Google::Iam::V1::Policy.new(
|
|
179
|
+
etag: etag,
|
|
180
|
+
bindings: roles.keys.map do |role_name|
|
|
181
|
+
next if roles[role_name].empty?
|
|
182
|
+
Google::Iam::V1::Binding.new(
|
|
183
|
+
role: role_name,
|
|
184
|
+
members: roles[role_name]
|
|
185
|
+
)
|
|
186
|
+
end
|
|
187
|
+
)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
##
|
|
191
|
+
# @private New Policy from a Google::Iam::V1::Policy object.
|
|
192
|
+
def self.from_grpc grpc
|
|
193
|
+
roles = grpc.bindings.each_with_object({}) do |binding, memo|
|
|
194
|
+
memo[binding.role] = binding.members.to_a
|
|
195
|
+
end
|
|
196
|
+
new grpc.etag, roles
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Copyright 2017 Google Inc. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
require "thread"
|
|
17
|
+
require "concurrent"
|
|
18
|
+
require "google/cloud/spanner/errors"
|
|
19
|
+
require "google/cloud/spanner/session"
|
|
20
|
+
|
|
21
|
+
module Google
|
|
22
|
+
module Cloud
|
|
23
|
+
module Spanner
|
|
24
|
+
##
|
|
25
|
+
# @private
|
|
26
|
+
#
|
|
27
|
+
# # Pool
|
|
28
|
+
#
|
|
29
|
+
# Implements a pool for managing and reusing
|
|
30
|
+
# {Google::Cloud::Spanner::Session} instances.
|
|
31
|
+
#
|
|
32
|
+
class Pool
|
|
33
|
+
attr_accessor :all_sessions, :session_queue, :transaction_queue
|
|
34
|
+
|
|
35
|
+
def initialize client, min: 10, max: 100, keepalive: 1800,
|
|
36
|
+
write_ratio: 0.3, fail: true
|
|
37
|
+
@client = client
|
|
38
|
+
@min = min
|
|
39
|
+
@max = max
|
|
40
|
+
@keepalive = keepalive
|
|
41
|
+
@write_ratio = write_ratio
|
|
42
|
+
@write_ratio = 0 if write_ratio < 0
|
|
43
|
+
@write_ratio = 1 if write_ratio > 1
|
|
44
|
+
@fail = fail
|
|
45
|
+
|
|
46
|
+
@mutex = Mutex.new
|
|
47
|
+
@resource = ConditionVariable.new
|
|
48
|
+
|
|
49
|
+
# initialize pool and availability queue
|
|
50
|
+
init
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def with_session
|
|
54
|
+
session = checkout_session
|
|
55
|
+
begin
|
|
56
|
+
yield session
|
|
57
|
+
ensure
|
|
58
|
+
checkin_session session
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def checkout_session
|
|
63
|
+
action = nil
|
|
64
|
+
@mutex.synchronize do
|
|
65
|
+
loop do
|
|
66
|
+
fail ClientClosedError if @closed
|
|
67
|
+
|
|
68
|
+
read_session = session_queue.shift
|
|
69
|
+
return read_session if read_session
|
|
70
|
+
write_transaction = transaction_queue.shift
|
|
71
|
+
return write_transaction.session if write_transaction
|
|
72
|
+
|
|
73
|
+
if can_allocate_more_sessions?
|
|
74
|
+
@new_sessions_in_process += 1
|
|
75
|
+
action = :new
|
|
76
|
+
break
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
fail SessionLimitError if @fail
|
|
80
|
+
|
|
81
|
+
@resource.wait @mutex
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
return new_session! if action == :new
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def checkin_session session
|
|
89
|
+
unless all_sessions.include? session
|
|
90
|
+
fail ArgumentError, "Cannot checkin session"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
@mutex.synchronize do
|
|
94
|
+
session_queue.push session
|
|
95
|
+
|
|
96
|
+
@resource.signal
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
nil
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def with_transaction
|
|
103
|
+
tx = checkout_transaction
|
|
104
|
+
begin
|
|
105
|
+
yield tx
|
|
106
|
+
ensure
|
|
107
|
+
future do
|
|
108
|
+
# Create and checkin a new transaction
|
|
109
|
+
tx = tx.session.create_transaction
|
|
110
|
+
checkin_transaction tx
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def checkout_transaction
|
|
116
|
+
action = nil
|
|
117
|
+
@mutex.synchronize do
|
|
118
|
+
loop do
|
|
119
|
+
fail ClientClosedError if @closed
|
|
120
|
+
|
|
121
|
+
write_transaction = transaction_queue.shift
|
|
122
|
+
return write_transaction if write_transaction
|
|
123
|
+
read_session = session_queue.shift
|
|
124
|
+
if read_session
|
|
125
|
+
action = read_session
|
|
126
|
+
break
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if can_allocate_more_sessions?
|
|
130
|
+
@new_sessions_in_process += 1
|
|
131
|
+
action = :new
|
|
132
|
+
break
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
fail SessionLimitError if @fail
|
|
136
|
+
|
|
137
|
+
@resource.wait @mutex
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
if action.is_a? Google::Cloud::Spanner::Session
|
|
141
|
+
return action.create_transaction
|
|
142
|
+
end
|
|
143
|
+
return new_transaction! if action == :new
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def checkin_transaction tx
|
|
147
|
+
unless all_sessions.include? tx.session
|
|
148
|
+
fail ArgumentError, "Cannot checkin session"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
@mutex.synchronize do
|
|
152
|
+
transaction_queue.push tx
|
|
153
|
+
|
|
154
|
+
@resource.signal
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
nil
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def reset
|
|
161
|
+
close
|
|
162
|
+
init
|
|
163
|
+
|
|
164
|
+
true
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def close
|
|
168
|
+
@mutex.synchronize do
|
|
169
|
+
@closed = true
|
|
170
|
+
end
|
|
171
|
+
@keepalive_task.shutdown
|
|
172
|
+
# Unblock all waiting threads
|
|
173
|
+
@resource.broadcast
|
|
174
|
+
# Delete all sessions
|
|
175
|
+
@mutex.synchronize do
|
|
176
|
+
@all_sessions.each { |s| future { s.release! } }
|
|
177
|
+
@all_sessions = []
|
|
178
|
+
@session_queue = []
|
|
179
|
+
@transaction_queue = []
|
|
180
|
+
end
|
|
181
|
+
# shutdown existing thread pool
|
|
182
|
+
@thread_pool.shutdown
|
|
183
|
+
|
|
184
|
+
true
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def keepalive_or_release!
|
|
188
|
+
to_keepalive = []
|
|
189
|
+
to_release = []
|
|
190
|
+
|
|
191
|
+
@mutex.synchronize do
|
|
192
|
+
available_count = session_queue.count + transaction_queue.count
|
|
193
|
+
release_count = @min - available_count
|
|
194
|
+
release_count = 0 if release_count < 0
|
|
195
|
+
|
|
196
|
+
to_keepalive += (session_queue + transaction_queue).select do |x|
|
|
197
|
+
x.idle_since? @keepalive
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Remove a random portion of the sessions and transactions
|
|
201
|
+
to_release = to_keepalive.sample release_count
|
|
202
|
+
to_keepalive -= to_release
|
|
203
|
+
|
|
204
|
+
# Remove those to be released from circulation
|
|
205
|
+
@all_sessions -= to_release.map(&:session)
|
|
206
|
+
@session_queue -= to_release
|
|
207
|
+
@transaction_queue -= to_release
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
to_release.each { |x| future { x.release! } }
|
|
211
|
+
to_keepalive.each { |x| future { x.keepalive! } }
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
private
|
|
215
|
+
|
|
216
|
+
def init
|
|
217
|
+
# init the thread pool
|
|
218
|
+
@thread_pool = Concurrent::FixedThreadPool.new(
|
|
219
|
+
[2, Concurrent.processor_count].max * 2,
|
|
220
|
+
fallback_policy: :caller_runs
|
|
221
|
+
)
|
|
222
|
+
# init the queues
|
|
223
|
+
@new_sessions_in_process = @min.to_i
|
|
224
|
+
@all_sessions = []
|
|
225
|
+
@session_queue = []
|
|
226
|
+
@transaction_queue = []
|
|
227
|
+
# init the keepalive task
|
|
228
|
+
create_keepalive_task!
|
|
229
|
+
# init session queue
|
|
230
|
+
num_transactions = (@min * @write_ratio).round
|
|
231
|
+
num_sessions = @min.to_i - num_transactions
|
|
232
|
+
num_sessions.times.each do
|
|
233
|
+
future { checkin_session new_session! }
|
|
234
|
+
end
|
|
235
|
+
# init transaction queue
|
|
236
|
+
num_transactions.times.each do
|
|
237
|
+
future { checkin_transaction new_transaction! }
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def new_session!
|
|
242
|
+
session = @client.create_new_session
|
|
243
|
+
|
|
244
|
+
@mutex.synchronize do
|
|
245
|
+
# don't add if the pool is closed
|
|
246
|
+
return session.release! if @closed
|
|
247
|
+
|
|
248
|
+
@new_sessions_in_process -= 1
|
|
249
|
+
all_sessions << session
|
|
250
|
+
end
|
|
251
|
+
session
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def new_transaction!
|
|
255
|
+
new_session!.create_transaction
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def can_allocate_more_sessions?
|
|
259
|
+
# This is expected to be called from within a synchronize block
|
|
260
|
+
all_sessions.size + @new_sessions_in_process < @max
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def create_keepalive_task!
|
|
264
|
+
@keepalive_task = Concurrent::TimerTask.new(execution_interval: 300,
|
|
265
|
+
timeout_interval: 60) do
|
|
266
|
+
keepalive_or_release!
|
|
267
|
+
end
|
|
268
|
+
@keepalive_task.execute
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def future
|
|
272
|
+
Concurrent::Future.new(executor: @thread_pool) do
|
|
273
|
+
yield
|
|
274
|
+
end.execute
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|