Skip to content
This repository was archived by the owner on May 8, 2026. It is now read-only.

Commit 8b5188d

Browse files
committed
udpate test
Change-Id: I8bd49e79fb8d9c368a5126e7fa0bb4f8d73cff3c
1 parent c992e24 commit 8b5188d

2 files changed

Lines changed: 81 additions & 58 deletions

File tree

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

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,62 +17,68 @@
1717

1818
import com.google.api.core.InternalApi;
1919
import com.google.cloud.bigtable.data.v2.models.Filters;
20+
import javax.annotation.Nullable;
2021

2122
/**
22-
* A paginator for fetching extremely large rows from Bigtable chunk by chunk to avoid the 256MB gRPC size limit.
23-
* It yields Filters that chunk the row by cell limits and handles limit halving if FAILED_PRECONDITION occurs.
23+
* A paginator for fetching large rows from Bigtable chunk by chunk to avoid the 256MB size limit.
24+
* It yields Filters that chunk the row by cell limits and handles limit halving if
25+
* FAILED_PRECONDITION occurs.
2426
*/
2527
@InternalApi("For internal usage only")
2628
public class LargeRowPaginator {
27-
private int currentLimit;
28-
private int currentOffset;
29-
private boolean hasMore;
29+
private int currentLimit;
30+
private int currentOffset;
31+
private boolean hasMore;
32+
@Nullable private final Filters.Filter baseFilter;
3033

31-
public LargeRowPaginator(int initialLimit) {
32-
this.currentLimit = initialLimit;
33-
this.currentOffset = 0;
34-
this.hasMore = true;
35-
}
34+
public LargeRowPaginator(int initialLimit, @Nullable Filters.Filter filter) {
35+
this.currentLimit = initialLimit;
36+
this.currentOffset = 0;
37+
this.hasMore = true;
38+
this.baseFilter = filter;
39+
}
3640

37-
/**
38-
* Yields the filter required to fetch the next chunk of cells for the large row.
39-
*/
40-
public Filters.Filter getNextFilter() {
41-
Filters.ChainFilter chain = Filters.FILTERS.chain();
42-
if (currentOffset > 0) {
43-
chain.filter(Filters.FILTERS.offset().cellsPerRow(currentOffset));
44-
}
45-
chain.filter(Filters.FILTERS.limit().cellsPerRow(currentLimit));
46-
return chain;
41+
/** Yields the filter required to fetch the next chunk of cells for the large row. */
42+
public Filters.Filter getNextFilter() {
43+
Filters.ChainFilter chain = Filters.FILTERS.chain();
44+
if (baseFilter != null) {
45+
chain.filter(baseFilter);
4746
}
48-
49-
/**
50-
* Advances the internal offset. Call this after a successful Bigtable API call.
51-
* @param cellsReadInLastChunk The number of cells returned in the last chunk.
52-
* @return true if there are potentially more cells to fetch.
53-
*/
54-
public boolean advance(int cellsReadInLastChunk) {
55-
this.currentOffset += cellsReadInLastChunk;
56-
57-
// If we read fewer cells than requested, we've hit the end of the row.
58-
if (cellsReadInLastChunk < currentLimit) {
59-
this.hasMore = false;
60-
}
61-
return this.hasMore;
47+
if (currentOffset > 0) {
48+
chain.filter(Filters.FILTERS.offset().cellsPerRow(currentOffset));
6249
}
50+
chain.filter(Filters.FILTERS.limit().cellsPerRow(currentLimit));
51+
return chain;
52+
}
6353

64-
/**
65-
* Call this if the Bigtable API call fails with a FAILED_PRECONDITION due to size limits.
66-
* It reduces the batch size to fetch a smaller chunk on the next attempt.
67-
*/
68-
public void halveLimit() {
69-
this.currentLimit /= 2;
70-
if (this.currentLimit == 0) {
71-
throw new RuntimeException("Cannot divide limit further. A single cell might be too large.");
72-
}
54+
/**
55+
* Advances the internal offset. Call this after a successful Bigtable API call.
56+
*
57+
* @param cellsReadInLastChunk The number of cells returned in the last chunk.
58+
* @return true if there are potentially more cells to fetch.
59+
*/
60+
public boolean advance(int cellsReadInLastChunk) {
61+
this.currentOffset += cellsReadInLastChunk;
62+
63+
// If we read fewer cells than requested, we've hit the end of the row.
64+
if (cellsReadInLastChunk < currentLimit) {
65+
this.hasMore = false;
7366
}
67+
return this.hasMore;
68+
}
7469

75-
public boolean hasNext() {
76-
return this.hasMore;
70+
/**
71+
* Call this if the Bigtable API call fails with a FAILED_PRECONDITION due to size limits. It
72+
* reduces the batch size to fetch a smaller chunk on the next attempt.
73+
*/
74+
public void halveLimit() {
75+
this.currentLimit /= 2;
76+
if (this.currentLimit == 0) {
77+
throw new RuntimeException("Cannot divide limit further. A single cell might be too large.");
7778
}
79+
}
80+
81+
public boolean hasNext() {
82+
return this.hasMore;
83+
}
7884
}

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/readrows/LargeRowPaginatorTest.java

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static com.google.common.truth.Truth.assertThat;
1919
import static org.junit.Assert.assertThrows;
2020

21+
import com.google.cloud.bigtable.data.v2.models.Filters;
2122
import org.junit.Test;
2223
import org.junit.runner.RunWith;
2324
import org.junit.runners.JUnit4;
@@ -27,15 +28,15 @@ public class LargeRowPaginatorTest {
2728

2829
@Test
2930
public void testPaginatorAdvancesProperly() {
30-
LargeRowPaginator paginator = new LargeRowPaginator(10);
31-
31+
LargeRowPaginator paginator = new LargeRowPaginator(10, null);
32+
3233
assertThat(paginator.hasNext()).isTrue();
33-
34+
3435
// Simulate reading exactly the limit (10 cells). Paginator assumes more data exists.
3536
boolean hasMore = paginator.advance(10);
3637
assertThat(hasMore).isTrue();
3738
assertThat(paginator.hasNext()).isTrue();
38-
39+
3940
// Simulate reading 5 cells (less than limit, indicating end of row)
4041
hasMore = paginator.advance(5);
4142
assertThat(hasMore).isFalse();
@@ -44,26 +45,42 @@ public void testPaginatorAdvancesProperly() {
4445

4546
@Test
4647
public void testPaginatorHalvesLimit() {
47-
LargeRowPaginator paginator = new LargeRowPaginator(10);
48-
48+
LargeRowPaginator paginator = new LargeRowPaginator(10, null);
49+
4950
paginator.halveLimit(); // Internal limit becomes 5
50-
51+
5152
// Simulate reading exactly the new limit (5 cells)
5253
boolean hasMore = paginator.advance(5);
53-
assertThat(hasMore).isTrue();
54-
54+
assertThat(hasMore).isTrue();
55+
5556
paginator.halveLimit(); // Internal limit becomes 2
56-
57+
5758
// Simulate reading 1 cell (less than the new limit of 2)
58-
hasMore = paginator.advance(1);
59+
hasMore = paginator.advance(1);
5960
assertThat(hasMore).isFalse();
6061
}
6162

6263
@Test
6364
public void testPaginatorThrowsOnZeroLimit() {
64-
LargeRowPaginator paginator = new LargeRowPaginator(1);
65-
65+
LargeRowPaginator paginator = new LargeRowPaginator(1, null);
66+
6667
RuntimeException exception = assertThrows(RuntimeException.class, () -> paginator.halveLimit());
6768
assertThat(exception).hasMessageThat().contains("Cannot divide limit further");
6869
}
70+
71+
@Test
72+
public void testPaginatorWithBaseFilter() {
73+
LargeRowPaginator paginator =
74+
new LargeRowPaginator(10, Filters.FILTERS.family().exactMatch("cf"));
75+
76+
Filters.Filter nextFilter = paginator.getNextFilter();
77+
78+
Filters.Filter expectedFilter =
79+
Filters.FILTERS
80+
.chain()
81+
.filter(Filters.FILTERS.family().exactMatch("cf"))
82+
.filter(Filters.FILTERS.limit().cellsPerRow(10));
83+
84+
assertThat(nextFilter.toProto()).isEqualTo(expectedFilter.toProto());
85+
}
6986
}

0 commit comments

Comments
 (0)