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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -0
- data/CONTRIBUTING.md +4 -5
- data/LOGGING.md +1 -1
- data/OVERVIEW.md +5 -5
- data/lib/google/cloud/bigtable/app_profile/list.rb +2 -2
- data/lib/google/cloud/bigtable/backup.rb +22 -1
- data/lib/google/cloud/bigtable/backup/list.rb +2 -2
- data/lib/google/cloud/bigtable/chunk_processor.rb +4 -3
- data/lib/google/cloud/bigtable/cluster.rb +22 -2
- data/lib/google/cloud/bigtable/cluster/list.rb +2 -2
- data/lib/google/cloud/bigtable/column_family.rb +4 -0
- data/lib/google/cloud/bigtable/column_family_map.rb +2 -0
- data/lib/google/cloud/bigtable/convert.rb +12 -0
- data/lib/google/cloud/bigtable/encryption_info.rb +118 -0
- data/lib/google/cloud/bigtable/gc_rule.rb +2 -0
- data/lib/google/cloud/bigtable/instance.rb +3 -2
- data/lib/google/cloud/bigtable/instance/cluster_map.rb +61 -21
- data/lib/google/cloud/bigtable/instance/list.rb +2 -2
- data/lib/google/cloud/bigtable/mutation_entry.rb +12 -9
- data/lib/google/cloud/bigtable/mutation_operations.rb +6 -5
- data/lib/google/cloud/bigtable/policy.rb +2 -1
- data/lib/google/cloud/bigtable/project.rb +4 -3
- data/lib/google/cloud/bigtable/routing_policy.rb +3 -1
- data/lib/google/cloud/bigtable/row.rb +5 -1
- data/lib/google/cloud/bigtable/row_filter/simple_filter.rb +6 -1
- data/lib/google/cloud/bigtable/rows_reader.rb +6 -6
- data/lib/google/cloud/bigtable/status.rb +4 -1
- data/lib/google/cloud/bigtable/table.rb +62 -19
- data/lib/google/cloud/bigtable/table/cluster_state.rb +43 -9
- data/lib/google/cloud/bigtable/table/list.rb +2 -2
- data/lib/google/cloud/bigtable/table/restore_job.rb +1 -1
- data/lib/google/cloud/bigtable/value_range.rb +24 -6
- data/lib/google/cloud/bigtable/version.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87e7128a687d06d1e5dc3e74f2b529ab43b3c9ea28717d738c433ea86fb297ad
|
4
|
+
data.tar.gz: a04ca13f900445078568a3a1cf64ef84d57686a494f3a652cc6ec461af41712e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc329bd4e9f0d1ed5c5376c40da2924a41f83681df93803aafcadd9ed846bdd2119b913661054b95cfc70cbbfada891b58563b3146396acd872ae2fa79cbff2b
|
7
|
+
data.tar.gz: af466ef435035adae503c5368b0e7c899e84424151044d49efb51f2920e121854ca8af51708c9fce7181e1116410408c635a040442b8b004df45d76b5184c721
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,47 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 2.5.0 / 2021-04-20
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
* Add CMEK support
|
8
|
+
* Add kms_key to ClusterMap#add
|
9
|
+
* Add Cluster#kms_key
|
10
|
+
* Add EncryptionInfo
|
11
|
+
* Add ClusterState#encryption_infos
|
12
|
+
* Add ENCRYPTION_VIEW support to Table
|
13
|
+
* Update docs in Table::ClusterState
|
14
|
+
* Add Backup#encryption_info
|
15
|
+
|
16
|
+
### 2.4.1 / 2021-04-01
|
17
|
+
|
18
|
+
#### Bug Fixes
|
19
|
+
|
20
|
+
* Update internal management of view states in Table
|
21
|
+
|
22
|
+
### 2.4.0 / 2021-03-10
|
23
|
+
|
24
|
+
#### Features
|
25
|
+
|
26
|
+
* Drop support for Ruby 2.4 and add support for Ruby 3.0
|
27
|
+
|
28
|
+
#### Documentation
|
29
|
+
|
30
|
+
* Add link to Garbage collection docs
|
31
|
+
|
32
|
+
### 2.3.0 / 2021-02-04
|
33
|
+
|
34
|
+
#### Features
|
35
|
+
|
36
|
+
* Add integer support to RowFilter#value and ValueRange
|
37
|
+
* Encode Integer arguments as 64-bit signed big-endian integer data
|
38
|
+
|
39
|
+
### 2.2.1 / 2021-01-13
|
40
|
+
|
41
|
+
#### Documentation
|
42
|
+
|
43
|
+
* Use consistent column family and field names in samples
|
44
|
+
|
3
45
|
### 2.2.0 / 2020-11-11
|
4
46
|
|
5
47
|
#### Features
|
data/CONTRIBUTING.md
CHANGED
@@ -24,7 +24,7 @@ be able to accept your pull requests.
|
|
24
24
|
In order to use the google-cloud-bigtable console and run the project's tests,
|
25
25
|
there is a small amount of setup:
|
26
26
|
|
27
|
-
1. Install Ruby. google-cloud-bigtable requires Ruby 2.
|
27
|
+
1. Install Ruby. google-cloud-bigtable requires Ruby 2.5+. You may choose to
|
28
28
|
manage your Ruby and gem installations with [RVM](https://rvm.io/),
|
29
29
|
[rbenv](https://github.com/rbenv/rbenv), or
|
30
30
|
[chruby](https://github.com/postmodern/chruby).
|
@@ -45,7 +45,7 @@ there is a small amount of setup:
|
|
45
45
|
|
46
46
|
```sh
|
47
47
|
$ cd google-cloud-bigtable/
|
48
|
-
$ bundle
|
48
|
+
$ bundle install
|
49
49
|
```
|
50
50
|
|
51
51
|
## Console
|
@@ -119,15 +119,14 @@ If you alter an example's title, you may encounter breaking tests.
|
|
119
119
|
### Bigtable Acceptance Tests
|
120
120
|
|
121
121
|
The Bigtable acceptance tests interact with the live service API. Follow the
|
122
|
-
instructions in the {file:AUTHENTICATION.md Authentication
|
122
|
+
instructions in the {file:AUTHENTICATION.md Authentication Guide} for enabling
|
123
123
|
the Bigtable API. Occasionally, some API features may not yet be generally
|
124
124
|
available, making it difficult for some contributors to successfully run the
|
125
125
|
entire acceptance test suite. However, please ensure that you do successfully
|
126
126
|
run acceptance tests for any code areas covered by your pull request.
|
127
127
|
|
128
128
|
To run the acceptance tests, first create and configure a project in the Google
|
129
|
-
Developers Console, as described in the {file:AUTHENTICATION.md Authentication
|
130
|
-
guide}. Be sure to download the JSON KEY file. Make note of the PROJECT_ID and
|
129
|
+
Developers Console, as described in the {file:AUTHENTICATION.md Authentication Guide}. Be sure to download the JSON KEY file. Make note of the PROJECT_ID and
|
131
130
|
the KEYFILE location on your system.
|
132
131
|
|
133
132
|
Before you can run the Bigtable acceptance tests, you must first create indexes
|
data/LOGGING.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
To enable logging for this library, set the logger for the underlying
|
4
4
|
[gRPC](https://github.com/grpc/grpc/tree/master/src/ruby) library. The logger
|
5
5
|
that you set may be a Ruby stdlib
|
6
|
-
[`Logger`](https://ruby-doc.org/stdlib
|
6
|
+
[`Logger`](https://ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html) as
|
7
7
|
shown below, or a
|
8
8
|
[`Google::Cloud::Logging::Logger`](https://googleapis.dev/ruby/google-cloud-logging/latest)
|
9
9
|
that will write logs to [Stackdriver
|
data/OVERVIEW.md
CHANGED
@@ -194,8 +194,8 @@ table = bigtable.table("my-instance", "my-table")
|
|
194
194
|
|
195
195
|
entry = table.new_mutation_entry("user-1")
|
196
196
|
entry.set_cell(
|
197
|
-
"
|
198
|
-
"
|
197
|
+
"cf1",
|
198
|
+
"field1",
|
199
199
|
"XYZ",
|
200
200
|
timestamp: (Time.now.to_f * 1000000).round(-3) # microseconds
|
201
201
|
).delete_cells("cf2", "field02")
|
@@ -245,8 +245,8 @@ table = bigtable.table("my-instance", "my-table")
|
|
245
245
|
predicate_filter = Google::Cloud::Bigtable::RowFilter.key("user-10")
|
246
246
|
on_match_mutations = Google::Cloud::Bigtable::MutationEntry.new
|
247
247
|
on_match_mutations.set_cell(
|
248
|
-
"
|
249
|
-
"
|
248
|
+
"cf1",
|
249
|
+
"field1",
|
250
250
|
"XYZ",
|
251
251
|
timestamp: (Time.now.to_f * 1000000).round(-3) # microseconds
|
252
252
|
).delete_cells("cf2", "field02")
|
@@ -396,5 +396,5 @@ instance.delete
|
|
396
396
|
## Additional information
|
397
397
|
|
398
398
|
Google Bigtable can be configured to use an emulator or to enable gRPC's
|
399
|
-
logging. To learn more, see the {file:EMULATOR.md Emulator guide} and
|
399
|
+
logging. To learn more, see the {file:EMULATOR.md Emulator guide}} and
|
400
400
|
{file:LOGGING.md Logging guide}.
|
@@ -122,12 +122,12 @@ module Google
|
|
122
122
|
# app_profile.name
|
123
123
|
# end
|
124
124
|
#
|
125
|
-
def all
|
125
|
+
def all &block
|
126
126
|
return enum_for :all unless block_given?
|
127
127
|
|
128
128
|
results = self
|
129
129
|
loop do
|
130
|
-
results.each
|
130
|
+
results.each(&block)
|
131
131
|
break unless next?
|
132
132
|
grpc.next_page
|
133
133
|
results = self.class.from_grpc grpc, service
|
@@ -18,10 +18,10 @@
|
|
18
18
|
require "google/cloud/bigtable/backup/job"
|
19
19
|
require "google/cloud/bigtable/backup/list"
|
20
20
|
require "google/cloud/bigtable/convert"
|
21
|
+
require "google/cloud/bigtable/encryption_info"
|
21
22
|
require "google/cloud/bigtable/policy"
|
22
23
|
require "google/cloud/bigtable/table/restore_job"
|
23
24
|
|
24
|
-
|
25
25
|
module Google
|
26
26
|
module Cloud
|
27
27
|
module Bigtable
|
@@ -207,6 +207,27 @@ module Google
|
|
207
207
|
state == :READY
|
208
208
|
end
|
209
209
|
|
210
|
+
##
|
211
|
+
# The encryption information for the backup. See also {Instance::ClusterMap#add}.
|
212
|
+
#
|
213
|
+
# @return [Google::Cloud::Bigtable::EncryptionInfo] The encryption information for the backup.
|
214
|
+
#
|
215
|
+
# @example
|
216
|
+
# require "google/cloud/bigtable"
|
217
|
+
#
|
218
|
+
# bigtable = Google::Cloud::Bigtable.new
|
219
|
+
# instance = bigtable.instance("my-instance")
|
220
|
+
# cluster = instance.cluster("my-cluster")
|
221
|
+
#
|
222
|
+
# backup = cluster.backup("my-backup")
|
223
|
+
#
|
224
|
+
# encryption_info = backup.encryption_info
|
225
|
+
# encryption_info.encryption_type #=> :GOOGLE_DEFAULT_ENCRYPTION
|
226
|
+
#
|
227
|
+
def encryption_info
|
228
|
+
EncryptionInfo.from_grpc @grpc.encryption_info
|
229
|
+
end
|
230
|
+
|
210
231
|
##
|
211
232
|
# Gets the [Cloud IAM](https://cloud.google.com/iam/) access control
|
212
233
|
# policy for the backup.
|
@@ -124,12 +124,12 @@ module Google
|
|
124
124
|
# backup.backup_id
|
125
125
|
# end
|
126
126
|
#
|
127
|
-
def all
|
127
|
+
def all &block
|
128
128
|
return enum_for :all unless block_given?
|
129
129
|
|
130
130
|
results = self
|
131
131
|
loop do
|
132
|
-
results.each
|
132
|
+
results.each(&block)
|
133
133
|
break unless next?
|
134
134
|
grpc.next_page
|
135
135
|
results = self.class.from_grpc grpc, service
|
@@ -57,11 +57,12 @@ module Google
|
|
57
57
|
|
58
58
|
raise_if chunk.value_size.positive?, "Commit rows cannot have a non-zero value_size." if chunk.commit_row
|
59
59
|
|
60
|
-
|
60
|
+
case state
|
61
|
+
when NEW_ROW
|
61
62
|
process_new_row
|
62
|
-
|
63
|
+
when CELL_IN_PROGRESS
|
63
64
|
process_cell_in_progress
|
64
|
-
|
65
|
+
when ROW_IN_PROGRESS
|
65
66
|
process_row_in_progress
|
66
67
|
end
|
67
68
|
end
|
@@ -168,8 +168,10 @@ module Google
|
|
168
168
|
##
|
169
169
|
# The type of storage used by this cluster to serve its
|
170
170
|
# parent instance's tables, unless explicitly overridden.
|
171
|
-
# Valid values are
|
172
|
-
#
|
171
|
+
# Valid values are:
|
172
|
+
#
|
173
|
+
# * `:SSD` - Flash (SSD) storage should be used.
|
174
|
+
# * `:HDD` - Magnetic drive (HDD) storage should be used.
|
173
175
|
#
|
174
176
|
# @return [Symbol]
|
175
177
|
#
|
@@ -197,6 +199,24 @@ module Google
|
|
197
199
|
@grpc.location
|
198
200
|
end
|
199
201
|
|
202
|
+
##
|
203
|
+
# The full name of the Cloud KMS encryption key for the cluster, if it is CMEK-protected, in the format
|
204
|
+
# `projects/{key_project_id}/locations/{location}/keyRings/{ring_name}/cryptoKeys/{key_name}`.
|
205
|
+
#
|
206
|
+
# The requirements for this key are:
|
207
|
+
#
|
208
|
+
# 1. The Cloud Bigtable service account associated with the project that contains this cluster must be granted
|
209
|
+
# the `cloudkms.cryptoKeyEncrypterDecrypter` role on the CMEK key.
|
210
|
+
# 2. Only regional keys can be used and the region of the CMEK key must match the region of the cluster.
|
211
|
+
# 3. All clusters within an instance must use the same CMEK key.
|
212
|
+
#
|
213
|
+
# @return [String, nil] The full name of the Cloud KMS encryption key, or `nil` if the cluster is not
|
214
|
+
# CMEK-protected.
|
215
|
+
#
|
216
|
+
def kms_key
|
217
|
+
@grpc.encryption_config&.kms_key_name
|
218
|
+
end
|
219
|
+
|
200
220
|
##
|
201
221
|
# Creates a new Cloud Bigtable Backup.
|
202
222
|
#
|
@@ -129,12 +129,12 @@ module Google
|
|
129
129
|
# puts cluster.cluster_id
|
130
130
|
# end
|
131
131
|
#
|
132
|
-
def all
|
132
|
+
def all &block
|
133
133
|
return enum_for :all unless block_given?
|
134
134
|
|
135
135
|
results = self
|
136
136
|
loop do
|
137
|
-
results.each
|
137
|
+
results.each(&block)
|
138
138
|
break unless results.next?
|
139
139
|
results = results.next
|
140
140
|
end
|
@@ -45,7 +45,11 @@ module Google
|
|
45
45
|
##
|
46
46
|
# The garbage collection rule to be used for the column family.
|
47
47
|
# Optional. The service default value will be used when not specified.
|
48
|
+
#
|
49
|
+
# @see https://cloud.google.com/bigtable/docs/garbage-collection Garbage collection
|
50
|
+
#
|
48
51
|
# @return [Google::Cloud::Bigtable::GcRule, nil]
|
52
|
+
#
|
49
53
|
attr_accessor :gc_rule
|
50
54
|
|
51
55
|
# @private
|
@@ -222,6 +222,8 @@ module Google
|
|
222
222
|
##
|
223
223
|
# Updates an existing column family in the table.
|
224
224
|
#
|
225
|
+
# @see https://cloud.google.com/bigtable/docs/garbage-collection Garbage collection
|
226
|
+
#
|
225
227
|
# @param name [String] Column family name.
|
226
228
|
# @param gc_rule [Google::Cloud::Bigtable::GcRule] The new garbage
|
227
229
|
# collection rule to be used for the column family. Optional. The
|
@@ -77,6 +77,18 @@ module Google
|
|
77
77
|
|
78
78
|
Google::Protobuf::Timestamp.new seconds: time.to_i, nanos: time.nsec
|
79
79
|
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Converts an Integer to 64-bit signed big-endian integer data.
|
83
|
+
# Returns a string argument unchanged.
|
84
|
+
#
|
85
|
+
# @param value [String, Integer]
|
86
|
+
# @return [String]
|
87
|
+
#
|
88
|
+
def integer_to_signed_be_64 value
|
89
|
+
return [value].pack "q>" if value.is_a? Integer
|
90
|
+
value
|
91
|
+
end
|
80
92
|
end
|
81
93
|
end
|
82
94
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2021 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/status"
|
19
|
+
|
20
|
+
module Google
|
21
|
+
module Cloud
|
22
|
+
module Bigtable
|
23
|
+
##
|
24
|
+
# # EncryptionInfo
|
25
|
+
#
|
26
|
+
# Encryption information for a given resource.
|
27
|
+
#
|
28
|
+
# See {Backup#encryption_info} and {Table::ClusterState#encryption_infos}.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# require "google/cloud/bigtable"
|
32
|
+
#
|
33
|
+
# bigtable = Google::Cloud::Bigtable.new
|
34
|
+
# instance = bigtable.instance("my-instance")
|
35
|
+
# cluster = instance.cluster("my-cluster")
|
36
|
+
#
|
37
|
+
# backup = cluster.backup("my-backup")
|
38
|
+
#
|
39
|
+
# encryption_info = backup.encryption_info
|
40
|
+
# encryption_info.encryption_type #=> :GOOGLE_DEFAULT_ENCRYPTION
|
41
|
+
#
|
42
|
+
# @example Retrieve a table with cluster states containing encryption info.
|
43
|
+
# require "google/cloud/bigtable"
|
44
|
+
#
|
45
|
+
# bigtable = Google::Cloud::Bigtable.new
|
46
|
+
#
|
47
|
+
# table = bigtable.table("my-instance", "my-table", view: :ENCRYPTION_VIEW, perform_lookup: true)
|
48
|
+
#
|
49
|
+
# table.cluster_states.each do |cs|
|
50
|
+
# puts cs.cluster_name
|
51
|
+
# puts cs.encryption_infos.first.encryption_type
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
class EncryptionInfo
|
55
|
+
# @private
|
56
|
+
#
|
57
|
+
# Creates a new EncryptionInfo instance.
|
58
|
+
def initialize grpc
|
59
|
+
@grpc = grpc
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# The type of encryption used to protect the resource. Possible values:
|
64
|
+
#
|
65
|
+
# * `ENCRYPTION_TYPE_UNSPECIFIED` - Encryption type was not specified, though data at rest remains encrypted.
|
66
|
+
# * `GOOGLE_DEFAULT_ENCRYPTION` - The data backing the resource is encrypted at rest with a key that is
|
67
|
+
# fully managed by Google. No key version or status will be populated. This is the default state.
|
68
|
+
# * `CUSTOMER_MANAGED_ENCRYPTION` - The data backing the resource is encrypted at rest with a key that is
|
69
|
+
# managed by the customer. The in-use version of the key and its status are populated for CMEK-protected
|
70
|
+
# tables. CMEK-protected backups are pinned to the key version that was in use at the time the backup was
|
71
|
+
# taken. This key version is populated but its status is not tracked and is reported as `UNKNOWN`.
|
72
|
+
#
|
73
|
+
# See also {#encryption_status}, {#kms_key_version} and {Instance::ClusterMap#add}.
|
74
|
+
#
|
75
|
+
# @return [Symbol] The encryption type code as an uppercase symbol.
|
76
|
+
#
|
77
|
+
def encryption_type
|
78
|
+
@grpc.encryption_type
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# The status of encrypt/decrypt calls on underlying data for the resource. Regardless of status, the existing
|
83
|
+
# data is always encrypted at rest.
|
84
|
+
#
|
85
|
+
# See also {#encryption_type}, {#kms_key_version} and {Instance::ClusterMap#add}.
|
86
|
+
#
|
87
|
+
# @return [Google::Cloud::Bigtable::Status, nil] The encryption status object, or `nil` if not present.
|
88
|
+
#
|
89
|
+
def encryption_status
|
90
|
+
status_grpc = @grpc.encryption_status
|
91
|
+
Status.from_grpc status_grpc if status_grpc
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# The version of the Cloud KMS key specified in the parent cluster that is in use for the data underlying the
|
96
|
+
# table.
|
97
|
+
#
|
98
|
+
# See also {#encryption_type}, {#encryption_status} and {Instance::ClusterMap#add}.
|
99
|
+
#
|
100
|
+
# @return [String, nil] The Cloud KMS key version, or `nil` if not present.
|
101
|
+
#
|
102
|
+
def kms_key_version
|
103
|
+
@grpc.kms_key_version unless @grpc.kms_key_version.empty?
|
104
|
+
end
|
105
|
+
|
106
|
+
# @private
|
107
|
+
#
|
108
|
+
# Creates a new EncryptionInfo instance from a Google::Cloud::Bigtable::Admin::V2::EncryptionInfo.
|
109
|
+
#
|
110
|
+
# @param grpc [Google::Cloud::Bigtable::Admin::V2::EncryptionInfo]
|
111
|
+
# @return [Google::Cloud::Bigtable::EncryptionInfo]
|
112
|
+
def self.from_grpc grpc
|
113
|
+
new grpc
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -28,6 +28,8 @@ module Google
|
|
28
28
|
# so it is possible for reads to return a cell even if it matches the
|
29
29
|
# active GC expression for its column family.
|
30
30
|
#
|
31
|
+
# @see https://cloud.google.com/bigtable/docs/garbage-collection Garbage collection
|
32
|
+
#
|
31
33
|
# GC Rule types:
|
32
34
|
# * `max_num_versions` - A garbage-collection rule that explicitly
|
33
35
|
# states the maximum number of cells to keep for all columns in a
|