@@ -26,6 +26,23 @@ type Stats struct {
2626 Total time.Duration
2727}
2828
29+ // httpEndpointConfig represents the configuration for an HTTP endpoint.
30+ type httpEndpointConfig struct {
31+ client * http.Client
32+ url string
33+ }
34+
35+ // sgAuthTransport is an http.RoundTripper that adds an Authorization header to requests.
36+ // It is used to add the Sourcegraph access token to requests to Sourcegraph endpoints.
37+ type sgAuthTransport struct {
38+ token string
39+ base http.RoundTripper
40+ }
41+ func (t * sgAuthTransport ) RoundTrip (req * http.Request ) (* http.Response , error ) {
42+ req .Header .Add ("Authorization" , "token " + t .token )
43+ return t .base .RoundTrip (req )
44+ }
45+
2946func init () {
3047 usage := `
3148'src gateway benchmark' runs performance benchmarks against Cody Gateway endpoints.
@@ -38,7 +55,7 @@ Examples:
3855
3956 $ src gateway benchmark
4057 $ src gateway benchmark --requests 50
41- $ src gateway benchmark --gateway http://localhost:9992 --sourcegraph http://localhost:3082
58+ $ src gateway benchmark --gateway http://localhost:9992 --sourcegraph http://localhost:3082 --sgp sgp_***** --requests 50
4259 $ src gateway benchmark --requests 50 --csv results.csv
4360`
4461
@@ -49,6 +66,7 @@ Examples:
4966 csvOutput = flagSet .String ("csv" , "" , "Export results to CSV file (provide filename)" )
5067 gatewayEndpoint = flagSet .String ("gateway" , "https://cody-gateway.sourcegraph.com" , "Cody Gateway endpoint" )
5168 sgEndpoint = flagSet .String ("sourcegraph" , "https://sourcegraph.com" , "Sourcegraph endpoint" )
69+ sgpToken = flagSet .String ("sgp" , "sgp_*****" , "Sourcegraph personal access token for the called instance" )
5270 )
5371
5472 handler := func (args []string ) error {
@@ -63,43 +81,76 @@ Examples:
6381 var (
6482 gatewayWebsocket , sourcegraphWebsocket * websocket.Conn
6583 err error
66- httpClient = & http.Client {}
84+ gatewayClient = & http.Client {}
85+ sourcegraphClient = & http.Client {}
6786 endpoints = map [string ]any {} // Values: URL `string`s or `*websocket.Conn`s
6887 )
88+
89+ // Connect to endpoints
6990 if * gatewayEndpoint != "" {
91+ fmt .Println ("Benchmarking Cody Gateway instance:" , * gatewayEndpoint )
7092 wsURL := strings .Replace (fmt .Sprint (* gatewayEndpoint , "/v2/websocket" ), "http" , "ws" , 1 )
93+ fmt .Println ("Connecting to Cody Gateway via WebSocket.." , wsURL )
7194 gatewayWebsocket , _ , err = websocket .DefaultDialer .Dial (wsURL , nil )
7295 if err != nil {
7396 return fmt .Errorf ("WebSocket dial(%s): %v" , wsURL , err )
7497 }
98+ fmt .Println ("Connected!" )
7599 endpoints ["ws(s): gateway" ] = gatewayWebsocket
76- endpoints ["http(s): gateway" ] = fmt .Sprint (* gatewayEndpoint , "/v2/http" )
100+ endpoints ["http(s): gateway" ] = & httpEndpointConfig {
101+ client : gatewayClient ,
102+ url : fmt .Sprint (* gatewayEndpoint , "/v2/http" ),
103+ }
104+ } else {
105+ fmt .Println ("warning: not benchmarking Cody Gateway (-gateway endpoint not provided)" )
77106 }
78107 if * sgEndpoint != "" {
108+ // Add auth header to sourcegraphClient transport
109+ if * sgpToken != "" {
110+ sourcegraphClient .Transport = & sgAuthTransport {
111+ token : * sgpToken ,
112+ base : http .DefaultTransport ,
113+ }
114+ }
115+ fmt .Println ("Benchmarking Sourcegraph instance:" , * sgEndpoint )
79116 wsURL := strings .Replace (fmt .Sprint (* sgEndpoint , "/.api/gateway/websocket" ), "http" , "ws" , 1 )
80- sourcegraphWebsocket , _ , err = websocket .DefaultDialer .Dial (wsURL , nil )
117+ header := http.Header {}
118+ header .Add ("Authorization" , "token " + * sgpToken )
119+ fmt .Println ("Connecting to Sourcegraph instance via WebSocket.." , wsURL )
120+ sourcegraphWebsocket , _ , err = websocket .DefaultDialer .Dial (wsURL , header )
81121 if err != nil {
82122 return fmt .Errorf ("WebSocket dial(%s): %v" , wsURL , err )
83123 }
124+ fmt .Println ("Connected!" )
125+
84126 endpoints ["ws(s): sourcegraph" ] = sourcegraphWebsocket
85- endpoints ["http(s): sourcegraph" ] = fmt .Sprint (* sgEndpoint , "/.api/gateway/http" )
127+ endpoints ["http(s): sourcegraph" ] = & httpEndpointConfig {
128+ client : sourcegraphClient ,
129+ url : fmt .Sprint (* sgEndpoint , "/.api/gateway/http" ),
130+ }
131+ endpoints ["http(s): http-then-ws" ] = & httpEndpointConfig {
132+ client : sourcegraphClient ,
133+ url : fmt .Sprint (* sgEndpoint , "/.api/gateway/http-then-websocket" ),
134+ }
135+ } else {
136+ fmt .Println ("warning: not benchmarking Sourcegraph instance (-sourcegraph endpoint not provided)" )
86137 }
87138
88139 fmt .Printf ("Starting benchmark with %d requests per endpoint...\n " , * requestCount )
89140
90141 var results []endpointResult
91- for name , clientOrURL := range endpoints {
142+ for name , clientOrEndpointConfig := range endpoints {
92143 durations := make ([]time.Duration , 0 , * requestCount )
93144 fmt .Printf ("\n Testing %s..." , name )
94145
95146 for i := 0 ; i < * requestCount ; i ++ {
96- if ws , ok := clientOrURL .(* websocket.Conn ); ok {
147+ if ws , ok := clientOrEndpointConfig .(* websocket.Conn ); ok {
97148 duration := benchmarkEndpointWebSocket (ws )
98149 if duration > 0 {
99150 durations = append (durations , duration )
100151 }
101- } else if url , ok := clientOrURL .( string ); ok {
102- duration := benchmarkEndpointHTTP (httpClient , url )
152+ } else if epConf , ok := clientOrEndpointConfig .( httpEndpointConfig ); ok {
153+ duration := benchmarkEndpointHTTP (epConf )
103154 if duration > 0 {
104155 durations = append (durations , duration )
105156 }
@@ -161,11 +212,11 @@ type endpointResult struct {
161212 successful int
162213}
163214
164- func benchmarkEndpointHTTP (client * http. Client , url string ) time.Duration {
215+ func benchmarkEndpointHTTP (epConfig httpEndpointConfig ) time.Duration {
165216 start := time .Now ()
166- resp , err := client .Get ( url )
217+ resp , err := epConfig . client .Post ( epConfig . url , "application/json" , strings . NewReader ( "ping" ) )
167218 if err != nil {
168- fmt .Printf ("Error calling %s: %v\n " , url , err )
219+ fmt .Printf ("Error calling %s: %v\n " , epConfig . url , err )
169220 return 0
170221 }
171222 defer func () {
@@ -174,12 +225,6 @@ func benchmarkEndpointHTTP(client *http.Client, url string) time.Duration {
174225 fmt .Printf ("Error closing response body: %v\n " , err )
175226 }
176227 }()
177-
178- _ , err = io .ReadAll (resp .Body )
179- if err != nil {
180- fmt .Printf ("Error reading response body: %v\n " , err )
181- return 0
182- }
183228 if resp .StatusCode != http .StatusOK {
184229 fmt .Printf ("non-200 response: %v\n " , resp .Status )
185230 return 0
@@ -267,7 +312,7 @@ func formatSuccessRate(successful, total int, best bool, worst bool) string {
267312
268313func printResults (results []endpointResult , requestCount * int ) {
269314 // Print header
270- headerFmt := ansiColors ["blue" ] + "%-20s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s" + ansiColors ["nc" ] + "\n "
315+ headerFmt := ansiColors ["blue" ] + "%-25s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s" + ansiColors ["nc" ] + "\n "
271316 fmt .Printf ("\n " + headerFmt ,
272317 "Endpoint " , "Average" , "Median" , "P5" , "P75" , "P80" , "P95" , "Total" , "Success" )
273318 fmt .Println (ansiColors ["blue" ] + strings .Repeat ("-" , 121 ) + ansiColors ["nc" ])
0 commit comments