Skip to content

Commit 389805a

Browse files
authored
Enable GRPC Connection pooling (#23)
* implement a connection pool * add a todo for when we next adjust the client interface * wording * lint * just do the todo * 1.5.0 * lint false positive
1 parent e70bc27 commit 389805a

21 files changed

+96
-77
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*.dll
55
*.so
66
*.dylib
7+
*.iml
78

89
# Test binary, built with `go test -c`
910
*.test

capture.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"io/ioutil"
88
"net/http"
9+
"os"
910
"time"
1011

1112
"github.com/speakeasy-api/speakeasy-go-sdk/internal/log"
@@ -37,6 +38,7 @@ func (s *Speakeasy) handleRequestResponse(w http.ResponseWriter, r *http.Request
3738
}
3839

3940
func (s *Speakeasy) handleRequestResponseError(w http.ResponseWriter, r *http.Request, next handlerFunc, capturePathHint func(r *http.Request) string) error {
41+
//nolint:ifshort
4042
startTime := timeNow()
4143

4244
cw := NewCaptureWriter(w, maxCaptureSize)
@@ -62,12 +64,17 @@ func (s *Speakeasy) handleRequestResponseError(w http.ResponseWriter, r *http.Re
6264
pathHint = c.pathHint
6365
}
6466

65-
// Assuming response is done
66-
go s.captureRequestResponse(cw, r, startTime, pathHint, c)
67-
67+
// Used for load testing: set this to true and the capture GRPC call is invoked inline.
68+
// This will cause the endpoint latency to be added to the GRPC request/response latency
69+
if os.Getenv("SPEAKEASY_SDK_CAPTURE_INLINE") == "true" {
70+
s.captureRequestResponse(cw, r, startTime, pathHint, c)
71+
} else {
72+
go s.captureRequestResponse(cw, r, startTime, pathHint, c)
73+
}
6874
return err
6975
}
7076

77+
//nolint:nolintlint,contextcheck
7178
func (s *Speakeasy) captureRequestResponse(cw *captureWriter, r *http.Request, startTime time.Time, pathHint string, c *controller) {
7279
var ctx context.Context = valueOnlyContext{r.Context()}
7380

middleware.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func Middleware(next http.Handler) http.Handler {
2020
// Currently only gorilla/mux, go-chi/chi routers and the http.DefaultServerMux are supported for automatically
2121
// capturing path hints. Otherwise path hints can be supplied by a handler through the speakeasy MiddlewareController.
2222
//
23-
//nolint:contextcheck
23+
//nolint:nolintlint,contextcheck
2424
func (s *Speakeasy) Middleware(next http.Handler) http.Handler {
2525
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2626
s.handleRequestResponse(w, r, next.ServeHTTP, func(r *http.Request) string {

middleware_test.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func TestSpeakeasy_Middleware_Capture_Success(t *testing.T) {
137137
wg := &sync.WaitGroup{}
138138
wg.Add(1)
139139

140-
sdkInstance := speakeasy.New(speakeasy.Config{
140+
sdkInstance, err := speakeasy.New(speakeasy.Config{
141141
APIKey: testAPIKey,
142142
ApiID: testApiID,
143143
VersionID: testVersionID,
@@ -156,6 +156,7 @@ func TestSpeakeasy_Middleware_Capture_Success(t *testing.T) {
156156
wg.Done()
157157
}),
158158
})
159+
assert.Nil(t, err)
159160

160161
h := sdkInstance.Middleware(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
161162
ctrl := speakeasy.MiddlewareController(req)
@@ -240,7 +241,6 @@ func TestSpeakeasy_Middleware_Capture_Success(t *testing.T) {
240241
w := httptest.NewRecorder()
241242

242243
var req *http.Request
243-
var err error
244244
if tt.Args.Body == "" {
245245
req, err = http.NewRequest(tt.Args.Method, tt.Args.URL, nil)
246246
} else {
@@ -342,7 +342,7 @@ func TestSpeakeasy_Middleware_URL_Resolve_Success(t *testing.T) {
342342
wg := &sync.WaitGroup{}
343343
wg.Add(1)
344344

345-
sdkInstance := speakeasy.New(speakeasy.Config{
345+
sdkInstance, err := speakeasy.New(speakeasy.Config{
346346
APIKey: testAPIKey,
347347
ApiID: testApiID,
348348
VersionID: testVersionID,
@@ -357,6 +357,7 @@ func TestSpeakeasy_Middleware_URL_Resolve_Success(t *testing.T) {
357357
wg.Done()
358358
}),
359359
})
360+
assert.Nil(t, err)
360361

361362
r := mux.NewRouter()
362363
r.Use(sdkInstance.Middleware)
@@ -440,7 +441,7 @@ func TestSpeakeasy_Middleware_GorillaMux_PathHint_Success(t *testing.T) {
440441
wg := &sync.WaitGroup{}
441442
wg.Add(1)
442443

443-
sdkInstance := speakeasy.New(speakeasy.Config{
444+
sdkInstance, err := speakeasy.New(speakeasy.Config{
444445
APIKey: testAPIKey,
445446
ApiID: testApiID,
446447
VersionID: testVersionID,
@@ -450,6 +451,7 @@ func TestSpeakeasy_Middleware_GorillaMux_PathHint_Success(t *testing.T) {
450451
wg.Done()
451452
}),
452453
})
454+
assert.Nil(t, err)
453455

454456
r := mux.NewRouter()
455457
r.Use(sdkInstance.Middleware)
@@ -519,7 +521,7 @@ func TestSpeakeasy_Middleware_Chi_PathHint_Success(t *testing.T) {
519521
wg := &sync.WaitGroup{}
520522
wg.Add(1)
521523

522-
sdkInstance := speakeasy.New(speakeasy.Config{
524+
sdkInstance, err := speakeasy.New(speakeasy.Config{
523525
APIKey: testAPIKey,
524526
ApiID: testApiID,
525527
VersionID: testVersionID,
@@ -529,6 +531,7 @@ func TestSpeakeasy_Middleware_Chi_PathHint_Success(t *testing.T) {
529531
wg.Done()
530532
}),
531533
})
534+
assert.Nil(t, err)
532535

533536
r := chi.NewRouter()
534537
r.Use(sdkInstance.Middleware)
@@ -584,7 +587,7 @@ func TestSpeakeasy_Middleware_ServerMux_PathHint_Success(t *testing.T) {
584587
wg := &sync.WaitGroup{}
585588
wg.Add(1)
586589

587-
sdkInstance := speakeasy.New(speakeasy.Config{
590+
sdkInstance, err := speakeasy.New(speakeasy.Config{
588591
APIKey: testAPIKey,
589592
ApiID: testApiID,
590593
VersionID: testVersionID,
@@ -594,6 +597,7 @@ func TestSpeakeasy_Middleware_ServerMux_PathHint_Success(t *testing.T) {
594597
wg.Done()
595598
}),
596599
})
600+
assert.Nil(t, err)
597601

598602
r := http.DefaultServeMux
599603

@@ -641,7 +645,7 @@ func TestSpeakeasy_GinMiddleware_Success(t *testing.T) {
641645
wg := &sync.WaitGroup{}
642646
wg.Add(1)
643647

644-
sdkInstance := speakeasy.New(speakeasy.Config{
648+
sdkInstance, err := speakeasy.New(speakeasy.Config{
645649
APIKey: testAPIKey,
646650
ApiID: testApiID,
647651
VersionID: testVersionID,
@@ -651,6 +655,7 @@ func TestSpeakeasy_GinMiddleware_Success(t *testing.T) {
651655
wg.Done()
652656
}),
653657
})
658+
assert.Nil(t, err)
654659

655660
r := gin.Default()
656661
r.Use(sdkInstance.GinMiddleware)
@@ -738,7 +743,6 @@ func TestSpeakeasy_GinMiddleware_Success(t *testing.T) {
738743
w := httptest.NewRecorder()
739744

740745
var req *http.Request
741-
var err error
742746
if tt.Args.Body == "" {
743747
req, err = http.NewRequest(tt.Args.Method, tt.Args.URL, nil)
744748
} else {
@@ -817,7 +821,7 @@ func TestSpeakeasy_GinMiddleware_PathHint_Success(t *testing.T) {
817821
wg := &sync.WaitGroup{}
818822
wg.Add(1)
819823

820-
sdkInstance := speakeasy.New(speakeasy.Config{
824+
sdkInstance, err := speakeasy.New(speakeasy.Config{
821825
APIKey: testAPIKey,
822826
ApiID: testApiID,
823827
VersionID: testVersionID,
@@ -827,6 +831,7 @@ func TestSpeakeasy_GinMiddleware_PathHint_Success(t *testing.T) {
827831
wg.Done()
828832
}),
829833
})
834+
assert.Nil(t, err)
830835

831836
r := gin.Default()
832837
r.Use(sdkInstance.GinMiddleware)
@@ -881,7 +886,7 @@ func TestSpeakeasy_EchoMiddleware_Success(t *testing.T) {
881886
wg := &sync.WaitGroup{}
882887
wg.Add(1)
883888

884-
sdkInstance := speakeasy.New(speakeasy.Config{
889+
sdkInstance, err := speakeasy.New(speakeasy.Config{
885890
APIKey: testAPIKey,
886891
ApiID: testApiID,
887892
VersionID: testVersionID,
@@ -891,6 +896,7 @@ func TestSpeakeasy_EchoMiddleware_Success(t *testing.T) {
891896
wg.Done()
892897
}),
893898
})
899+
assert.Nil(t, err)
894900

895901
r := echo.New()
896902
r.Use(sdkInstance.EchoMiddleware)
@@ -980,7 +986,6 @@ func TestSpeakeasy_EchoMiddleware_Success(t *testing.T) {
980986
w := httptest.NewRecorder()
981987

982988
var req *http.Request
983-
var err error
984989
if tt.Args.Body == "" {
985990
req, err = http.NewRequest(tt.Args.Method, tt.Args.URL, nil)
986991
} else {
@@ -1059,7 +1064,7 @@ func TestSpeakeasy_EchoMiddleware_PathHint_Success(t *testing.T) {
10591064
wg := &sync.WaitGroup{}
10601065
wg.Add(1)
10611066

1062-
sdkInstance := speakeasy.New(speakeasy.Config{
1067+
sdkInstance, err := speakeasy.New(speakeasy.Config{
10631068
APIKey: testAPIKey,
10641069
ApiID: testApiID,
10651070
VersionID: testVersionID,
@@ -1069,6 +1074,7 @@ func TestSpeakeasy_EchoMiddleware_PathHint_Success(t *testing.T) {
10691074
wg.Done()
10701075
}),
10711076
})
1077+
assert.Nil(t, err)
10721078

10731079
r := echo.New()
10741080
r.Use(sdkInstance.EchoMiddleware)
@@ -1131,7 +1137,7 @@ func TestSpeakeasy_Middleware_Capture_CustomerID_Success(t *testing.T) {
11311137
wg := &sync.WaitGroup{}
11321138
wg.Add(1)
11331139

1134-
sdkInstance := speakeasy.New(speakeasy.Config{
1140+
sdkInstance, err := speakeasy.New(speakeasy.Config{
11351141
APIKey: testAPIKey,
11361142
ApiID: testApiID,
11371143
VersionID: testVersionID,
@@ -1141,6 +1147,7 @@ func TestSpeakeasy_Middleware_Capture_CustomerID_Success(t *testing.T) {
11411147
wg.Done()
11421148
}),
11431149
})
1150+
assert.Nil(t, err)
11441151

11451152
w := httptest.NewRecorder()
11461153

speakeasy.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const (
2929
)
3030

3131
var (
32-
speakeasyVersion = "1.3.4" // TODO get this from CI
32+
speakeasyVersion = "1.5.0" // TODO get this from CI
3333
serverURL = "grpc.prod.speakeasyapi.dev:443"
3434

3535
defaultInstance *Speakeasy
@@ -63,29 +63,39 @@ type Speakeasy struct {
6363

6464
// Configure allows you to configure the default instance of the Speakeasy SDK.
6565
// Use this if you will use the same API Key for all connected APIs.
66-
func Configure(config Config) {
67-
defaultInstance = New(config)
66+
func Configure(config Config) error {
67+
globalInstance, err := New(config)
68+
defaultInstance = globalInstance
69+
return err
6870
}
6971

7072
// New creates a new instance of the Speakeasy SDK.
7173
// This allows you to create multiple instances of the SDK
7274
// for specifying different API Keys for different APIs.
73-
func New(config Config) *Speakeasy {
75+
func New(config Config) (*Speakeasy, error) {
7476
s := &Speakeasy{}
75-
s.configure(config)
77+
err := s.configure(config)
7678

77-
return s
79+
return s, err
7880
}
7981

8082
func GetEmbedAccessToken(ctx context.Context, req *embedaccesstoken.EmbedAccessTokenRequest) (string, error) {
8183
return defaultInstance.GetEmbedAccessToken(ctx, req)
8284
}
8385

86+
func Close() error {
87+
return defaultInstance.Close()
88+
}
89+
8490
func (s *Speakeasy) GetEmbedAccessToken(ctx context.Context, req *embedaccesstoken.EmbedAccessTokenRequest) (string, error) {
8591
return s.grpcClient.GetEmbedAccessToken(ctx, req)
8692
}
8793

88-
func (s *Speakeasy) configure(cfg Config) {
94+
func (s *Speakeasy) Close() error {
95+
return s.grpcClient.conn.Close()
96+
}
97+
98+
func (s *Speakeasy) configure(cfg Config) error {
8999
mustValidateConfig(cfg)
90100

91101
// The below environment variables allow the overriding of the location of the ingest server.
@@ -107,7 +117,9 @@ func (s *Speakeasy) configure(cfg Config) {
107117

108118
s.config = cfg
109119

110-
s.grpcClient = newGRPCClient(s.config.APIKey, configuredServerURL, secure, s.config.GRPCDialer)
120+
grpcClient, err := newGRPCClient(context.Background(), s.config.APIKey, configuredServerURL, secure, s.config.GRPCDialer)
121+
s.grpcClient = grpcClient
122+
return err
111123
}
112124

113125
func mustValidateConfig(cfg Config) {

speakeasy_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ func TestConfigure_Success(t *testing.T) {
6969
os.Setenv("SPEAKEASY_SERVER_SECURE", strconv.FormatBool(*tt.fields.envSecure))
7070
}
7171

72-
sdkInstance := speakeasy.New(tt.args.config)
72+
sdkInstance, err := speakeasy.New(tt.args.config)
73+
assert.Nil(t, err)
7374
assert.NotNil(t, sdkInstance)
7475

7576
config := sdkInstance.ExportGetSpeakeasyConfig()
@@ -163,7 +164,7 @@ func TestConfigure_Error(t *testing.T) {
163164
for _, tt := range tests {
164165
t.Run(tt.name, func(t *testing.T) {
165166
assert.PanicsWithError(t, tt.wantErr, func() {
166-
speakeasy.New(tt.args.config)
167+
_, _ = speakeasy.New(tt.args.config)
167168
})
168169
})
169170
}

testdata/captures_basic_request_and_no_response_body_output.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "1.2",
44
"creator": {
55
"name": "speakeasy-go-sdk",
6-
"version": "1.3.4"
6+
"version": "1.5.0"
77
},
88
"entries": [
99
{

testdata/captures_basic_request_and_response_output.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "1.2",
44
"creator": {
55
"name": "speakeasy-go-sdk",
6-
"version": "1.3.4"
6+
"version": "1.5.0"
77
},
88
"entries": [
99
{

testdata/captures_basic_request_and_response_with_different_content_types_output.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "1.2",
44
"creator": {
55
"name": "speakeasy-go-sdk",
6-
"version": "1.3.4"
6+
"version": "1.5.0"
77
},
88
"entries": [
99
{

testdata/captures_basic_request_and_response_with_no_response_header_set_output.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "1.2",
44
"creator": {
55
"name": "speakeasy-go-sdk",
6-
"version": "1.3.4"
6+
"version": "1.5.0"
77
},
88
"entries": [
99
{

0 commit comments

Comments
 (0)