Skip to content

Commit d88547c

Browse files
feat: add more context to row merging errors (#281)
* chore: add more context to row merging errors * reformat * add last 5 keys to exception
1 parent 1571dd9 commit d88547c

File tree

2 files changed

+113
-2
lines changed

2 files changed

+113
-2
lines changed

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/readrows/StateMachine.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import com.google.bigtable.v2.ReadRowsResponse.CellChunk;
1919
import com.google.cloud.bigtable.data.v2.internal.ByteStringComparator;
2020
import com.google.cloud.bigtable.data.v2.models.RowAdapter.RowBuilder;
21+
import com.google.common.base.Joiner;
2122
import com.google.common.base.Preconditions;
23+
import com.google.common.collect.EvictingQueue;
2224
import com.google.protobuf.ByteString;
2325
import java.util.List;
2426

@@ -77,6 +79,14 @@ final class StateMachine<RowT> {
7779
private State currentState;
7880
private ByteString lastCompleteRowKey;
7981

82+
// debug stats
83+
private int numScannedNotifications = 0;
84+
private int numRowsCommitted = 0;
85+
private int numChunksProcessed = 0;
86+
private int numCellsInRow = 0;
87+
private int numCellsInLastRow = 0;
88+
private EvictingQueue<ByteString> lastSeenKeys = EvictingQueue.create(5);
89+
8090
// Track current cell attributes: protocol omits them when they are repeated
8191
private ByteString rowKey;
8292
private String familyName;
@@ -120,6 +130,7 @@ final class StateMachine<RowT> {
120130
*/
121131
void handleLastScannedRow(ByteString key) {
122132
try {
133+
numScannedNotifications++;
123134
currentState = currentState.handleLastScannedRow(key);
124135
} catch (RuntimeException e) {
125136
currentState = null;
@@ -148,6 +159,7 @@ void handleLastScannedRow(ByteString key) {
148159
*/
149160
void handleChunk(CellChunk chunk) {
150161
try {
162+
numChunksProcessed++;
151163
currentState = currentState.handleChunk(chunk);
152164
} catch (RuntimeException e) {
153165
currentState = null;
@@ -191,6 +203,7 @@ private void reset() {
191203
expectedCellSize = 0;
192204
remainingCellBytes = 0;
193205
completeRow = null;
206+
numCellsInRow = 0;
194207

195208
adapter.reset();
196209
}
@@ -326,6 +339,7 @@ State handleChunk(CellChunk chunk) {
326339
return AWAITING_CELL_VALUE;
327340
}
328341
adapter.finishCell();
342+
numCellsInRow++;
329343

330344
if (!chunk.getCommitRow()) {
331345
return AWAITING_NEW_CELL;
@@ -374,6 +388,7 @@ State handleChunk(CellChunk chunk) {
374388
return AWAITING_CELL_VALUE;
375389
}
376390
adapter.finishCell();
391+
numCellsInRow++;
377392

378393
if (!chunk.getCommitRow()) {
379394
return AWAITING_NEW_CELL;
@@ -416,12 +431,31 @@ private State handleCommit() {
416431
validate(remainingCellBytes == 0, "Can't commit with remaining bytes");
417432
completeRow = adapter.finishRow();
418433
lastCompleteRowKey = rowKey;
434+
435+
lastSeenKeys.add(rowKey);
436+
numRowsCommitted++;
437+
numCellsInLastRow = numCellsInRow;
419438
return AWAITING_ROW_CONSUME;
420439
}
421440

422-
private static void validate(boolean condition, String message) {
441+
private void validate(boolean condition, String message) {
423442
if (!condition) {
424-
throw new InvalidInputException(message);
443+
throw new InvalidInputException(
444+
message
445+
+ ". numScannedNotifications: "
446+
+ numScannedNotifications
447+
+ ", numRowsCommitted: "
448+
+ numRowsCommitted
449+
+ ", numChunksProcessed: "
450+
+ numChunksProcessed
451+
+ ", numCellsInRow: "
452+
+ numCellsInRow
453+
+ ", numCellsInLastRow: "
454+
+ numCellsInLastRow
455+
+ ", rowKey: "
456+
+ rowKey
457+
+ ", last5Keys: "
458+
+ Joiner.on(",").join(lastSeenKeys));
425459
}
426460
}
427461

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigtable.data.v2.stub.readrows;
17+
18+
import static com.google.common.truth.Truth.assertThat;
19+
20+
import com.google.bigtable.v2.ReadRowsResponse;
21+
import com.google.cloud.bigtable.data.v2.models.DefaultRowAdapter;
22+
import com.google.cloud.bigtable.data.v2.models.Row;
23+
import com.google.protobuf.ByteString;
24+
import com.google.protobuf.BytesValue;
25+
import com.google.protobuf.StringValue;
26+
import org.junit.Before;
27+
import org.junit.Test;
28+
import org.junit.runner.RunWith;
29+
import org.junit.runners.JUnit4;
30+
31+
@RunWith(JUnit4.class)
32+
public class StateMachineTest {
33+
StateMachine<Row> stateMachine;
34+
35+
@Before
36+
public void setUp() throws Exception {
37+
stateMachine = new StateMachine<>(new DefaultRowAdapter().createRowBuilder());
38+
}
39+
40+
@Test
41+
public void testErrorHandlingStats() {
42+
StateMachine.InvalidInputException actualError = null;
43+
44+
ReadRowsResponse.CellChunk chunk =
45+
ReadRowsResponse.CellChunk.newBuilder()
46+
.setRowKey(ByteString.copyFromUtf8("my-key1"))
47+
.setFamilyName(StringValue.newBuilder().setValue("my-family"))
48+
.setQualifier(BytesValue.newBuilder().setValue(ByteString.copyFromUtf8("q")))
49+
.setTimestampMicros(1_000)
50+
.setValue(ByteString.copyFromUtf8("my-value"))
51+
.setCommitRow(true)
52+
.build();
53+
try {
54+
stateMachine.handleChunk(chunk);
55+
stateMachine.consumeRow();
56+
57+
stateMachine.handleChunk(
58+
chunk.toBuilder().setRowKey(ByteString.copyFromUtf8("my-key2")).build());
59+
stateMachine.consumeRow();
60+
61+
stateMachine.handleChunk(
62+
chunk
63+
.toBuilder()
64+
.setRowKey(ByteString.copyFromUtf8("my-key3"))
65+
.setValueSize(123) // invalid value size
66+
.build());
67+
} catch (StateMachine.InvalidInputException e) {
68+
actualError = e;
69+
}
70+
71+
assertThat(actualError).hasMessageThat().containsMatch("last5Keys: .*my-key1.*my-key2");
72+
assertThat(actualError).hasMessageThat().contains("numScannedNotifications: 0");
73+
assertThat(actualError).hasMessageThat().contains("numChunksProcessed: 3");
74+
assertThat(actualError).hasMessageThat().contains("numCellsInRow: 0");
75+
assertThat(actualError).hasMessageThat().contains("numCellsInLastRow: 1");
76+
}
77+
}

0 commit comments

Comments
 (0)