-
Notifications
You must be signed in to change notification settings - Fork 213
Fix bsr watcher leak #873
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix bsr watcher leak #873
Changes from all commits
367bee9
5396bc7
78c9581
af20c3d
2821b50
383d936
357333d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ import ( | |
|
|
||
| "buf.build/gen/go/bufbuild/reflect/connectrpc/go/buf/reflect/v1beta1/reflectv1beta1connect" | ||
| connectrpc "connectrpc.com/connect" | ||
| "github.com/Jeffail/shutdown" | ||
| "github.com/bufbuild/prototransform" | ||
| "google.golang.org/protobuf/reflect/protoreflect" | ||
| "google.golang.org/protobuf/reflect/protoregistry" | ||
|
|
@@ -19,6 +20,8 @@ import ( | |
|
|
||
| type MultiModuleWatcher struct { | ||
| bsrClients map[string]*prototransform.SchemaWatcher | ||
| httpClient *http.Client | ||
| shutSig *shutdown.Signaller | ||
| } | ||
|
|
||
| var _ prototransform.Resolver = &MultiModuleWatcher{} | ||
|
|
@@ -27,10 +30,19 @@ func newMultiModuleWatcher(bsrModules []*service.ParsedConfig) (*MultiModuleWatc | |
| if len(bsrModules) == 0 { | ||
| return nil, errors.New("no modules provided") | ||
| } | ||
| multiModuleWatcher := &MultiModuleWatcher{} | ||
|
|
||
| httpClient := &http.Client{ | ||
| Transport: &http.Transport{}, | ||
| } | ||
| multiModuleWatcher := &MultiModuleWatcher{ | ||
| bsrClients: make(map[string]*prototransform.SchemaWatcher), | ||
| httpClient: httpClient, | ||
| shutSig: shutdown.NewSignaller(), | ||
| } | ||
|
|
||
| ctx, _ := multiModuleWatcher.shutSig.SoftStopCtx(context.Background()) | ||
|
|
||
| // Initialise one client for each module | ||
| multiModuleWatcher.bsrClients = make(map[string]*prototransform.SchemaWatcher) | ||
| for _, bsrModule := range bsrModules { | ||
| var bsrURL string | ||
| bsrURL, err := bsrModule.FieldString(fieldBSRUrl) | ||
|
|
@@ -53,7 +65,7 @@ func newMultiModuleWatcher(bsrModules []*service.ParsedConfig) (*MultiModuleWatc | |
| return nil, err | ||
| } | ||
|
|
||
| watcher, err := newSchemaWatcher(context.Background(), bsrURL, bsrAPIKey, module, version) | ||
| watcher, err := newSchemaWatcher(ctx, httpClient, bsrURL, bsrAPIKey, module, version) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
@@ -63,7 +75,7 @@ func newMultiModuleWatcher(bsrModules []*service.ParsedConfig) (*MultiModuleWatc | |
| return multiModuleWatcher, nil | ||
| } | ||
|
|
||
| func newSchemaWatcher(ctx context.Context, bsrURL string, bsrAPIKey string, module string, version string) (*prototransform.SchemaWatcher, error) { | ||
| func newSchemaWatcher(ctx context.Context, httpClient *http.Client, bsrURL string, bsrAPIKey string, module string, version string) (*prototransform.SchemaWatcher, error) { | ||
| // If no BSR url provided, extract from module | ||
| if bsrURL == "" { | ||
| segments := strings.Split(module, "/") | ||
|
|
@@ -80,7 +92,7 @@ func newSchemaWatcher(ctx context.Context, bsrURL string, bsrAPIKey string, modu | |
| if bsrAPIKey != "" { | ||
| opts = append(opts, connectrpc.WithInterceptors(prototransform.NewAuthInterceptor(bsrAPIKey))) | ||
| } | ||
| client := reflectv1beta1connect.NewFileDescriptorSetServiceClient(http.DefaultClient, bsrURL, opts...) | ||
| client := reflectv1beta1connect.NewFileDescriptorSetServiceClient(httpClient, bsrURL, opts...) | ||
|
|
||
| cfg := &prototransform.SchemaWatcherConfig{ | ||
| SchemaPoller: prototransform.NewSchemaPoller( | ||
|
|
@@ -173,3 +185,14 @@ func (w *MultiModuleWatcher) FindEnumByName(enum protoreflect.FullName) (protore | |
| } | ||
| return nil, fmt.Errorf("could not find %s in any loaded modules", enum) | ||
| } | ||
|
|
||
| func (m *MultiModuleWatcher) Close() { | ||
| m.shutSig.TriggerHardStop() | ||
| for _, v := range m.bsrClients { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q: Is it a memory leak that we never actually clear these clients stored in the map? Also, what if one stops before we call close -- should it be deleted early? 🤔
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a |
||
| v.Stop() | ||
| } | ||
| m.bsrClients = nil | ||
| if t, ok := m.httpClient.Transport.(*http.Transport); ok { | ||
| t.CloseIdleConnections() | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -549,6 +549,10 @@ func (p *protobufProc) Process(ctx context.Context, msg *service.Message) (servi | |
| return service.MessageBatch{msg}, nil | ||
| } | ||
|
|
||
| func (p *protobufProc) Close(context.Context) error { | ||
| func (p *protobufProc) Close(ctx context.Context) error { | ||
| if p.multiModuleWatcher != nil { | ||
| p.multiModuleWatcher.Close() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we be setting |
||
| p.multiModuleWatcher = nil | ||
| } | ||
| return nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.