Skip to content

Commit 3fa1fa0

Browse files
igorbernstein2pongad
authored andcommitted
Bigtable: limit mutation sizes in the client to avoid overloading the server (googleapis#3695)
* Bigtable: limit mutation sizes in the client to avoid overloading the server * address feedback
1 parent f977c8a commit 3fa1fa0

File tree

2 files changed

+66
-13
lines changed
  • google-cloud-clients/google-cloud-bigtable/src

2 files changed

+66
-13
lines changed

google-cloud-clients/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Mutation.java

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.google.cloud.bigtable.data.v2.models;
1717

18+
import com.google.api.core.InternalApi;
1819
import com.google.bigtable.v2.Mutation.DeleteFromColumn;
1920
import com.google.bigtable.v2.Mutation.DeleteFromFamily;
2021
import com.google.bigtable.v2.Mutation.DeleteFromRow;
@@ -36,11 +37,19 @@
3637
* encapsulate a list of mutations that will to be applied to a single row.
3738
*/
3839
public final class Mutation implements MutationApi<Mutation>, Serializable {
39-
private static final long serialVersionUID = 5893216644683374339L;
40+
private static final long serialVersionUID = 5893216644683374340L;
41+
42+
@InternalApi("Visible for testing")
43+
static final int MAX_MUTATIONS = 100_000;
44+
@InternalApi("Visible for testing")
45+
static final int MAX_BYTE_SIZE = 200 * 1024 * 1024;
4046

4147
private transient ImmutableList.Builder<com.google.bigtable.v2.Mutation> mutations =
4248
ImmutableList.builder();
4349

50+
private int numMutations;
51+
private long byteSize;
52+
4453
public static Mutation create() {
4554
return new Mutation();
4655
}
@@ -95,7 +104,7 @@ public Mutation setCell(
95104
Preconditions.checkNotNull(value, "value can't be null.");
96105
Preconditions.checkArgument(timestamp != -1, "Serverside timestamps are not supported");
97106

98-
com.google.bigtable.v2.Mutation mutation =
107+
addMutation(
99108
com.google.bigtable.v2.Mutation.newBuilder()
100109
.setSetCell(
101110
SetCell.newBuilder()
@@ -104,9 +113,8 @@ public Mutation setCell(
104113
.setTimestampMicros(timestamp)
105114
.setValue(value)
106115
.build())
107-
.build();
116+
.build());
108117

109-
mutations.add(mutation);
110118
return this;
111119
}
112120

@@ -161,9 +169,8 @@ public Mutation deleteCells(
161169
throw new IllegalArgumentException("Unknown end bound: " + timestampRange.getEndBound());
162170
}
163171

164-
com.google.bigtable.v2.Mutation mutation =
165-
com.google.bigtable.v2.Mutation.newBuilder().setDeleteFromColumn(builder.build()).build();
166-
mutations.add(mutation);
172+
addMutation(
173+
com.google.bigtable.v2.Mutation.newBuilder().setDeleteFromColumn(builder.build()).build());
167174

168175
return this;
169176
}
@@ -172,26 +179,36 @@ public Mutation deleteCells(
172179
public Mutation deleteFamily(@Nonnull String familyName) {
173180
Validations.validateFamily(familyName);
174181

175-
com.google.bigtable.v2.Mutation mutation =
182+
addMutation(
176183
com.google.bigtable.v2.Mutation.newBuilder()
177184
.setDeleteFromFamily(DeleteFromFamily.newBuilder().setFamilyName(familyName).build())
178-
.build();
179-
mutations.add(mutation);
185+
.build());
180186

181187
return this;
182188
}
183189

184190
@Override
185191
public Mutation deleteRow() {
186-
com.google.bigtable.v2.Mutation mutation =
192+
addMutation(
187193
com.google.bigtable.v2.Mutation.newBuilder()
188194
.setDeleteFromRow(DeleteFromRow.getDefaultInstance())
189-
.build();
190-
mutations.add(mutation);
195+
.build());
191196

192197
return this;
193198
}
194199

200+
private void addMutation(com.google.bigtable.v2.Mutation mutation) {
201+
Preconditions.checkState(numMutations + 1 <= MAX_MUTATIONS,
202+
"Too many mutations per row");
203+
Preconditions.checkState(byteSize + mutation.getSerializedSize() <= MAX_BYTE_SIZE,
204+
"Byte size of mutations is too large");
205+
206+
numMutations++;
207+
byteSize += mutation.getSerializedSize();
208+
209+
mutations.add(mutation);
210+
}
211+
195212
private static ByteString wrapByteString(String str) {
196213
if (str == null) {
197214
return null;

google-cloud-clients/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/MutationTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
@RunWith(JUnit4.class)
3737
public class MutationTest {
38+
3839
private Mutation mutation;
3940

4041
@Before
@@ -176,4 +177,39 @@ public void serializationTest() throws IOException, ClassNotFoundException {
176177
Mutation actual = (Mutation) ois.readObject();
177178
assertThat(actual.getMutations()).isEqualTo(expected.getMutations());
178179
}
180+
181+
@Test
182+
public void tooManyMutationsTest() {
183+
Mutation mutation = Mutation.create();
184+
185+
for (int i = 0; i < Mutation.MAX_MUTATIONS; i++) {
186+
mutation.setCell("f", "", "");
187+
}
188+
189+
Exception actualError = null;
190+
191+
try {
192+
mutation.setCell("f", "", "");
193+
} catch (Exception e) {
194+
actualError = e;
195+
}
196+
197+
assertThat(actualError).isInstanceOf(IllegalStateException.class);
198+
}
199+
200+
@Test
201+
public void tooLargeRequest() {
202+
Mutation mutation = Mutation.create();
203+
204+
Exception actualError = null;
205+
206+
try {
207+
mutation.setCell("f", ByteString.copyFromUtf8(""),
208+
ByteString.copyFrom(new byte[Mutation.MAX_BYTE_SIZE]));
209+
} catch (Exception e) {
210+
actualError = e;
211+
}
212+
213+
assertThat(actualError).isInstanceOf(IllegalStateException.class);
214+
}
179215
}

0 commit comments

Comments
 (0)