google-cloud-bigtable 2.2.0 → 2.5.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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/CONTRIBUTING.md +4 -5
  4. data/LOGGING.md +1 -1
  5. data/OVERVIEW.md +5 -5
  6. data/lib/google/cloud/bigtable/app_profile/list.rb +2 -2
  7. data/lib/google/cloud/bigtable/backup.rb +22 -1
  8. data/lib/google/cloud/bigtable/backup/list.rb +2 -2
  9. data/lib/google/cloud/bigtable/chunk_processor.rb +4 -3
  10. data/lib/google/cloud/bigtable/cluster.rb +22 -2
  11. data/lib/google/cloud/bigtable/cluster/list.rb +2 -2
  12. data/lib/google/cloud/bigtable/column_family.rb +4 -0
  13. data/lib/google/cloud/bigtable/column_family_map.rb +2 -0
  14. data/lib/google/cloud/bigtable/convert.rb +12 -0
  15. data/lib/google/cloud/bigtable/encryption_info.rb +118 -0
  16. data/lib/google/cloud/bigtable/gc_rule.rb +2 -0
  17. data/lib/google/cloud/bigtable/instance.rb +3 -2
  18. data/lib/google/cloud/bigtable/instance/cluster_map.rb +61 -21
  19. data/lib/google/cloud/bigtable/instance/list.rb +2 -2
  20. data/lib/google/cloud/bigtable/mutation_entry.rb +12 -9
  21. data/lib/google/cloud/bigtable/mutation_operations.rb +6 -5
  22. data/lib/google/cloud/bigtable/policy.rb +2 -1
  23. data/lib/google/cloud/bigtable/project.rb +4 -3
  24. data/lib/google/cloud/bigtable/routing_policy.rb +3 -1
  25. data/lib/google/cloud/bigtable/row.rb +5 -1
  26. data/lib/google/cloud/bigtable/row_filter/simple_filter.rb +6 -1
  27. data/lib/google/cloud/bigtable/rows_reader.rb +6 -6
  28. data/lib/google/cloud/bigtable/status.rb +4 -1
  29. data/lib/google/cloud/bigtable/table.rb +62 -19
  30. data/lib/google/cloud/bigtable/table/cluster_state.rb +43 -9
  31. data/lib/google/cloud/bigtable/table/list.rb +2 -2
  32. data/lib/google/cloud/bigtable/table/restore_job.rb +1 -1
  33. data/lib/google/cloud/bigtable/value_range.rb +24 -6
  34. data/lib/google/cloud/bigtable/version.rb +1 -1
  35. metadata +7 -6
@@ -509,8 +509,8 @@ module Google
509
509
  #
510
510
  # entry = table.new_mutation_entry("user-1")
511
511
  # entry.set_cell(
512
- # "cf-1",
513
- # "field-1",
512
+ # "cf1",
513
+ # "field1",
514
514
  # "XYZ",
515
515
  # timestamp: (Time.now.to_f * 1000000).round(-3) # microseconds
516
516
  # ).delete_cells("cf2", "field02")
@@ -520,6 +520,7 @@ module Google
520
520
  def table table_id, view: nil, perform_lookup: nil, app_profile_id: nil
521
521
  ensure_service!
522
522
 
523
+ view ||= :SCHEMA_VIEW
523
524
  table = if perform_lookup
524
525
  grpc = service.get_table instance_id, table_id, view: view
525
526
  Table.from_grpc grpc, service, view: view
@@ -15,28 +15,31 @@
15
15
  # limitations under the License.
16
16
 
17
17
 
18
+ require "google/cloud/bigtable/admin/v2"
19
+
18
20
  module Google
19
21
  module Cloud
20
22
  module Bigtable
21
23
  class Instance
22
24
  ##
23
- # Instance::ClusterMap is a hash with cluster ID keys and cluster configuration values.
24
- # It is used to create a cluster.
25
+ # Instance::ClusterMap is a hash with cluster ID keys and cluster configuration values. It is used to create a
26
+ # cluster.
25
27
  #
