Skip to content

Commit bc008db

Browse files
chore(bigtable/bttest): Add read stats to the emulator and a simple test (#7019)
Closes #7017
1 parent c9acf47 commit bc008db

File tree

2 files changed

+123
-11
lines changed

2 files changed

+123
-11
lines changed

bigtable/bigtable_test.go

+81
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,87 @@ func TestReadRowsInvalidRowSet(t *testing.T) {
249249
}
250250
}
251251

252+
func TestReadRowsRequestStats(t *testing.T) {
253+
testEnv, err := NewEmulatedEnv(IntegrationTestConfig{})
254+
if err != nil {
255+
t.Fatalf("NewEmulatedEnv failed: %v", err)
256+
}
257+
conn, err := grpc.Dial(testEnv.server.Addr, grpc.WithInsecure(), grpc.WithBlock(),
258+
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(100<<20), grpc.MaxCallRecvMsgSize(100<<20)),
259+
)
260+
if err != nil {
261+
t.Fatalf("grpc.Dial failed: %v", err)
262+
}
263+
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
264+
defer cancel()
265+
adminClient, err := NewAdminClient(ctx, testEnv.config.Project, testEnv.config.Instance, option.WithGRPCConn(conn))
266+
if err != nil {
267+
t.Fatalf("NewClient failed: %v", err)
268+
}
269+
defer adminClient.Close()
270+
tableConf := &TableConf{
271+
TableID: testEnv.config.Table,
272+
Families: map[string]GCPolicy{
273+
"f": NoGcPolicy(),
274+
},
275+
}
276+
if err := adminClient.CreateTableFromConf(ctx, tableConf); err != nil {
277+
t.Fatalf("CreateTable(%v) failed: %v", testEnv.config.Table, err)
278+
}
279+
280+
client, err := NewClient(ctx, testEnv.config.Project, testEnv.config.Instance, option.WithGRPCConn(conn))
281+
if err != nil {
282+
t.Fatalf("NewClient failed: %v", err)
283+
}
284+
defer client.Close()
285+
table := client.Open(testEnv.config.Table)
286+
287+
m := NewMutation()
288+
m.Set("f", "q", ServerTime, []byte("value"))
289+
290+
if err = table.Apply(ctx, "row1", m); err != nil {
291+
t.Fatalf("Apply failed: %v", err)
292+
}
293+
294+
m = NewMutation()
295+
m.Set("f", "q", ServerTime, []byte("value"))
296+
m.Set("f", "q2", ServerTime, []byte("value2"))
297+
if err = table.Apply(ctx, "row2", m); err != nil {
298+
t.Fatalf("Apply failed: %v", err)
299+
}
300+
301+
m = NewMutation()
302+
m.Set("f", "excluded", ServerTime, []byte("value"))
303+
if err = table.Apply(ctx, "row3", m); err != nil {
304+
t.Fatalf("Apply failed: %v", err)
305+
}
306+
307+
statsChannel := make(chan FullReadStats, 1)
308+
309+
readStart := time.Now()
310+
if err := table.ReadRows(ctx, RowRange{}, func(r Row) bool { return true }, WithFullReadStats(func(s *FullReadStats) { statsChannel <- *s }), RowFilter(ColumnFilter("q.*"))); err != nil {
311+
t.Fatalf("NewClient failed: %v", err)
312+
}
313+
readElapsed := time.Since(readStart)
314+
315+
got := <-statsChannel
316+
317+
wantIter := ReadIterationStats{
318+
RowsSeenCount: 3,
319+
RowsReturnedCount: 2,
320+
CellsSeenCount: 4,
321+
CellsReturnedCount: 3,
322+
}
323+
324+
if diff := cmp.Diff(wantIter, got.ReadIterationStats); diff != "" {
325+
t.Errorf("ReadRows RequestStats are incorrect (-want +got):\n%s", diff)
326+
}
327+
328+
if got.RequestLatencyStats.FrontendServerLatency > readElapsed || got.RequestLatencyStats.FrontendServerLatency <= 0 {
329+
t.Fatalf("ReadRows FrontendServerLatency should be in range 0, %v", readElapsed)
330+
}
331+
}
332+
252333
// TestHeaderPopulatedWithAppProfile verifies that request params header is populated with table name and app profile
253334
func TestHeaderPopulatedWithAppProfile(t *testing.T) {
254335
testEnv, err := NewEmulatedEnv(IntegrationTestConfig{})

bigtable/bttest/inmem.go

+42-11
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import (
5656
"google.golang.org/grpc/codes"
5757
"google.golang.org/grpc/status"
5858
"google.golang.org/protobuf/types/known/anypb"
59+
"google.golang.org/protobuf/types/known/durationpb"
5960
"google.golang.org/protobuf/types/known/timestamppb"
6061
"rsc.io/binaryregexp"
6162
)
@@ -402,6 +403,7 @@ func (s *server) DeleteSnapshot(context.Context, *btapb.DeleteSnapshotRequest) (
402403
}
403404

404405
func (s *server) ReadRows(req *btpb.ReadRowsRequest, stream btpb.Bigtable_ReadRowsServer) error {
406+
start := time.Now()
405407
s.mu.Lock()
406408
tbl, ok := s.tables[req.TableName]
407409
s.mu.Unlock()
@@ -479,36 +481,65 @@ func (s *server) ReadRows(req *btpb.ReadRowsRequest, stream btpb.Bigtable_ReadRo
479481
sort.Sort(byRowKey(rows))
480482

481483
limit := int(req.RowsLimit)
482-
count := 0
484+
if limit == 0 {
485+
limit = len(rows)
486+
}
487+
488+
iterStats := &btpb.ReadIterationStats{}
489+
483490
for _, r := range rows {
484-
if limit > 0 && count >= limit {
485-
return nil
491+
if int(iterStats.RowsReturnedCount) >= limit {
492+
break
486493
}
487-
streamed, err := streamRow(stream, r, req.Filter)
488-
if err != nil {
494+
495+
if err := streamRow(stream, r, req.Filter, iterStats); err != nil {
489496
return err
490497
}
491-
if streamed {
492-
count++
498+
}
499+
500+
elapsed := time.Since(start)
501+
if req.RequestStatsView == btpb.ReadRowsRequest_REQUEST_STATS_FULL {
502+
rrr := &btpb.ReadRowsResponse{}
503+
rrr.RequestStats = &btpb.RequestStats{
504+
StatsView: &btpb.RequestStats_FullReadStatsView{
505+
FullReadStatsView: &btpb.FullReadStatsView{
506+
ReadIterationStats: iterStats,
507+
RequestLatencyStats: &btpb.RequestLatencyStats{
508+
FrontendServerLatency: durationpb.New(elapsed),
509+
},
510+
},
511+
},
493512
}
513+
514+
return stream.Send(rrr)
494515
}
495516
return nil
496517
}
497518

498519
// streamRow filters the given row and sends it via the given stream.
499520
// Returns true if at least one cell matched the filter and was streamed, false otherwise.
500-
func streamRow(stream btpb.Bigtable_ReadRowsServer, r *row, f *btpb.RowFilter) (bool, error) {
521+
func streamRow(stream btpb.Bigtable_ReadRowsServer, r *row, f *btpb.RowFilter, s *btpb.ReadIterationStats) error {
501522
r.mu.Lock()
502523
nr := r.copy()
503524
r.mu.Unlock()
504525
r = nr
505526

527+
s.RowsSeenCount++
528+
for _, f := range r.families {
529+
s.CellsSeenCount += int64(len(f.cells))
530+
}
531+
506532
match, err := filterRow(f, r)
507533
if err != nil {
508-
return false, err
534+
return err
509535
}
510536
if !match {
511-
return false, nil
537+
return nil
538+
}
539+
540+
s.RowsReturnedCount++
541+
for _, f := range r.families {
542+
s.CellsReturnedCount += int64(len(f.cells))
512543
}
513544

514545
rrr := &btpb.ReadRowsResponse{}
@@ -537,7 +568,7 @@ func streamRow(stream btpb.Bigtable_ReadRowsServer, r *row, f *btpb.RowFilter) (
537568
rrr.Chunks[len(rrr.Chunks)-1].RowStatus = &btpb.ReadRowsResponse_CellChunk_CommitRow{CommitRow: true}
538569
}
539570

540-
return true, stream.Send(rrr)
571+
return stream.Send(rrr)
541572
}
542573

543574
// filterRow modifies a row with the given filter. Returns true if at least one cell from the row matches,

0 commit comments

Comments
 (0)