google-cloud-spanner 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/lib/google-cloud-spanner.rb +106 -0
  3. data/lib/google/cloud/spanner.rb +382 -0
  4. data/lib/google/cloud/spanner/admin/database/v1.rb +17 -0
  5. data/lib/google/cloud/spanner/admin/database/v1/database_admin_client.rb +703 -0
  6. data/lib/google/cloud/spanner/admin/database/v1/database_admin_client_config.json +73 -0
  7. data/lib/google/cloud/spanner/admin/database/v1/doc/google/iam/v1/policy.rb +139 -0
  8. data/lib/google/cloud/spanner/admin/database/v1/doc/google/protobuf/any.rb +114 -0
  9. data/lib/google/cloud/spanner/admin/database/v1/doc/google/rpc/status.rb +83 -0
  10. data/lib/google/cloud/spanner/admin/database/v1/doc/google/spanner/admin/database/v1/spanner_database_admin.rb +188 -0
  11. data/lib/google/cloud/spanner/admin/instance/v1.rb +17 -0
  12. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/iam/v1/policy.rb +139 -0
  13. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/any.rb +114 -0
  14. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/field_mask.rb +223 -0
  15. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/rpc/status.rb +83 -0
  16. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/spanner/admin/instance/v1/spanner_instance_admin.rb +268 -0
  17. data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client.rb +868 -0
  18. data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client_config.json +78 -0
  19. data/lib/google/cloud/spanner/client.rb +1034 -0
  20. data/lib/google/cloud/spanner/commit.rb +351 -0
  21. data/lib/google/cloud/spanner/convert.rb +311 -0
  22. data/lib/google/cloud/spanner/credentials.rb +32 -0
  23. data/lib/google/cloud/spanner/data.rb +199 -0
  24. data/lib/google/cloud/spanner/database.rb +377 -0
  25. data/lib/google/cloud/spanner/database/job.rb +179 -0
  26. data/lib/google/cloud/spanner/database/list.rb +171 -0
  27. data/lib/google/cloud/spanner/errors.rb +73 -0
  28. data/lib/google/cloud/spanner/fields.rb +252 -0
  29. data/lib/google/cloud/spanner/instance.rb +472 -0
  30. data/lib/google/cloud/spanner/instance/config.rb +99 -0
  31. data/lib/google/cloud/spanner/instance/config/list.rb +171 -0
  32. data/lib/google/cloud/spanner/instance/job.rb +197 -0
  33. data/lib/google/cloud/spanner/instance/list.rb +167 -0
  34. data/lib/google/cloud/spanner/policy.rb +201 -0
  35. data/lib/google/cloud/spanner/pool.rb +279 -0
  36. data/lib/google/cloud/spanner/project.rb +480 -0
  37. data/lib/google/cloud/spanner/range.rb +99 -0
  38. data/lib/google/cloud/spanner/results.rb +280 -0
  39. data/lib/google/cloud/spanner/service.rb +458 -0
  40. data/lib/google/cloud/spanner/session.rb +565 -0
  41. data/lib/google/cloud/spanner/snapshot.rb +260 -0
  42. data/lib/google/cloud/spanner/transaction.rb +533 -0
  43. data/lib/google/cloud/spanner/v1.rb +17 -0
  44. data/lib/google/cloud/spanner/v1/doc/google/protobuf/duration.rb +77 -0
  45. data/lib/google/cloud/spanner/v1/doc/google/protobuf/struct.rb +73 -0
  46. data/lib/google/cloud/spanner/v1/doc/google/protobuf/timestamp.rb +81 -0
  47. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/keys.rb +148 -0
  48. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/mutation.rb +80 -0
  49. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/query_plan.rb +120 -0
  50. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/result_set.rb +175 -0
  51. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/spanner.rb +206 -0
  52. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/transaction.rb +351 -0
  53. data/lib/google/cloud/spanner/v1/spanner_client.rb +850 -0
  54. data/lib/google/cloud/spanner/v1/spanner_client_config.json +78 -0
  55. data/lib/google/cloud/spanner/version.rb +22 -0
  56. data/lib/google/spanner/admin/database/v1/spanner_database_admin_pb.rb +85 -0
  57. data/lib/google/spanner/admin/database/v1/spanner_database_admin_services_pb.rb +95 -0
  58. data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_pb.rb +106 -0
  59. data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_services_pb.rb +180 -0
  60. data/lib/google/spanner/v1/keys_pb.rb +33 -0
  61. data/lib/google/spanner/v1/mutation_pb.rb +38 -0
  62. data/lib/google/spanner/v1/query_plan_pb.rb +47 -0
  63. data/lib/google/spanner/v1/result_set_pb.rb +43 -0
  64. data/lib/google/spanner/v1/spanner_pb.rb +90 -0
  65. data/lib/google/spanner/v1/spanner_services_pb.rb +131 -0
  66. data/lib/google/spanner/v1/transaction_pb.rb +51 -0
  67. data/lib/google/spanner/v1/type_pb.rb +43 -0
  68. 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