GetProfiles should always respond with a (possibly empty) profile immediately (#2146)

When `GetProfiles` is called for a destination that does not have a service profile, the proxy-api service does not return any messages until a service profile is created for that service.  This can be interpreted as hanging, and can make it difficult to calculate response latency metrics.

Change the behavior of the API to always return a service profile message immediately.  If the service does not have a service profile, the default service profile is returned.

Signed-off-by: Alex Leong <alex@buoyant.io>
This commit is contained in:
Alex Leong 2019-01-24 15:22:14 -08:00 committed by GitHub
parent 3a5d506004
commit d542571b65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 15 deletions

View File

@ -40,12 +40,14 @@ func (l *profileListener) Stop() {
}
func (l *profileListener) Update(profile *sp.ServiceProfile) {
if profile != nil {
destinationProfile, err := profiles.ToServiceProfile(&profile.Spec)
if err != nil {
log.Error(err)
return
}
l.stream.Send(destinationProfile)
if profile == nil {
l.stream.Send(&profiles.DefaultServiceProfile)
return
}
destinationProfile, err := profiles.ToServiceProfile(&profile.Spec)
if err != nil {
log.Error(err)
return
}
l.stream.Send(destinationProfile)
}

View File

@ -177,6 +177,11 @@ var (
RetryBudget: &profiles.DefaultRetryBudget,
}
defaultPbProfile = &pb.DestinationProfile{
Routes: []*pb.Route{},
RetryBudget: &profiles.DefaultRetryBudget,
}
multipleRequestMatches = &sp.ServiceProfile{
Spec: sp.ServiceProfileSpec{
Routes: []*sp.RouteSpec{
@ -521,4 +526,23 @@ func TestProfileListener(t *testing.T) {
t.Fatalf("Expected function to be completed after the cancel()")
}
})
t.Run("Sends empty update", func(t *testing.T) {
mockGetProfileServer := &mockDestinationGetProfileServer{profilesReceived: []*pb.DestinationProfile{}}
listener := &profileListener{
stream: mockGetProfileServer,
}
listener.Update(nil)
numProfiles := len(mockGetProfileServer.profilesReceived)
if numProfiles != 1 {
t.Fatalf("Expecting [1] profile, got [%d]. Updates: %v", numProfiles, mockGetProfileServer.profilesReceived)
}
actualPbProfile := mockGetProfileServer.profilesReceived[0]
if !reflect.DeepEqual(actualPbProfile, defaultPbProfile) {
t.Fatalf("Expected profile sent to be [%v] but was [%v]", defaultPbProfile, actualPbProfile)
}
})
}

View File

@ -20,14 +20,21 @@ type profileTemplateConfig struct {
ClusterZone string
}
// DefaultRetryBudget is used for routes which do not specify one.
var DefaultRetryBudget = pb.RetryBudget{
MinRetriesPerSecond: 10,
RetryRatio: 0.2,
Ttl: &duration.Duration{
Seconds: 10,
},
}
var (
// DefaultRetryBudget is used for routes which do not specify one.
DefaultRetryBudget = pb.RetryBudget{
MinRetriesPerSecond: 10,
RetryRatio: 0.2,
Ttl: &duration.Duration{
Seconds: 10,
},
}
// DefaultServiceProfile is used for services with no service profile.
DefaultServiceProfile = pb.DestinationProfile{
Routes: []*pb.Route{},
RetryBudget: &DefaultRetryBudget,
}
)
// ToServiceProfile returns a Proxy API DestinationProfile, given a
// ServiceProfile.