26
28
  # @example
29
+ # require "google/cloud/bigtable"
27
30
  #
28
- # clusters = Google::Cloud::Bigtable::Instance::ClusterMap.new
31
+ # bigtable = Google::Cloud::Bigtable.new
29
32
  #
30
- # clusters.add("cluster-1", "us-east1-b", nodes: 3, storage_type: :SSD)
33
+ # job = bigtable.create_instance("my-instance") do |clusters|
34
+ # clusters.add("test-cluster", "us-east1-b", nodes: 3, storage_type: :SSD)
35
+ # end
31
36
  #
32
- # # Or
33
- # clusters.add("cluster-2", "us-east1-b")
37
+ # job.wait_until_done!
34
38
  #
35
39
  class ClusterMap < DelegateClass(::Hash)
36
40
  # @private
37
41
  #
38
- # Creates a new Instance::ClusterMap with an hash of Cluster name and
39
- # cluster grpc instances.
42
+ # Creates a new Instance::ClusterMap with an hash of Cluster name and cluster grpc instances.
40
43
  def initialize value = {}
41
44
  super value
42
45
  end
@@ -45,24 +48,61 @@ module Google
45
48
  # Adds a cluster to the cluster map.
46
49
  #
47
50
  # @param name [String] The unique identifier for the cluster.
48
- # @param location [String]
49
- # The location where this cluster's nodes and storage reside. For best
50
- # performance, clients should be located as close as possible to this
51
- # cluster. Currently only zones are supported.
52
- # @param nodes [Integer] Number of nodes for the cluster. When creating
53
- # an instance of type `:DEVELOPMENT`, `nodes` must not be set.
54
- # @param storage_type [Symbol]
55
- # Valid values are:
56
- # * `:SSD`(Flash (SSD) storage should be used),
57
- # *`:HDD`(Magnetic drive (HDD) storage should be used)
51
+ # @param location [String] The location where this cluster's nodes and storage reside. For best performance,
52
+ # clients should be located as close as possible to this cluster. Currently only zones are supported.
53
+ # @param nodes [Integer] Number of nodes for the cluster. When creating an instance of type `:DEVELOPMENT`,
54
+ # `nodes` must not be set.
55
+ # @param storage_type [Symbol] The type of storage used by this cluster to serve its parent instance's tables,
56
+ # unless explicitly overridden. Valid values are:
57
+ #
58
+ # * `:SSD` - Flash (SSD) storage should be used.
59
+ # * `:HDD` - Magnetic drive (HDD) storage should be used.
60
+ #
61
+ # If not set then default will set to `:STORAGE_TYPE_UNSPECIFIED`.
62
+ # @param kms_key [String] The full name of a Cloud KMS encryption key for a CMEK-protected cluster, in the
63
+ # format `projects/{key_project_id}/locations/{location}/keyRings/{ring_name}/cryptoKeys/{key_name}`.
64
+ #
65
+ # The requirements for this key are:
66
+ #
67
+ # 1. The Cloud Bigtable service account associated with the project that contains this cluster must be
68
+ # granted the `cloudkms.cryptoKeyEncrypterDecrypter` role on the CMEK key.
69
+ # 2. Only regional keys can be used and the region of the CMEK key must match the region of the cluster.
70
+ # 3. All clusters within an instance must use the same CMEK key.
71
+ #
72
+ # @example
73
+ # require "google/cloud/bigtable"
74
+ #
75
+ # bigtable = Google::Cloud::Bigtable.new
76
+ #
77
+ # job = bigtable.create_instance("my-instance") do |clusters|
78
+ # clusters.add("test-cluster", "us-east1-b", nodes: 3, storage_type: :SSD)
79
+ # end
80
+ #
81
+ # job.wait_until_done!
82
+ #
83
+ # @example With a Cloud KMS encryption key name for a CMEK-protected cluster:
84
+ # require "google/cloud/bigtable"
85
+ #
86
+ # bigtable = Google::Cloud::Bigtable.new
87
+ #
88
+ # kms_key_name = "projects/a/locations/b/keyRings/c/cryptoKeys/d"
89
+ # job = bigtable.create_instance("my-instance") do |clusters|
90
+ # clusters.add("test-cluster", "us-east1-b", kms_key: kms_key_name)
91
+ # end
58
92
  #
