@@ -26,6 +26,7 @@ import (
26
26
"reflect"
27
27
"strings"
28
28
"sync"
29
+ "sync/atomic"
29
30
30
31
"github.com/ServiceWeaver/weaver/internal/config"
31
32
"github.com/ServiceWeaver/weaver/internal/control"
@@ -45,8 +46,9 @@ import (
45
46
"golang.org/x/sync/errgroup"
46
47
)
47
48
48
- // readyMethodKey holds the key for a method used to check if a backend is ready.
49
- var readyMethodKey = call .MakeMethodKey ("" , "ready" )
49
+ // readyMethodName holds the name of the special component method used by the
50
+ // clients to check if a component is ready.
51
+ const readyMethodName = "ready"
50
52
51
53
// RemoteWeaveletOptions configure a RemoteWeavelet.
52
54
type RemoteWeaveletOptions struct {
@@ -113,16 +115,10 @@ type component struct {
113
115
114
116
implInit sync.Once // used to initialize impl, severStub
115
117
implErr error // non-nil if impl creation fails
118
+ implReady atomic.Bool // true only after impl creation succeeds
116
119
impl any // instance of component implementation
117
120
serverStub codegen.Server // handles remote calls from other processes
118
121
119
- // TODO(mwhittaker): We have one client for every component. Every client
120
- // independently maintains network connections to every weavelet hosting
121
- // the component. Thus, there may be many redundant network connections to
122
- // the same weavelet. Given n weavelets hosting m components, there's at
123
- // worst n^2m connections rather than a more optimal n^2 (a single
124
- // connection between every pair of weavelets). We should rewrite things to
125
- // avoid the redundancy.
126
122
resolver * routingResolver // client resolver
127
123
balancer * routingBalancer // client balancer
128
124
@@ -419,6 +415,7 @@ func (w *RemoteWeavelet) GetImpl(t reflect.Type) (any, error) {
419
415
return
420
416
} else {
421
417
w .syslogger .Debug ("Constructed" , "component" , name )
418
+ c .implReady .Store (true )
422
419
}
423
420
424
421
logger := w .logger (c .reg .Name )
@@ -513,7 +510,7 @@ func (w *RemoteWeavelet) makeStub(fullName string, reg *codegen.Registration, re
513
510
return nil , err
514
511
}
515
512
if wait {
516
- if err := waitUntilReady (w .ctx , conn ); err != nil {
513
+ if err := waitUntilReady (w .ctx , conn , fullName ); err != nil {
517
514
w .syslogger .Error ("Failed to wait for remote" , "component" , name , "err" , err )
518
515
return nil , err
519
516
}
@@ -647,7 +644,16 @@ func (w *RemoteWeavelet) UpdateRoutingInfo(ctx context.Context, req *protos.Upda
647
644
648
645
// GetHealth implements controller.GetHealth.
649
646
func (w * RemoteWeavelet ) GetHealth (ctx context.Context , req * protos.GetHealthRequest ) (* protos.GetHealthReply , error ) {
650
- return & protos.GetHealthReply {Status : protos .HealthStatus_HEALTHY }, nil
647
+ // Get the health status for all components. For now, we consider a component
648
+ // healthy iff it has been successfully initialized. In the future, we will
649
+ // maintain a real-time health for each component.
650
+ reply := & protos.GetHealthReply {Status : protos .HealthStatus_HEALTHY }
651
+ for cname , c := range w .componentsByName {
652
+ if c .implReady .Load () {
653
+ reply .HealthyComponents = append (reply .HealthyComponents , cname )
654
+ }
655
+ }
656
+ return reply , nil
651
657
}
652
658
653
659
// GetMetrics implements controller.GetMetrics.
@@ -718,6 +724,16 @@ func (w *RemoteWeavelet) addHandlers(handlers *call.HandlerMap, c *component) {
718
724
}
719
725
handlers .Set (c .reg .Name , mname , handler )
720
726
}
727
+
728
+ // Add the special "component is ready" method handler, which is used by
729
+ // the clients to wait for the component to be ready before receiving traffic
730
+ // (see waitUntilReady).
731
+ handlers .Set (c .reg .Name , readyMethodName , func (context.Context , []byte ) ([]byte , error ) {
732
+ if c .implReady .Load () {
733
+ return nil , nil
734
+ }
735
+ return nil , call .Unreachable
736
+ })
721
737
}
722
738
723
739
// repeatedly repeatedly executes f until it succeeds or until ctx is cancelled.
@@ -924,10 +940,10 @@ func (s *server) handlers(components []string) (*call.HandlerMap, error) {
924
940
}
925
941
926
942
// waitUntilReady blocks until a successful call to the "ready" method is made
927
- // on the provided client .
928
- func waitUntilReady (ctx context.Context , client call.Connection ) error {
943
+ // on the provided component .
944
+ func waitUntilReady (ctx context.Context , client call.Connection , fullComponentName string ) error {
929
945
for r := retry .Begin (); r .Continue (ctx ); {
930
- _ , err := client .Call (ctx , readyMethodKey , nil , call.CallOptions {})
946
+ _ , err := client .Call (ctx , call . MakeMethodKey ( fullComponentName , readyMethodName ) , nil , call.CallOptions {})
931
947
if err == nil || ! errors .Is (err , call .Unreachable ) {
932
948
return err
933
949
}
0 commit comments