BulkRead { public synchronized ApiFuture<FlatRow> add(Query query) { Preconditions.checkNotNull(query); ReadRowsRequest request = query.toProto(requestContext); Preconditions.checkArgument(request.getRows().getRowKeysCount() == 1); ByteString rowKey = request.getRows().getRowKeysList().get(0); Preconditions.checkArgument(!rowKey.equals(ByteString.EMPTY)); final RowFilter filter = request.getFilter(); Batch batch = batches.get(filter); if (batch == null) { batch = new Batch(filter); batches.put(filter, batch); } return batch.addKey(rowKey); } @InternalApi("For internal usage only")  BulkRead(
      IBigtableDataClient client,
      BigtableTableName tableName,
      int batchSizes,
      ExecutorService threadPool); synchronized ApiFuture<FlatRow> add(Query query); void flush(); @InternalApi("For internal usage only") int getBatchSizes();  }
@Test public void testMissingResponses() throws Exception { List<ByteString> rowKeys = createRandomKeys(100); final Set<ByteString> missing = new HashSet<>(); List<Future<FlatRow>> futures = addRows( rowKeys, new Answer<ResultScanner<FlatRow>>() { @Override public ResultScanner<FlatRow> answer(InvocationOnMock invocation) throws Throwable { Query request = invocation.getArgument(0, Query.class); List<ByteString> rowKeysList = new ArrayList<>(request.toProto(CONTEXT).getRows().getRowKeysList()); missing.add(rowKeysList.remove((int) Math.random() * rowKeysList.size())); return createMockScanner(rowKeysList.iterator()); } }); for (int i = 0; i < rowKeys.size(); i++) { FlatRow row = futures.get(i).get(10, TimeUnit.MILLISECONDS); if (missing.contains(rowKeys.get(i))) { Assert.assertNull(row); } else { Assert.assertNotNull(row); Assert.assertEquals(rowKeys.get(i), row.getRowKey()); } } }