59
- # If not set then default will set to `:STORAGE_TYPE_UNSPECIFIED`
93
+ # job.wait_until_done!
60
94
  #
61
- def add name, location, nodes: nil, storage_type: nil
95
+ def add name, location, nodes: nil, storage_type: nil, kms_key: nil
96
+ if kms_key
97
+ encryption_config = Google::Cloud::Bigtable::Admin::V2::Cluster::EncryptionConfig.new(
98
+ kms_key_name: kms_key
99
+ )
100
+ end
62
101
  attrs = {
63
102
  serve_nodes: nodes,
64
103
  location: location,
65
- default_storage_type: storage_type
104
+ default_storage_type: storage_type,
105
+ encryption_config: encryption_config
66
106
  }.delete_if { |_, v| v.nil? }
67
107
 
68
108
  self[name] = Google::Cloud::Bigtable::Admin::V2::Cluster.new attrs
@@ -126,12 +126,12 @@ module Google
126
126
  # puts instance.instance_id
127
127
  # end
128
128
  #
129
- def all
129
+ def all &block
130
130
  return enum_for :all unless block_given?
131
131
 
132
132
  results = self
133
133
  loop do
134
- results.each { |r| yield r }
134
+ results.each(&block)
135
135
  break unless results.next?
136
136
  results = results.next
137
137
  end
@@ -15,6 +15,8 @@
15
15
  # limitations under the License.
16
16
 
17
17
 
18
+ require "google/cloud/bigtable/convert"
19
+
18
20
  module Google
19
21
  module Cloud
20
22
  module Bigtable
@@ -84,8 +86,9 @@ module Google
84
86
  # @param qualifier [String] Column qualifier name.
85
87
  # The qualifier of the column into which new data should be written.
86
88
  # Can be any byte string, including an empty string.
87
- # @param value [String, Integer] Cell value data.
88
- # The value to be written into the specified cell.
89
+ # @param value [String, Integer] Cell value data. The value to be written
90
+ # into the specified cell. If the argument is an Integer, it will be
91
+ # encoded as a 64-bit signed big-endian integer.
89
92
  # @param timestamp [Integer] Timestamp value in microseconds.
90
93
  # The timestamp of the cell into which new data should be written.
91
94
  # Use -1 for current Bigtable server time.
@@ -104,15 +107,15 @@ module Google
104
107
  # @example With timestamp.
105
108
  # entry = Google::Cloud::Bigtable::MutationEntry.new("user-1")
106
109
  # entry.set_cell(
107
- # "cf-1",
108
- # "field-1",
110
+ # "cf1",
111
+ # "field1",
109
112
  # "XYZ",
110
113
  # timestamp: (Time.now.to_f * 1000000).round(-3) # microseconds
111
114
  # )
112
115
  #
113
116
  def set_cell family, qualifier, value, timestamp: nil
114
117
  # If value is integer, covert it to a 64-bit signed big-endian integer.
