google.golang.org/grpc@v1.72.2/orca/service_test.go (about) 1 /* 2 * 3 * Copyright 2022 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package orca_test 20 21 import ( 22 "context" 23 "fmt" 24 "sync/atomic" 25 "testing" 26 "time" 27 28 "github.com/google/go-cmp/cmp" 29 "google.golang.org/grpc" 30 "google.golang.org/grpc/credentials/insecure" 31 "google.golang.org/grpc/internal/pretty" 32 "google.golang.org/grpc/internal/stubserver" 33 "google.golang.org/grpc/internal/testutils" 34 "google.golang.org/grpc/orca" 35 "google.golang.org/grpc/orca/internal" 36 "google.golang.org/protobuf/proto" 37 "google.golang.org/protobuf/types/known/durationpb" 38 39 v3orcapb "github.com/cncf/xds/go/xds/data/orca/v3" 40 v3orcaservicegrpc "github.com/cncf/xds/go/xds/service/orca/v3" 41 v3orcaservicepb "github.com/cncf/xds/go/xds/service/orca/v3" 42 testgrpc "google.golang.org/grpc/interop/grpc_testing" 43 testpb "google.golang.org/grpc/interop/grpc_testing" 44 ) 45 46 const requestsMetricKey = "test-service-requests" 47 48 // TestE2E_CustomBackendMetrics_OutOfBand tests the injection of out-of-band 49 // custom backend metrics from the server application, and verifies that 50 // expected load reports are received at the client. 51 // 52 // TODO: Change this test to use the client API, when ready, to read the 53 // out-of-band metrics pushed by the server. 54 func (s) TestE2E_CustomBackendMetrics_OutOfBand(t *testing.T) { 55 lis, err := testutils.LocalTCPListener() 56 if err != nil { 57 t.Fatal(err) 58 } 59 60 // Override the min reporting interval in the internal package. 61 const shortReportingInterval = 10 * time.Millisecond 62 smr := orca.NewServerMetricsRecorder() 63 opts := orca.ServiceOptions{MinReportingInterval: shortReportingInterval, ServerMetricsProvider: smr} 64 internal.AllowAnyMinReportingInterval.(func(*orca.ServiceOptions))(&opts) 65 66 var requests atomic.Int64 67 68 stub := &stubserver.StubServer{ 69 Listener: lis, 70 UnaryCallF: func(ctx context.Context, req *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { 71 newRequests := requests.Add(1) 72 73 smr.SetNamedUtilization(requestsMetricKey, float64(newRequests)*0.01) 74 smr.SetCPUUtilization(50.0) 75 smr.SetMemoryUtilization(0.9) 76 smr.SetApplicationUtilization(1.2) 77 return &testpb.SimpleResponse{}, nil 78 }, 79 EmptyCallF: func(ctx context.Context, req *testpb.Empty) (*testpb.Empty, error) { 80 smr.DeleteNamedUtilization(requestsMetricKey) 81 smr.SetCPUUtilization(0) 82 smr.SetMemoryUtilization(0) 83 smr.DeleteApplicationUtilization() 84 return &testpb.Empty{}, nil 85 }, 86 } 87 88 // Assign the gRPC server to the stub server and start serving. 89 stub.S = grpc.NewServer() 90 // Register the OpenRCAService with a very short metrics reporting interval. 91 if err := orca.Register(stub.S, opts); err != nil { 92 t.Fatalf("orca.EnableOutOfBandMetricsReportingForTesting() failed: %v", err) 93 } 94 stubserver.StartTestService(t, stub) 95 defer stub.S.Stop() 96 t.Logf("Started gRPC server at %s...", lis.Addr().String()) 97 98 // Dial the test server. 99 cc, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) 100 if err != nil { 101 t.Fatalf("grpc.NewClient(%s) failed: %v", lis.Addr().String(), err) 102 } 103 defer cc.Close() 104 105 // Spawn a goroutine which sends 20 unary RPCs to the stub server. This 106 // will trigger the injection of custom backend metrics from the 107 // stubServer. 108 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 109 defer cancel() 110 testStub := testgrpc.NewTestServiceClient(cc) 111 const numRequests = 20 112 errCh := make(chan error, 1) 113 go func() { 114 for i := 0; i < numRequests; i++ { 115 if _, err := testStub.UnaryCall(ctx, &testpb.SimpleRequest{}); err != nil { 116 errCh <- fmt.Errorf("UnaryCall failed: %v", err) 117 return 118 } 119 time.Sleep(time.Millisecond) 120 } 121 errCh <- nil 122 }() 123 124 // Start the server streaming RPC to receive custom backend metrics. 125 oobStub := v3orcaservicegrpc.NewOpenRcaServiceClient(cc) 126 stream, err := oobStub.StreamCoreMetrics(ctx, &v3orcaservicepb.OrcaLoadReportRequest{ReportInterval: durationpb.New(shortReportingInterval)}) 127 if err != nil { 128 t.Fatalf("Failed to create a stream for out-of-band metrics") 129 } 130 131 // Wait for the server to push metrics which indicate the completion of all 132 // the unary RPCs made from the above goroutine. 133 for { 134 select { 135 case <-ctx.Done(): 136 t.Fatal("Timeout when waiting for out-of-band custom backend metrics to match expected values") 137 case err := <-errCh: 138 if err != nil { 139 t.Fatal(err) 140 } 141 default: 142 } 143 144 wantProto := &v3orcapb.OrcaLoadReport{ 145 CpuUtilization: 50.0, 146 MemUtilization: 0.9, 147 ApplicationUtilization: 1.2, 148 Utilization: map[string]float64{requestsMetricKey: numRequests * 0.01}, 149 } 150 gotProto, err := stream.Recv() 151 if err != nil { 152 t.Fatalf("Recv() failed: %v", err) 153 } 154 if !cmp.Equal(gotProto, wantProto, cmp.Comparer(proto.Equal)) { 155 t.Logf("Received load report from stream: %s, want: %s", pretty.ToJSON(gotProto), pretty.ToJSON(wantProto)) 156 continue 157 } 158 // This means that we received the metrics which we expected. 159 break 160 } 161 162 // The EmptyCall RPC is expected to delete earlier injected metrics. 163 if _, err := testStub.EmptyCall(ctx, &testpb.Empty{}); err != nil { 164 t.Fatalf("EmptyCall failed: %v", err) 165 } 166 // Wait for the server to push empty metrics which indicate the processing 167 // of the above EmptyCall RPC. 168 for { 169 select { 170 case <-ctx.Done(): 171 t.Fatal("Timeout when waiting for out-of-band custom backend metrics to match expected values") 172 default: 173 } 174 175 wantProto := &v3orcapb.OrcaLoadReport{} 176 gotProto, err := stream.Recv() 177 if err != nil { 178 t.Fatalf("Recv() failed: %v", err) 179 } 180 if !cmp.Equal(gotProto, wantProto, cmp.Comparer(proto.Equal)) { 181 t.Logf("Received load report from stream: %s, want: %s", pretty.ToJSON(gotProto), pretty.ToJSON(wantProto)) 182 continue 183 } 184 // This means that we received the metrics which we expected. 185 break 186 } 187 }