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.
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