115
- value = [value].pack "q>" if value.is_a? Integer
118
+ value = Convert.integer_to_signed_be_64 value
116
119
  options = {
117
120
  family_name: family,
118
121
  column_qualifier: qualifier,
@@ -156,14 +159,14 @@ module Google
156
159
  #
157
160
  # @example Without timestamp range.
158
161
  # entry = Google::Cloud::Bigtable::MutationEntry.new("user-1")
159
- # entry.delete_cells("cf-1", "field-1")
162
+ # entry.delete_cells("cf1", "field1")
160
163
  #
161
164
  # @example With timestamp range.
162
165
  # entry = Google::Cloud::Bigtable::MutationEntry.new("user-1")
163
166
  # timestamp_micros = (Time.now.to_f * 1000000).round(-3)
164
167
  # entry.delete_cells(
165
168
  # "cf1",
166
- # "field-1",
169
+ # "field1",
167
170
  # timestamp_from: timestamp_micros - 5000000,
168
171
  # timestamp_to: timestamp_micros
169
172
  # )
@@ -172,7 +175,7 @@ module Google
172
175
  # timestamp_micros = (Time.now.to_f * 1000000).round(-3)
173
176
  # entry.delete_cells(
174
177
  # "cf1",
175
- # "field-1",
178
+ # "field1",
176
179
  # timestamp_from: timestamp_micros - 5000000
177
180
  # )
178
181
  #
@@ -201,7 +204,7 @@ module Google
201
204
  #
202
205
  # @example
203
206
  # entry = Google::Cloud::Bigtable::MutationEntry.new("user-1")
204
- # entry.delete_from_family("cf-1")
207
+ # entry.delete_from_family("cf1")
205
208
  #
206
209
  def delete_from_family family
207
210
  @mutations << Google::Cloud::Bigtable::V2::Mutation.new(delete_from_family: { family_name: family })
@@ -65,8 +65,8 @@ module Google
65
65
  #
66
66
  # entry = table.new_mutation_entry("user-1")
67
67
  # entry.set_cell(
68
- # "cf-1",
69
- # "field-1",
68
+ # "cf1",
69
+ # "field1",
70
70
  # "XYZ",
71
71
  # timestamp: (Time.now.to_f * 1000000).round(-3) # microseconds
72
72
  # ).delete_cells("cf2", "field02")
@@ -219,8 +219,8 @@ module Google
219
219
  # predicate_filter = Google::Cloud::Bigtable::RowFilter.key("user-10")
220
220
  # on_match_mutations = Google::Cloud::Bigtable::MutationEntry.new
221
221
  # on_match_mutations.set_cell(
222
- # "cf-1",
223
- # "field-1",
222
+ # "cf1",
223
+ # "field1",
224
224
  # "XYZ",
225
225
  # timestamp: (Time.now.to_f * 1000000).round(-3) # microseconds
226
226
  # ).delete_cells("cf2", "field02")
@@ -335,7 +335,8 @@ module Google
335
335
  # end
336
336
  #
337
337
  class Response
338
- attr_reader :index, :status
338
+ attr_reader :index
339
+ attr_reader :status
339
340
 
340
341
  ##
341
342
  # @private Creates a MutationEntry::Response object.
@@ -54,7 +54,8 @@ module Google
54
54
  # policy.roles["roles/viewer"] = ["allUsers"]
55
55
  #
56
56
  class Policy
57
- attr_reader :etag, :roles
57
+ attr_reader :etag
58
+ attr_reader :roles
58
59
 
59
60
  # Creates a Policy instance.
60
61
  # @param etag [String]
@@ -316,18 +316,18 @@ module Google
316
316
  #
317
317
  # table = bigtable.table("my-instance", "my-table")
318
318
  #
319
- # @example Get a table with schema-only view.
319
+ # @example Retrieve a table with a schema-only view.
320
320
  # require "google/cloud/bigtable"
321
321
  #
322
322
  # bigtable = Google::Cloud::Bigtable.new
323
323
  #
324
- # table = bigtable.table("my-instance", "my-table", perform_lookup: true, view: :SCHEMA_VIEW)
324
+ # table = bigtable.table("my-instance", "my-table", perform_lookup: true)
325
325
  # if table
326
326
  # puts table.name
327
327
  # puts table.column_families
328
328
  # end
329
329
  #
330
- # @example Get a table with all fields, cluster states, and column families.
330
+ # @example Retrieve a table with all fields, cluster states, and column families.
331
331
  # require "google/cloud/bigtable"
332
332
  #
333
333
  # bigtable = Google::Cloud::Bigtable.new
@@ -342,6 +342,7 @@ module Google
342
342
  def table instance_id, table_id, view: nil, perform_lookup: nil, app_profile_id: nil
343
343
  ensure_service!
344
344
 
345
+ view ||= :SCHEMA_VIEW
345
346
  table = if perform_lookup
346
347
  grpc = service.get_table instance_id, table_id, view: view
347
348
  Table.from_grpc grpc, service, view: view
@@ -141,7 +141,8 @@ module Google
141
141
  # Default value is false.
142
142
  #
143
143
  class SingleClusterRouting < RoutingPolicy
144
- attr_reader :cluster_id, :allow_transactional_writes
144
+ attr_reader :cluster_id
145
+ attr_reader :allow_transactional_writes
145
146
 
146
147
  ##
147
148
  # Creates a new single-cluster routing policy.
@@ -155,6 +156,7 @@ module Google
155
156
  # Default value is false.
156
157
  #
157
158
  def initialize cluster_id, allow_transactional_writes
159
+ super()
158
160
  @cluster_id = cluster_id
159
161
  @allow_transactional_writes = allow_transactional_writes
160
162
  end
@@ -30,7 +30,11 @@ module Google
30
30
  # Row cell built from data chunks.
31
31
  #
32
32
  class Cell
33
- attr_reader :family, :qualifier, :value, :labels, :timestamp
33
+ attr_reader :family
34
+ attr_reader :qualifier
35
+ attr_reader :value
36
+ attr_reader :labels
37
+ attr_reader :timestamp
34
38
 
35
39
  ##
36
40
  # Creates a row cell instance.
@@ -15,6 +15,8 @@
15
15
  # limitations under the License.
16
16
 
17
17
 
18
+ require "google/cloud/bigtable/convert"
19
+
18
20
  module Google
19
21
  module Cloud
20
22
  module Bigtable
@@ -147,10 +149,13 @@ module Google
147
149
  # will not match the new line character `\n`, which may be present in a
148
150
  # binary value.
149
151
  #
150
- # @param regex [String] Regex to match cell value.
152
+ # @param regex [String, Integer] Regex to match cell value, or an Integer
153
+ # value to be encoded as a 64-bit signed big-endian integer.
151
154
  # @return [Google::Cloud::Bigtable::RowFilter::SimpleFilter]
152
155
  #
153
156
  def value regex
157
+ # If regex is integer, covert it to a 64-bit signed big-endian integer.
158
+ regex = Convert.integer_to_signed_be_64 regex
154
159
  @grpc.value_regex_filter = regex
155
160
  self
156
161
  end
@@ -170,10 +170,10 @@ module Google
170
170
  # @return [Boolean]
171
171
  #
172
172
  def start_key_read? range
173
- start_key = if !range.start_key_closed.empty?
174
- range.start_key_closed
175
- else
173
+ start_key = if range.start_key_closed.empty?
176
174
  range.start_key_open
175
+ else
176
+ range.start_key_closed
177
177
  end
178
178
 
179
179
  start_key.empty? || last_key >= start_key
@@ -186,10 +186,10 @@ module Google
186
186
  # @return [Boolean]
187
187
  #
188
188
  def end_key_read? range
189
- end_key = if !range.end_key_closed.empty?
190
- range.end_key_closed
191
- else
189
+ end_key = if range.end_key_closed.empty?
192
190
  range.end_key_open
191
+ else
192
+ range.end_key_closed
193
193
  end
194
194
 
195
195
  end_key && end_key <= last_key
@@ -47,7 +47,10 @@ module Google
47
47
  # end
48
48
  #
49
49
  class Status
50
- attr_reader :code, :description, :message, :details
50
+ attr_reader :code
51
+ attr_reader :description
52
+ attr_reader :message
53
+ attr_reader :details
51
54
 
52
55
  ##
53
56
  # @private Creates a Status object.
@@ -56,6 +56,14 @@ module Google
56
56
  # The gRPC Service object.
57
57
  attr_accessor :service
58
58
 
59
+ # @private
60
+ # The current gRPC resource, for testing only.
61
+ attr_accessor :grpc
62
+
63
+ # @private
64
+ # The current loaded_views, for testing only. See #check_view_and_load, below.
65
+ attr_reader :loaded_views
66
+
59
67
  ##
60
68
  # @return [String] App profile ID for request routing.
61
69
  #
@@ -64,10 +72,11 @@ module Google
64
72
  # @private
65
73
  #
66
74
  # Creates a new Table instance.
67
- def initialize grpc, service, view: nil
75
+ def initialize grpc, service, view:
68
76
  @grpc = grpc
69
77
  @service = service
70
- @view = view || :SCHEMA_VIEW
78
+ raise ArgumentError, "view must not be nil" if view.nil?
79
+ @loaded_views = Set[view]
71
80
  end
72
81
 
73
82
  ##
@@ -109,7 +118,8 @@ module Google
109
118
  end
110
119
 
111
120
  ##
112
- # Reloads table data.
121
+ # Reloads table data with the provided `view`, or with `SCHEMA_VIEW`
122
+ # if none is provided. Previously loaded data is not retained.
113
123
  #
114
124
  # @param view [Symbol] Table view type.
115
125
  # Default view type is `:SCHEMA_VIEW`.
@@ -123,22 +133,41 @@ module Google
123
133
  # @return [Google::Cloud::Bigtable::Table]
124
134
  #
125
135
  def reload! view: nil
126
- @view = view || :SCHEMA_VIEW
136
+ view ||= :SCHEMA_VIEW
127
137
  @grpc = service.get_table instance_id, name, view: view
138
+ @loaded_views = Set[view]
128
139
  self
129
140
  end
130
141
 
131
142
  ##
132
- # Map from cluster ID to per-cluster table state.
143
+ # Returns an array of {Table::ClusterState} objects that map cluster ID
144
+ # to per-cluster table state.
145
+ #
133
146
  # If it could not be determined whether or not the table has data in a
134
147
  # particular cluster (for example, if its zone is unavailable), then
135
- # there will be an entry for the cluster with UNKNOWN `replication_status`.
136
- # Views: `FULL`.
148
+ # the cluster state's `replication_state` will be `UNKNOWN`.
149
+ #
150
+ # Reloads the table with the `FULL` view type to retrieve the cluster states
151
+ # data, unless the table was previously loaded with view type `ENCRYPTION_VIEW`,
152
+ # `REPLICATION_VIEW` or `FULL`.
137
153
  #
138
154
  # @return [Array<Google::Cloud::Bigtable::Table::ClusterState>]
139
155
  #
156
+ # @example Retrieve a table with cluster states.
157
+ # require "google/cloud/bigtable"
158
+ #
159
+ # bigtable = Google::Cloud::Bigtable.new
160
+ #
161
+ # table = bigtable.table("my-instance", "my-table", view: :FULL, perform_lookup: true)
162
+ #
163
+ # table.cluster_states.each do |cs|
164
+ # puts cs.cluster_name
165
+ # puts cs.replication_state
166
+ # puts cs.encryption_infos.first.encryption_type
167
+ # end
168
+ #
140
169
  def cluster_states
141
- check_view_and_load :REPLICATION_VIEW
170
+ check_view_and_load :FULL, skip_if: [:ENCRYPTION_VIEW, :REPLICATION_VIEW]
142
171
  @grpc.cluster_states.map do |name, state_grpc|
143
172
  ClusterState.from_grpc state_grpc, name
144
173
  end
@@ -146,15 +175,19 @@ module Google
146
175
 
147
176
  ##
148
177
  # Returns a frozen object containing the column families configured for
149
- # the table, mapped by column family name. Reloads the table if
150
- # necessary to retrieve the column families data, since it is only
151
- # available in a table with view type `SCHEMA_VIEW` or `FULL`.
178
+ # the table, mapped by column family name.
179
+ #
180
+ # Reloads the table if necessary to retrieve the column families data,
181
+ # since it is only available in a table with view type `SCHEMA_VIEW`
182
+ # or `FULL`. Previously loaded data is retained.
152
183
  #
153
184
  # Also accepts a block for making modifications to the table's column
154
185
  # families. After the modifications are completed, the table will be
155
186
  # updated with the changes, and the updated column families will be
156
187
  # returned.
157
188
  #
189
+ # @see https://cloud.google.com/bigtable/docs/garbage-collection Garbage collection
190
+ #
158
191
  # @yield [column_families] A block for modifying the table's column
159
192
  # families. Applies multiple column modifications. Performs a series
160
193
  # of column family modifications on the specified table. Either all or
@@ -222,7 +255,10 @@ module Google
222
255
  # The granularity (e.g. `MILLIS`, `MICROS`) at which timestamps are stored in
223
256
  # this table. Timestamps not matching the granularity will be rejected.
224
257
  # If unspecified at creation time, the value will be set to `MILLIS`.
225
- # Views: `SCHEMA_VIEW`, `FULL`.
258
+ #
259
+ # Reloads the table if necessary to retrieve the column families data,
260
+ # since it is only available in a table with view type `SCHEMA_VIEW`
261
+ # or `FULL`. Previously loaded data is retained.
226
262
  #
227
263
  # @return [Symbol]
228
264
  #
@@ -437,7 +473,7 @@ module Google
437
473
  }.delete_if { |_, v| v.nil? })
