Skip to content

Commit 1d13e4f

Browse files
Add -early-exit-clients command-line flag (#223)
* Add -early-exit-client command-line argument. * Make flag configurable via cloudbuild vars * Use consts for early exit parameter and value
1 parent 7696a7e commit 1d13e4f

File tree

9 files changed

+79
-13
lines changed

9 files changed

+79
-13
lines changed

cloudbuild/app.yaml.mlab-ns.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ env_variables:
4444
RATE_LIMIT_IP_MAX: {{RATE_LIMIT_IP_MAX}}
4545
PROMETHEUSX_LISTEN_ADDRESS: ':9090' # Must match one of the forwarded_ports above.
4646
PROMETHEUS_URL: 'https://prometheus-basicauth.{{PLATFORM_PROJECT}}.measurementlab.net/'
47+
EARLY_EXIT_CLIENTS: {{EARLY_EXIT_CLIENTS}}

cloudbuild/app.yaml.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ env_variables:
4343
RATE_LIMIT_IP_MAX: {{RATE_LIMIT_IP_MAX}}
4444
PROMETHEUSX_LISTEN_ADDRESS: ':9090' # Must match one of the forwarded_ports above.
4545
PROMETHEUS_URL: 'https://prometheus-basicauth.{{PLATFORM_PROJECT}}.measurementlab.net/'
46+
EARLY_EXIT_CLIENTS: {{EARLY_EXIT_CLIENTS}}

cloudbuild/cloudbuild.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ steps:
3434
-e 's/{{RATE_LIMIT_MAX}}/$_RATE_LIMIT_MAX/'
3535
-e 's/{{RATE_LIMIT_IP_INTERVAL}}/$_RATE_LIMIT_IP_INTERVAL/'
3636
-e 's/{{RATE_LIMIT_IP_MAX}}/$_RATE_LIMIT_IP_MAX/'
37+
-e 's/{{EARLY_EXIT_CLIENTS}}/$_EARLY_EXIT_CLIENTS/'
3738
app.yaml
3839
- gcloud --project $PROJECT_ID app deploy --promote app.yaml
3940
# After deploying the new service, deploy the openapi spec.
@@ -57,6 +58,7 @@ steps:
5758
-e 's/{{RATE_LIMIT_MAX}}/$_RATE_LIMIT_MAX/'
5859
-e 's/{{RATE_LIMIT_IP_INTERVAL}}/$_RATE_LIMIT_IP_INTERVAL/'
5960
-e 's/{{RATE_LIMIT_IP_MAX}}/$_RATE_LIMIT_IP_MAX/'
61+
-e 's/{{EARLY_EXIT_CLIENTS}}/$_EARLY_EXIT_CLIENTS/'
6062
app.yaml
6163
- gcloud --project $PROJECT_ID app deploy --no-promote app.yaml
6264
# After deploying the new service, deploy the openapi spec.

handler/handler.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ type Client struct {
5454
LocatorV2
5555
ClientLocator
5656
PrometheusClient
57-
targetTmpl *template.Template
58-
agentLimits limits.Agents
59-
ipLimiter Limiter
57+
targetTmpl *template.Template
58+
agentLimits limits.Agents
59+
ipLimiter Limiter
60+
earlyExitClients map[string]bool
6061
}
6162

6263
// LocatorV2 defines how the Nearest handler requests machines nearest to the
@@ -90,7 +91,12 @@ func init() {
9091

9192
// NewClient creates a new client.
9293
func NewClient(project string, private Signer, locatorV2 LocatorV2, client ClientLocator,
93-
prom PrometheusClient, lmts limits.Agents, limiter Limiter) *Client {
94+
prom PrometheusClient, lmts limits.Agents, limiter Limiter, earlyExitClients []string) *Client {
95+
// Convert slice to map for O(1) lookups
96+
earlyExitMap := make(map[string]bool)
97+
for _, client := range earlyExitClients {
98+
earlyExitMap[client] = true
99+
}
94100
return &Client{
95101
Signer: private,
96102
project: project,
@@ -100,6 +106,7 @@ func NewClient(project string, private Signer, locatorV2 LocatorV2, client Clien
100106
targetTmpl: template.Must(template.New("name").Parse("{{.Hostname}}{{.Ports}}")),
101107
agentLimits: lmts,
102108
ipLimiter: limiter,
109+
earlyExitClients: earlyExitMap,
103110
}
104111
}
105112

@@ -116,8 +123,9 @@ func NewClientDirect(project string, private Signer, locatorV2 LocatorV2, client
116123
}
117124
}
118125

119-
func extraParams(hostname string, index int, p paramOpts) url.Values {
126+
func (c *Client) extraParams(hostname string, index int, p paramOpts) url.Values {
120127
v := url.Values{}
128+
121129
// Add client parameters.
122130
for key := range p.raw {
123131
if strings.HasPrefix(key, "client_") {
@@ -131,6 +139,12 @@ func extraParams(hostname string, index int, p paramOpts) url.Values {
131139
}
132140
}
133141

142+
// Add early_exit parameter for specified clients
143+
clientName := p.raw.Get("client_name")
144+
if clientName != "" && c.earlyExitClients[clientName] {
145+
v.Set(static.EarlyExitParameter, static.EarlyExitDefaultValue)
146+
}
147+
134148
// Add Locate Service version.
135149
v.Set("locate_version", p.version)
136150

@@ -323,7 +337,7 @@ func (c *Client) checkClientLocation(rw http.ResponseWriter, req *http.Request)
323337
func (c *Client) populateURLs(targets []v2.Target, ports static.Ports, exp string, pOpts paramOpts) {
324338
for i, target := range targets {
325339
token := c.getAccessToken(target.Machine, exp)
326-
params := extraParams(target.Machine, i, pOpts)
340+
params := c.extraParams(target.Machine, i, pOpts)
327341
targets[i].URLs = c.getURLs(ports, target.Hostname, token, params)
328342
}
329343
}

handler/handler_test.go

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ func TestClient_Nearest(t *testing.T) {
338338
if tt.cl == nil {
339339
tt.cl = clientgeo.NewAppEngineLocator()
340340
}
341-
c := NewClient(tt.project, tt.signer, tt.locator, tt.cl, prom.NewAPI(nil), tt.limits, tt.ipLimiter)
341+
c := NewClient(tt.project, tt.signer, tt.locator, tt.cl, prom.NewAPI(nil), tt.limits, tt.ipLimiter, nil)
342342

343343
mux := http.NewServeMux()
344344
mux.HandleFunc("/v2/nearest/", c.Nearest)
@@ -417,7 +417,7 @@ func TestClient_Ready(t *testing.T) {
417417
}
418418
for _, tt := range tests {
419419
t.Run(tt.name, func(t *testing.T) {
420-
c := NewClient("foo", &fakeSigner{}, &fakeLocatorV2{StatusTracker: &heartbeattest.FakeStatusTracker{Err: tt.fakeErr}}, nil, nil, nil, nil)
420+
c := NewClient("foo", &fakeSigner{}, &fakeLocatorV2{StatusTracker: &heartbeattest.FakeStatusTracker{Err: tt.fakeErr}}, nil, nil, nil, nil, nil)
421421

422422
mux := http.NewServeMux()
423423
mux.HandleFunc("/ready/", c.Ready)
@@ -475,7 +475,7 @@ func TestClient_Registrations(t *testing.T) {
475475
}
476476

477477
t.Run(tt.name, func(t *testing.T) {
478-
c := NewClient("foo", &fakeSigner{}, &fakeLocatorV2{StatusTracker: fakeStatusTracker}, nil, nil, nil, nil)
478+
c := NewClient("foo", &fakeSigner{}, &fakeLocatorV2{StatusTracker: fakeStatusTracker}, nil, nil, nil, nil, nil)
479479

480480
mux := http.NewServeMux()
481481
mux.HandleFunc("/v2/siteinfo/registrations/", c.Registrations)
@@ -499,6 +499,7 @@ func TestExtraParams(t *testing.T) {
499499
hostname string
500500
index int
501501
p paramOpts
502+
client *Client
502503
earlyExitProbability float64
503504
want url.Values
504505
}{
@@ -512,13 +513,55 @@ func TestExtraParams(t *testing.T) {
512513
ranks: map[string]int{"host": 0},
513514
svcParams: map[string]float64{},
514515
},
516+
client: &Client{},
515517
want: url.Values{
516518
"client_name": []string{"client"},
517519
"locate_version": []string{"v2"},
518520
"metro_rank": []string{"0"},
519521
"index": []string{"0"},
520522
},
521523
},
524+
{
525+
name: "early-exit-client-match",
526+
hostname: "host",
527+
index: 0,
528+
p: paramOpts{
529+
raw: map[string][]string{"client_name": {"foo"}},
530+
version: "v2",
531+
ranks: map[string]int{"host": 0},
532+
svcParams: map[string]float64{},
533+
},
534+
client: &Client{
535+
earlyExitClients: map[string]bool{"foo": true},
536+
},
537+
want: url.Values{
538+
"client_name": []string{"foo"},
539+
"locate_version": []string{"v2"},
540+
"metro_rank": []string{"0"},
541+
"index": []string{"0"},
542+
"early_exit": []string{"250"},
543+
},
544+
},
545+
{
546+
name: "early-exit-client-no-match",
547+
hostname: "host",
548+
index: 0,
549+
p: paramOpts{
550+
raw: map[string][]string{"client_name": {"bar"}},
551+
version: "v2",
552+
ranks: map[string]int{"host": 0},
553+
svcParams: map[string]float64{},
554+
},
555+
client: &Client{
556+
earlyExitClients: map[string]bool{"foo": true},
557+
},
558+
want: url.Values{
559+
"client_name": []string{"bar"},
560+
"locate_version": []string{"v2"},
561+
"metro_rank": []string{"0"},
562+
"index": []string{"0"},
563+
},
564+
},
522565
{
523566
name: "no-client",
524567
hostname: "host",
@@ -626,7 +669,7 @@ func TestExtraParams(t *testing.T) {
626669
}
627670
for _, tt := range tests {
628671
t.Run(tt.name, func(t *testing.T) {
629-
got := extraParams(tt.hostname, tt.index, tt.p)
672+
got := tt.client.extraParams(tt.hostname, tt.index, tt.p)
630673
if !reflect.DeepEqual(got, tt.want) {
631674
t.Errorf("extraParams() = %v, want %v", got, tt.want)
632675
}

handler/heartbeat_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func TestClient_handleHeartbeats(t *testing.T) {
7070
func fakeClient(t heartbeat.StatusTracker) *Client {
7171
locatorv2 := fakeLocatorV2{StatusTracker: t}
7272
return NewClient("mlab-sandbox", &fakeSigner{}, &locatorv2,
73-
clientgeo.NewAppEngineLocator(), prom.NewAPI(nil), nil, nil)
73+
clientgeo.NewAppEngineLocator(), prom.NewAPI(nil), nil, nil, nil)
7474
}
7575

7676
type fakeConn struct {

handler/monitoring_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func TestClient_Monitoring(t *testing.T) {
9292
for _, tt := range tests {
9393
t.Run(tt.name, func(t *testing.T) {
9494
cl := clientgeo.NewAppEngineLocator()
95-
c := NewClient("mlab-sandbox", tt.signer, tt.locator, cl, prom.NewAPI(nil), nil, nil)
95+
c := NewClient("mlab-sandbox", tt.signer, tt.locator, cl, prom.NewAPI(nil), nil, nil, nil)
9696
rw := httptest.NewRecorder()
9797
req := httptest.NewRequest(http.MethodGet, "/v2/platform/monitoring/"+tt.path, nil)
9898
req = req.Clone(controller.SetClaim(req.Context(), tt.claim))

locate.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ var (
6060
rateLimitIPUAMax int
6161
rateLimitIPInterval time.Duration
6262
rateLimitIPMax int
63+
64+
earlyExitClients flagx.StringArray
6365
)
6466

6567
func init() {
@@ -88,6 +90,7 @@ func init() {
8890
"Max number of events in the time window for IP-only rate limiting")
8991
flag.StringVar(&rateLimitPrefix, "rate-limit-prefix", "locate:ratelimit", "Prefix for Redis keys for IP+UA rate limiting")
9092
flag.StringVar(&rateLimitRedisAddr, "rate-limit-redis-address", "", "Primary endpoint for Redis instance for rate limiting")
93+
flag.Var(&earlyExitClients, "early-exit-clients", "Client names that should receive early_exit parameter (can be specified multiple times)")
9194
// Enable logging with line numbers to trace error locations.
9295
log.SetFlags(log.LUTC | log.Llongfile)
9396
}
@@ -173,7 +176,8 @@ func main() {
173176

174177
lmts, err := limits.ParseConfig(limitsPath)
175178
rtx.Must(err, "failed to parse limits config")
176-
c := handler.NewClient(project, signer, srvLocatorV2, locators, promClient, lmts, ipLimiter)
179+
c := handler.NewClient(project, signer, srvLocatorV2, locators, promClient,
180+
lmts, ipLimiter, earlyExitClients)
177181

178182
go func() {
179183
// Check and reload db at least once a day.

static/configs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
RegistrationLoadMax = 24 * time.Hour
3131
EarthHalfCircumferenceKm = 20038
3232
EarlyExitParameter = "early_exit"
33+
EarlyExitDefaultValue = "250"
3334
MaxCwndGainParameter = "max_cwnd_gain"
3435
MaxElapsedTimeParameter = "max_elapsed_time"
3536
CountryParameter = "country"

0 commit comments

Comments
 (0)