blob: 3d4020d7c242eff7be55aabead355979c95e0b0b [file] [edit]
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package spanner
import (
"context"
"fmt"
"strings"
"testing"
"time"
"cloud.google.com/go/spanner/apiv1/spannerpb"
"cloud.google.com/go/spanner/internal"
stestutil "cloud.google.com/go/spanner/internal/testutil"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
"google.golang.org/api/iterator"
structpb "google.golang.org/protobuf/types/known/structpb"
)
func TestOCStats_SessionPool_GetSessionTimeoutsCount(t *testing.T) {
DisableGfeLatencyAndHeaderMissingCountViews()
te := stestutil.NewTestExporter(GetSessionTimeoutsCountView)
defer te.Unregister()
server, client, teardown := setupMockedTestServerWithoutWaitingForMultiplexedSessionInit(t)
defer teardown()
server.TestSpanner.PutExecutionTime(stestutil.MethodBatchCreateSession,
stestutil.SimulatedExecutionTime{
MinimumExecutionTime: 2 * time.Millisecond,
})
server.TestSpanner.PutExecutionTime(stestutil.MethodCreateSession,
stestutil.SimulatedExecutionTime{
MinimumExecutionTime: 2 * time.Millisecond,
})
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
client.Single().ReadRow(ctx, "Users", Key{"alice"}, []string{"email"})
// Wait for a while to see all exported metrics.
waitErr := &Error{}
waitFor(t, func() error {
select {
case stat := <-te.Stats:
if len(stat.Rows) > 0 {
return nil
}
}
return waitErr
})
// Wait until we see data from the view.
select {
case stat := <-te.Stats:
if len(stat.Rows) == 0 {
t.Fatal("No metrics are exported")
}
if got, want := stat.View.Measure.Name(), statsPrefix+"get_session_timeouts"; got != want {
t.Fatalf("Incorrect measure: got %v, want %v", got, want)
}
row := stat.Rows[0]
m := getTagMap(row.Tags)
checkCommonTags(t, m)
data := row.Data.(*view.CountData).Value
if got, want := fmt.Sprintf("%v", data), "1"; got != want {
t.Fatalf("Incorrect data: got %v, want %v", got, want)
}
case <-time.After(1 * time.Second):
t.Fatal("no stats were exported before timeout")
}
}
func TestOCStats_GFE_Latency(t *testing.T) {
te := stestutil.NewTestExporter([]*view.View{GFELatencyView, GFEHeaderMissingCountView}...)
defer te.Unregister()
setGFELatencyMetricsFlag(true)
server, client, teardown := setupMockedTestServer(t)
defer teardown()
if err := server.TestSpanner.PutStatementResult("SELECT email FROM Users", &stestutil.StatementResult{
Type: stestutil.StatementResultResultSet,
ResultSet: &spannerpb.ResultSet{
Metadata: &spannerpb.ResultSetMetadata{
RowType: &spannerpb.StructType{
Fields: []*spannerpb.StructType_Field{
{
Name: "email",
Type: &spannerpb.Type{Code: spannerpb.TypeCode_STRING},
},
},
},
},
Rows: []*structpb.ListValue{
{Values: []*structpb.Value{{
Kind: &structpb.Value_StringValue{StringValue: "test@test.com"},
}}},
},
},
}); err != nil {
t.Fatalf("could not add result: %v", err)
}
iter := client.Single().Read(context.Background(), "Users", AllKeys(), []string{"email"})
for {
_, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
t.Fatal(err.Error())
}
}
waitErr := &Error{}
waitFor(t, func() error {
select {
case stat := <-te.Stats:
if len(stat.Rows) > 0 {
return nil
}
}
return waitErr
})
// Wait until we see data from the view.
select {
case stat := <-te.Stats:
if len(stat.Rows) == 0 {
t.Fatal("No metrics are exported")
}
if stat.View.Measure.Name() != statsPrefix+"gfe_latency" && stat.View.Measure.Name() != statsPrefix+"gfe_header_missing_count" {
t.Fatalf("Incorrect measure: got %v, want %v", stat.View.Measure.Name(), statsPrefix+"gfe_header_missing_count or "+statsPrefix+"gfe_latency")
}
row := stat.Rows[0]
m := getTagMap(row.Tags)
checkCommonTags(t, m)
var data string
switch row.Data.(type) {
default:
data = fmt.Sprintf("%v", row.Data)
case *view.CountData:
data = fmt.Sprintf("%v", row.Data.(*view.CountData).Value)
case *view.LastValueData:
data = fmt.Sprintf("%v", row.Data.(*view.LastValueData).Value)
case *view.DistributionData:
data = fmt.Sprintf("%v", row.Data.(*view.DistributionData).Count)
}
if got, want := fmt.Sprintf("%v", data), "0"; got <= want {
t.Fatalf("Incorrect data: got %v, wanted more than %v for metric %v", got, want, stat.View.Measure.Name())
}
case <-time.After(1 * time.Second):
t.Fatal("no stats were exported before timeout")
}
}
func getTagMap(tags []tag.Tag) map[tag.Key]string {
m := make(map[tag.Key]string)
for _, t := range tags {
m[t.Key] = t.Value
}
return m
}
func checkCommonTags(t *testing.T, m map[tag.Key]string) {
// We only check prefix because client ID increases if we create
// multiple clients for the same database.
if !strings.HasPrefix(m[tagKeyClientID], "client-") {
t.Fatalf("Incorrect client ID: %v", m[tagKeyClientID])
}
if m[tagKeyInstance] != "[INSTANCE]" {
t.Fatalf("Incorrect instance ID: %v", m[tagKeyInstance])
}
if m[tagKeyDatabase] != "[DATABASE]" {
t.Fatalf("Incorrect database ID: %v", m[tagKeyDatabase])
}
if m[tagKeyLibVersion] != internal.Version {
t.Fatalf("Incorrect library version: %v", m[tagKeyLibVersion])
}
}