438
474
 
439
475
  grpc = service.create_table instance_id, table_id, table, initial_splits: initial_splits
440
- from_grpc grpc, service
476
+ from_grpc grpc, service, view: :SCHEMA_VIEW
441
477
  end
442
478
 
443
479
  ##
@@ -623,7 +659,7 @@ module Google
623
659
  # @param view [Symbol] View type.
624
660
  # @return [Google::Cloud::Bigtable::Table]
625
661
  #
626
- def self.from_grpc grpc, service, view: nil
662
+ def self.from_grpc grpc, service, view:
627
663
  new grpc, service, view: view
628
664
  end
629
665
 
@@ -653,20 +689,24 @@ module Google
653
689
 
654
690
  FIELDS_BY_VIEW = {
655
691
  SCHEMA_VIEW: ["granularity", "column_families"],
692
+ ENCRYPTION_VIEW: ["cluster_states"],
656
693
  REPLICATION_VIEW: ["cluster_states"],
657
694
  FULL: ["granularity", "column_families", "cluster_states"]
658
695
  }.freeze
659
696
 
660
697
  # @private
661
698
  #
662
- # Checks and reloads table with expected view and sets fields.
663
- # @param view [Symbol] Expected view type.
699
+ # Checks and reloads table with expected view. Performs additive updates to fields specified by the given view.
700
+ # @param view [Symbol] The view type to load. If already loaded, no load is performed.
701
+ # @param skip_if [Symbol] Additional satisfying view types. If already loaded, no load is performed.
664
702
  #
665
- def check_view_and_load view
703
+ def check_view_and_load view, skip_if: nil
666
704
  ensure_service!
667
- @loaded_views ||= Set.new [@view]
668
705
 
669
- return if @loaded_views.include?(view) || @loaded_views.include?(:FULL)
706
+ skip = Set.new skip_if
707
+ skip << view
708
+ skip << :FULL
709
+ return if (@loaded_views & skip).any?
670
710
 
671
711
  grpc = service.get_table instance_id, table_id, view: view
672
712
  @loaded_views << view
@@ -674,6 +714,9 @@ module Google
674
714
  FIELDS_BY_VIEW[view].each do |field|
675
715
  case grpc[field]
676
716
  when Google::Protobuf::Map
717
+ # Special handling for column_families:
718
+ # Replace contents of existing Map since setting the new Map won't work.
719
+ # See https://github.com/protocolbuffers/protobuf/issues/4969
677
720
  @grpc[field].clear
678
721
  grpc[field].each { |k, v| @grpc[field][k] = v }
679
722
  else