google-cloud-bigtable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/LICENSE +201 -0
  4. data/README.md +65 -0
  5. data/lib/google/bigtable/admin/v2/bigtable_instance_admin_pb.rb +139 -0
  6. data/lib/google/bigtable/admin/v2/bigtable_instance_admin_services_pb.rb +85 -0
  7. data/lib/google/bigtable/admin/v2/bigtable_table_admin_pb.rb +137 -0
  8. data/lib/google/bigtable/admin/v2/bigtable_table_admin_services_pb.rb +117 -0
  9. data/lib/google/bigtable/admin/v2/common_pb.rb +24 -0
  10. data/lib/google/bigtable/admin/v2/instance_pb.rb +72 -0
  11. data/lib/google/bigtable/admin/v2/table_pb.rb +88 -0
  12. data/lib/google/bigtable/v2/bigtable_pb.rb +109 -0
  13. data/lib/google/bigtable/v2/bigtable_services_pb.rb +67 -0
  14. data/lib/google/bigtable/v2/data_pb.rb +155 -0
  15. data/lib/google/cloud/bigtable/admin/credentials.rb +26 -0
  16. data/lib/google/cloud/bigtable/admin/v2/bigtable_instance_admin_client.rb +1417 -0
  17. data/lib/google/cloud/bigtable/admin/v2/bigtable_instance_admin_client_config.json +123 -0
  18. data/lib/google/cloud/bigtable/admin/v2/bigtable_table_admin_client.rb +1079 -0
  19. data/lib/google/cloud/bigtable/admin/v2/bigtable_table_admin_client_config.json +109 -0
  20. data/lib/google/cloud/bigtable/admin/v2/credentials.rb +50 -0
  21. data/lib/google/cloud/bigtable/admin/v2/doc/google/bigtable/admin/v2/bigtable_instance_admin.rb +279 -0
  22. data/lib/google/cloud/bigtable/admin/v2/doc/google/bigtable/admin/v2/bigtable_table_admin.rb +353 -0
  23. data/lib/google/cloud/bigtable/admin/v2/doc/google/bigtable/admin/v2/instance.rb +194 -0
  24. data/lib/google/cloud/bigtable/admin/v2/doc/google/bigtable/admin/v2/table.rb +209 -0
  25. data/lib/google/cloud/bigtable/admin/v2/doc/google/iam/v1/iam_policy.rb +62 -0
  26. data/lib/google/cloud/bigtable/admin/v2/doc/google/iam/v1/policy.rb +127 -0
  27. data/lib/google/cloud/bigtable/admin/v2/doc/google/longrunning/operations.rb +92 -0
  28. data/lib/google/cloud/bigtable/admin/v2/doc/google/protobuf/any.rb +124 -0
  29. data/lib/google/cloud/bigtable/admin/v2/doc/google/protobuf/duration.rb +90 -0
  30. data/lib/google/cloud/bigtable/admin/v2/doc/google/protobuf/empty.rb +28 -0
  31. data/lib/google/cloud/bigtable/admin/v2/doc/google/protobuf/field_mask.rb +223 -0
  32. data/lib/google/cloud/bigtable/admin/v2/doc/google/protobuf/timestamp.rb +106 -0
  33. data/lib/google/cloud/bigtable/admin/v2/doc/google/rpc/status.rb +83 -0
  34. data/lib/google/cloud/bigtable/admin/v2.rb +200 -0
  35. data/lib/google/cloud/bigtable/admin.rb +196 -0
  36. data/lib/google/cloud/bigtable/app_profile/job.rb +102 -0
  37. data/lib/google/cloud/bigtable/app_profile/list.rb +159 -0
  38. data/lib/google/cloud/bigtable/app_profile.rb +373 -0
  39. data/lib/google/cloud/bigtable/chunk_processor.rb +253 -0
  40. data/lib/google/cloud/bigtable/cluster/job.rb +92 -0
  41. data/lib/google/cloud/bigtable/cluster/list.rb +169 -0
  42. data/lib/google/cloud/bigtable/cluster.rb +264 -0
  43. data/lib/google/cloud/bigtable/column_family.rb +280 -0
  44. data/lib/google/cloud/bigtable/column_range.rb +186 -0
  45. data/lib/google/cloud/bigtable/convert.rb +75 -0
  46. data/lib/google/cloud/bigtable/credentials.rb +24 -0
  47. data/lib/google/cloud/bigtable/errors.rb +35 -0
  48. data/lib/google/cloud/bigtable/gc_rule.rb +215 -0
  49. data/lib/google/cloud/bigtable/instance/cluster_map.rb +70 -0
  50. data/lib/google/cloud/bigtable/instance/job.rb +97 -0
  51. data/lib/google/cloud/bigtable/instance/list.rb +159 -0
  52. data/lib/google/cloud/bigtable/instance.rb +921 -0
  53. data/lib/google/cloud/bigtable/longrunning_job.rb +105 -0
  54. data/lib/google/cloud/bigtable/mutation_entry.rb +244 -0
  55. data/lib/google/cloud/bigtable/mutation_operations.rb +338 -0
  56. data/lib/google/cloud/bigtable/policy.rb +163 -0
  57. data/lib/google/cloud/bigtable/project.rb +580 -0
  58. data/lib/google/cloud/bigtable/read_modify_write_rule.rb +129 -0
  59. data/lib/google/cloud/bigtable/read_operations.rb +345 -0
  60. data/lib/google/cloud/bigtable/row.rb +125 -0
  61. data/lib/google/cloud/bigtable/row_filter/chain_filter.rb +539 -0
  62. data/lib/google/cloud/bigtable/row_filter/condition_filter.rb +108 -0
  63. data/lib/google/cloud/bigtable/row_filter/interleave_filter.rb +570 -0
  64. data/lib/google/cloud/bigtable/row_filter/simple_filter.rb +273 -0
  65. data/lib/google/cloud/bigtable/row_filter.rb +593 -0
  66. data/lib/google/cloud/bigtable/row_range.rb +174 -0
  67. data/lib/google/cloud/bigtable/rows_mutator.rb +120 -0
  68. data/lib/google/cloud/bigtable/rows_reader.rb +196 -0
  69. data/lib/google/cloud/bigtable/sample_row_key.rb +82 -0
  70. data/lib/google/cloud/bigtable/service.rb +817 -0
  71. data/lib/google/cloud/bigtable/table/cluster_state.rb +93 -0
  72. data/lib/google/cloud/bigtable/table/column_family_map.rb +68 -0
  73. data/lib/google/cloud/bigtable/table/list.rb +147 -0
  74. data/lib/google/cloud/bigtable/table.rb +676 -0
  75. data/lib/google/cloud/bigtable/v2/bigtable_client.rb +579 -0
  76. data/lib/google/cloud/bigtable/v2/bigtable_client_config.json +65 -0
  77. data/lib/google/cloud/bigtable/v2/credentials.rb +45 -0
  78. data/lib/google/cloud/bigtable/v2/doc/google/bigtable/v2/bigtable.rb +286 -0
  79. data/lib/google/cloud/bigtable/v2/doc/google/bigtable/v2/data.rb +492 -0
  80. data/lib/google/cloud/bigtable/v2/doc/google/protobuf/any.rb +124 -0
  81. data/lib/google/cloud/bigtable/v2/doc/google/protobuf/wrappers.rb +89 -0
  82. data/lib/google/cloud/bigtable/v2/doc/google/rpc/status.rb +83 -0
  83. data/lib/google/cloud/bigtable/v2.rb +132 -0
  84. data/lib/google/cloud/bigtable/value_range.rb +175 -0
  85. data/lib/google/cloud/bigtable/version.rb +22 -0
  86. data/lib/google/cloud/bigtable.rb +223 -0
  87. data/lib/google-cloud-bigtable.rb +167 -0
  88. metadata +283 -0
@@ -0,0 +1,174 @@
1
+ # Copyright 2018 Google LLC
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
+ # https://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
+ module Google
17
+ module Cloud
18
+ module Bigtable
19
+ # # RowRange
20
+ #
21
+ # Specifies a contiguous range of rows.
22
+ #
23
+ # * from key bound : The row key at which to from the range.
24
+ # If neither field is set, interpreted as the empty string, inclusive.
25
+ # * End key bound: The row key at which to end the range.
26
+ # If neither field is set, interpreted as the infinite row key, exclusive.
27
+ #
28
+ # @example
29
+ # require "google/cloud/bigtable"
30
+ #
31
+ # bigtable = Google::Cloud::Bigtable.new
32
+ # table = bigtable.table("my-instance", "my-table")
33
+ #
34
+ # # Range that includes all row keys including "user-001" to "user-005"
35
+ # table.new_row_range.from("user-001").to("user-005", inclusive: true)
36
+ #
37
+ # # Range that includes all row keys including "user-001" up to exclusive "user-010".
38
+ # table.new_row_range.from("user-001").to("user-010")
39
+ #
40
+ # # Range that includes all row keys including "user-001" up until end of the row keys.
41
+ # table.new_row_range.from("user-001")
42
+ #
43
+ # # Range that includes all row keys exclusive "user-001" up until end of the row keys.
44
+ # table.new_row_range.from("user-001", inclusive: false)
45
+ #
46
+ # # Range with unbounded from and the exclusive end "user-010"
47
+ # table.new_row_range.to("user-010")
48
+ #
49
+ # # Range that includes all row keys including from and end row keys "user-001", "user-010"
50
+ # table.new_row_range.between("user-001", "user-010")
51
+ #
52
+ # # Range that includes all row keys including "user-001" up until "user-010"
53
+ # table.new_row_range.of("user-001", "user-010")
54
+ #
55
+ class RowRange
56
+ # @private
57
+ # Create row range instance.
58
+ def initialize
59
+ @grpc = Google::Bigtable::V2::RowRange.new
60
+ end
61
+
62
+ # Set row range with the lower bound.
63
+ #
64
+ # @param key [String] Row key. Required
65
+ # @param inclusive [String] Inclusive/Exclusive lower bound.
66
+ # Default it is an inclusive lower bound.
67
+ # @return [Google::Cloud::Bigtable::RowRange]
68
+ #
69
+ # @example Inclusive lower bound.
70
+ # require "google/cloud/bigtable"
71
+ #
72
+ # bigtable = Google::Cloud::Bigtable.new
73
+ # table = bigtable.table("my-instance", "my-table")
74
+ #
75
+ # range = table.new_row_range.from("key-001")
76
+ #
77
+ # @example Exclusive lower bound.
78
+ # require "google/cloud/bigtable"
79
+ #
80
+ # bigtable = Google::Cloud::Bigtable.new
81
+ # table = bigtable.table("my-instance", "my-table")
82
+ #
83
+ # range = table.new_row_range.from("key-001", inclusive: false)
84
+ #
85
+ def from key, inclusive: true
86
+ if inclusive
87
+ @grpc.start_key_closed = key
88
+ else
89
+ @grpc.start_key_open = key
90
+ end
91
+ self
92
+ end
93
+
94
+ # Set row range with the upper bound.
95
+ #
96
+ # @param key [String] Row key. Required
97
+ # @param inclusive [String] Inclusive/Exclusive upper bound.
98
+ # Default it is an exclusive upper bound.
99
+ # @return [Google::Cloud::Bigtable::RowRange]
100
+ #
101
+ # @example Inclusive upper bound.
102
+ # require "google/cloud/bigtable"
103
+ #
104
+ # bigtable = Google::Cloud::Bigtable.new
105
+ # table = bigtable.table("my-instance", "my-table")
106
+ #
107
+ # range = table.new_row_range.to("key-001", inclusive: true)
108
+ #
109
+ # @example Exclusive upper bound.
110
+ # require "google/cloud/bigtable"
111
+ #
112
+ # bigtable = Google::Cloud::Bigtable.new
113
+ # table = bigtable.table("my-instance", "my-table")
114
+ #
115
+ # range = table.new_row_range.to("key-001")
116
+ #
117
+ def to key, inclusive: false
118
+ if inclusive
119
+ @grpc.end_key_closed = key
120
+ else
121
+ @grpc.end_key_open = key
122
+ end
123
+ self
124
+ end
125
+
126
+ # Set row range with the inclusive upper and lower bounds.
127
+ #
128
+ # @param from_key [String] Inclusive from row key. Required
129
+ # @param to_key [String] Inclusive end row key. Required
130
+ # @return [Google::Cloud::Bigtable::RowRange]
131
+ # Range with inclusive from and end row key.
132
+ #
133
+ # @example
134
+ # require "google/cloud/bigtable"
135
+ #
136
+ # bigtable = Google::Cloud::Bigtable.new
137
+ # table = bigtable.table("my-instance", "my-table")
138
+ #
139
+ # range = table.new_row_range.between("key-001", "key-010")
140
+ #
141
+ def between from_key, to_key
142
+ from(from_key).to(to_key, inclusive: true)
143
+ end
144
+
145
+ # Set row range with the inclusive lower and the exclusive upper bound.
146
+ #
147
+ # @param from_key [String] Inclusive from row key
148
+ # @param to_key [String] Exclusive end row key
149
+ # @return [Google::Cloud::Bigtable::RowRange]
150
+ # Range with inclusive from and exclusive end row key.
151
+ #
152
+ # @example
153
+ # require "google/cloud/bigtable"
154
+ #
155
+ # bigtable = Google::Cloud::Bigtable.new
156
+ # table = bigtable.table("my-instance", "my-table")
157
+ #
158
+ # range = table.new_row_range.of("key-001", "key-010")
159
+ #
160
+ def of from_key, to_key
161
+ from(from_key).to(to_key)
162
+ end
163
+
164
+ # @private
165
+ #
166
+ # @return [Google::Bigtable::V2::RowRange]
167
+ #
168
+ def to_grpc
169
+ @grpc
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Google LLC
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ require "google/rpc/code_pb"
19
+ require "google/cloud/bigtable/mutation_entry"
20
+
21
+ module Google
22
+ module Cloud
23
+ module Bigtable
24
+ # @private
25
+ # # RowsMutator
26
+ #
27
+ # Retryable mutate rows helper
28
+ #
29
+ class RowsMutator
30
+ # @private
31
+ # Retryable status codes
32
+ RETRYABLE_CODES = {
33
+ Google::Rpc::Code::DEADLINE_EXCEEDED => true,
34
+ Google::Rpc::Code::ABORTED => true,
35
+ Google::Rpc::Code::UNAVAILABLE => true
36
+ }.freeze
37
+
38
+ # @private
39
+ RETRY_LIMIT = 3
40
+
41
+ # @private
42
+ #
43
+ # Create mutate rows instance
44
+ #
45
+ # @param table [Google::Cloud::Bigtable::TableDataOperations]
46
+ # @param entries [Array<Google::Cloud::Bigtable::MutationEntry>]
47
+ #
48
+ def initialize table, entries
49
+ @table = table
50
+ @entries = entries
51
+ end
52
+
53
+ # Apply mutations.
54
+ #
55
+ # @return [Array<Google::Bigtable::V2::MutateRowsResponse::Entry>]
56
+ #
57
+ def apply_mutations
58
+ @req_entries = @entries.map(&:to_grpc)
59
+ statuses = mutate_rows(@req_entries)
60
+
61
+ # Collect retryable mutations indices
62
+ indices = statuses.each_with_object([]) do |e, r|
63
+ if @entries[e.index].retryable? && RETRYABLE_CODES[e.status.code]
64
+ r << e.index
65
+ end
66
+ end
67
+
68
+ return statuses if indices.empty?
69
+
70
+ (RETRY_LIMIT - 1).times do
71
+ break if indices.empty?
72
+ indices = retry_entries(statuses, indices)
73
+ end
74
+
75
+ statuses
76
+ end
77
+
78
+ private
79
+
80
+ # Mutate rows
81
+ #
82
+ # @param entries [Array<Google::Cloud::Bigtable::MutationEntry>]
83
+ # @return [Array<Google::Bigtable::V2::MutateRowsResponse::Entry>]
84
+ #
85
+ def mutate_rows entries
86
+ response = @table.client.mutate_rows(
87
+ @table.path,
88
+ entries,
89
+ app_profile_id: @table.app_profile_id
90
+ )
91
+ response.each_with_object([]) do |res, statuses|
92
+ statuses.concat(res.entries)
93
+ end
94
+ rescue Google::Gax::GaxError => e
95
+ raise Google::Cloud::Error.from_error(e.cause)
96
+ rescue GRPC::BadStatus => e
97
+ raise Google::Cloud::Error.from_error(e)
98
+ end
99
+
100
+ # Collected failed entries, retry mutation and update status
101
+ #
102
+ # @param statuses [Array<Google::Bigtable::V2::MutateRowsResponse::Entry>]
103
+ # @param indices [Array<Integer>]
104
+ # Retry entries position mapping list
105
+ # @return [Array<Integer>]
106
+ # New list of failed entries positions
107
+ #
108
+ def retry_entries statuses, indices
109
+ entries = indices.map { |i| @req_entries[i] }
110
+ retry_statuses = mutate_rows(entries)
111
+
112
+ retry_statuses.each_with_object([]) do |e, next_indices|
113
+ next_indices << indices[e.index] if RETRYABLE_CODES[e.status.code]
114
+ statuses[indices[e.index]].status = e.status
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Google LLC
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ require "google/cloud/bigtable/chunk_processor"
19
+
20
+ module Google
21
+ module Cloud
22
+ module Bigtable
23
+ # @private
24
+ # # RowsReader
25
+ #
26
+ # Retyable read rows helper
27
+ #
28
+ class RowsReader
29
+ # @private
30
+ # Retryable error list.
31
+ RETRYABLE_ERRORS = [
32
+ GRPC::DeadlineExceeded,
33
+ GRPC::Aborted,
34
+ GRPC::Unavailable,
35
+ GRPC::Core::CallError
36
+ ].freeze
37
+
38
+ # @private
39
+ # Default retry limit
40
+ RETRY_LIMIT = 3
41
+
42
+ # @private
43
+ # @return [Integer] Current retry count
44
+ attr_accessor :retry_count
45
+
46
+ # @private
47
+ #
48
+ # Create read rows instance
49
+ #
50
+ # @param table [Google::Cloud::Bigtable::TableDataOperations]
51
+ #
52
+ def initialize table
53
+ @table = table
54
+ @chunk_processor = ChunkProcessor.new
55
+ @rows_count = 0
56
+ @retry_count = 0
57
+ end
58
+
59
+ # Read rows
60
+ #
61
+ # @param rows [Google::Bigtable::V2::RowSet]
62
+ # The row keys and/or ranges to read.
63
+ # If not specified, reads from all rows.
64
+ # A hash of the same form as `Google::Bigtable::V2::RowSet`
65
+ # can also be provided.
66
+ # @param filter [Google::Bigtable::V2::RowFilter | Hash]
67
+ # The filter to apply to the contents of the specified row(s). If unset,
68
+ # reads the entirety of each row.
69
+ # A hash of the same form as `Google::Bigtable::V2::RowFilter`
70
+ # can also be provided.
71
+ # @param rows_limit [Integer]
72
+ # The read will terminate after committing to N rows' worth of results.
73
+ # The default (zero) is to return all results.
74
+ # @return [:yields: row]
75
+ # Array of row or yield block for each processed row.
76
+
77
+ def read \
78
+ rows: nil,
79
+ filter: nil,
80
+ rows_limit: nil
81
+ response = @table.client.read_rows(
82
+ @table.path,
83
+ rows: rows,
84
+ filter: filter,
85
+ rows_limit: rows_limit,
86
+ app_profile_id: @table.app_profile_id
87
+ )
88
+ response.each do |res|
89
+ res.chunks.each do |chunk|
90
+ @retry_count = 0
91
+ row = @chunk_processor.process(chunk)
92
+ next if row.nil?
93
+ yield row
94
+ @rows_count += 1
95
+ end
96
+ end
97
+
98
+ @chunk_processor.validate_last_row_complete
99
+ end
100
+
101
+ # Last read row key
102
+ #
103
+ # @return [String]
104
+
105
+ def last_key
106
+ @chunk_processor.last_key
107
+ end
108
+
109
+ # Calucate and return read rows limit and row set based on last read key
110
+ #
111
+ # @param rows_limit [Integer]
112
+ # The read will terminate after committing to N rows' worth of results.
113
+ # The default (zero) is to return all results.
114
+ # @param row_set [Google::Bigtable::V2::RowSet]
115
+ # The row keys and/or ranges to read.
116
+ # If not specified, reads from all rows.
117
+ # A hash of the same form as `Google::Bigtable::V2::RowSet`
118
+ # can also be provided.
119
+ # @return [Integer, Google::Bigtable::V2::RowSet]
120
+
121
+ def retry_options rows_limit, row_set
122
+ return [rows_limit, row_set] unless last_key
123
+
124
+ # 1. Reduce the limit by the number of already returned responses.
125
+ rows_limit -= @rows_count if rows_limit
126
+
127
+ # 2. Remove ranges that have already been read, and reduce ranges that
128
+ # include the last read rows
129
+ if last_key
130
+ delete_indexes = []
131
+
132
+ row_set.row_ranges.each_with_index do |range, i|
133
+ if end_key_read?(range)
134
+ delete_indexes << i
135
+ elsif start_key_read?(range)
136
+ range.start_key_open = last_key
137
+ end
138
+ end
139
+
140
+ delete_indexes.each { |i| row_set.row_ranges.delete_at(i) }
141
+ end
142
+
143
+ if row_set.row_ranges.empty?
144
+ row_set.row_ranges <<
145
+ Google::Bigtable::V2::RowRange.new(start_key_open: last_key)
146
+ end
147
+
148
+ # 3. Remove all individual keys before and up to the last read key
149
+ row_set.row_keys.select! { |k| k > last_key }
150
+
151
+ @chunk_processor.reset_to_new_row
152
+ [rows_limit, row_set]
153
+ end
154
+
155
+ # Check if read operation is retryable.
156
+ #
157
+ # @return [Boolean]
158
+ def retryable?
159
+ @retry_count < RowsReader::RETRY_LIMIT
160
+ end
161
+
162
+ private
163
+
164
+ # Check start key already read for range
165
+ #
166
+ # @param range [Google::Bigtable::V2::RowRange]
167
+ # @return [Boolean]
168
+ #
169
+ def start_key_read? range
170
+ start_key = if !range.start_key_closed.empty?
171
+ range.start_key_closed
172
+ else
173
+ range.start_key_open
174
+ end
175
+
176
+ start_key.empty? || last_key >= start_key
177
+ end
178
+
179
+ # Check end key already read for range
180
+ #
181
+ # @param range [Google::Bigtable::V2::RowRange]
182
+ # @return [Boolean]
183
+ #
184
+ def end_key_read? range
185
+ end_key = if !range.end_key_closed.empty?
186
+ range.end_key_closed
187
+ else
188
+ range.end_key_open
189
+ end
190
+
191
+ end_key && end_key <= last_key
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end