Skip to content

Commit 0687d2f

Browse files
Add in-country biasing for Locate V2 (#94)
* Add in-country biasing * Make test parameter explicit * Move check * Remove unnecessary line
1 parent 8ae6c47 commit 0687d2f

File tree

5 files changed

+92
-12
lines changed

5 files changed

+92
-12
lines changed

handler/handler.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type Locator interface {
5656
// LocatorV2 defines how the Nearest handler requests machines nearest to the
5757
// client.
5858
type LocatorV2 interface {
59-
Nearest(service, typ string, lat, lon float64) ([]v2.Target, []url.URL, error)
59+
Nearest(service, typ, country string, lat, lon float64) ([]v2.Target, []url.URL, error)
6060
heartbeat.StatusTracker
6161
}
6262

@@ -188,7 +188,8 @@ func (c *Client) Nearest(rw http.ResponseWriter, req *http.Request) {
188188

189189
// Find the nearest targets using the client parameters.
190190
t := req.URL.Query().Get("machine-type")
191-
targets, urls, err := c.LocatorV2.Nearest(service, t, lat, lon)
191+
country := req.Header.Get("X-AppEngine-Country")
192+
targets, urls, err := c.LocatorV2.Nearest(service, t, country, lat, lon)
192193
if err != nil {
193194
result.Error = v2.NewError("nearest", "Failed to lookup nearest machines", http.StatusInternalServerError)
194195
writeResult(rw, result.Error.Status, &result)

handler/handler_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ type fakeLocatorV2 struct {
6161
urls []url.URL
6262
}
6363

64-
func (l *fakeLocatorV2) Nearest(service, typ string, lat, lon float64) ([]v2.Target, []url.URL, error) {
64+
func (l *fakeLocatorV2) Nearest(service, typ, country string, lat, lon float64) ([]v2.Target, []url.URL, error) {
6565
if l.err != nil {
6666
return nil, nil, l.err
6767
}

heartbeat/location.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ func NewServerLocator(tracker StatusTracker) *Locator {
5454

5555
// Nearest discovers the nearest machines for the target service, using
5656
// an exponentially distributed function based on distance.
57-
func (l *Locator) Nearest(service, typ string, lat, lon float64) ([]v2.Target, []url.URL, error) {
57+
func (l *Locator) Nearest(service, typ, country string, lat, lon float64) ([]v2.Target, []url.URL, error) {
5858
// Filter.
59-
sites := filterSites(service, typ, lat, lon, l.Instances())
59+
sites := filterSites(service, typ, country, lat, lon, l.Instances())
6060

6161
// Sort.
6262
sortSites(sites)
@@ -73,7 +73,7 @@ func (l *Locator) Nearest(service, typ string, lat, lon float64) ([]v2.Target, [
7373

7474
// filterSites groups the v2.HeartbeatMessage instances into sites and returns
7575
// only those that can serve the client request.
76-
func filterSites(service, typ string, lat, lon float64, instances map[string]v2.HeartbeatMessage) []site {
76+
func filterSites(service, typ, country string, lat, lon float64, instances map[string]v2.HeartbeatMessage) []site {
7777
m := make(map[string]*site)
7878

7979
for _, v := range instances {
@@ -86,7 +86,7 @@ func filterSites(service, typ string, lat, lon float64, instances map[string]v2.
8686
s, ok := m[r.Site]
8787
if !ok {
8888
s = &site{
89-
distance: distance,
89+
distance: biasedDistance(country, r, distance),
9090
registration: *r,
9191
machines: make([]machine, 0),
9292
}
@@ -133,8 +133,6 @@ func isValidInstance(service, typ string, lat, lon float64, v v2.HeartbeatMessag
133133
return false, host.Name{}, 0
134134
}
135135

136-
// TODO(cristinaleon): Add in-country biasing for distance.
137-
// It might require implementing a reverse geocoder.
138136
distance := mathx.GetHaversineDistance(lat, lon, r.Latitude, r.Longitude)
139137
if distance > static.EarthHalfCircumferenceKm {
140138
return false, host.Name{}, 0
@@ -213,3 +211,16 @@ func getURLs(service string, registration v2.Registration) []url.URL {
213211

214212
return result
215213
}
214+
215+
func biasedDistance(country string, r *v2.Registration, distance float64) float64 {
216+
// The 'ZZ' country code is used for unknown or unspecified countries.
217+
if country == "" || country == "ZZ" {
218+
return distance
219+
}
220+
221+
if country == r.CountryCode {
222+
return distance
223+
}
224+
225+
return 2 * distance
226+
}

heartbeat/location_test.go

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ func TestNearest(t *testing.T) {
239239
name string
240240
service string
241241
typ string
242+
country string
242243
lat float64
243244
lon float64
244245
instances []v2.HeartbeatMessage
@@ -250,6 +251,7 @@ func TestNearest(t *testing.T) {
250251
name: "NDT7-any-type",
251252
service: "ndt/ndt7",
252253
typ: "",
254+
country: "US",
253255
lat: 43.1988,
254256
lon: -75.3242,
255257
expectedTargets: []v2.Target{virtualTarget, physicalTarget},
@@ -260,6 +262,7 @@ func TestNearest(t *testing.T) {
260262
name: "NDT7-physical",
261263
service: "ndt/ndt7",
262264
typ: "physical",
265+
country: "US",
263266
lat: 43.1988,
264267
lon: -75.3242,
265268
expectedTargets: []v2.Target{physicalTarget},
@@ -270,6 +273,7 @@ func TestNearest(t *testing.T) {
270273
name: "NDT7-virtual",
271274
service: "ndt/ndt7",
272275
typ: "virtual",
276+
country: "US",
273277
lat: 43.1988,
274278
lon: -75.3242,
275279
expectedTargets: []v2.Target{virtualTarget},
@@ -280,6 +284,7 @@ func TestNearest(t *testing.T) {
280284
name: "wehe",
281285
service: "wehe/replay",
282286
typ: "",
287+
country: "US",
283288
lat: 43.1988,
284289
lon: -75.3242,
285290
expectedTargets: []v2.Target{weheTarget},
@@ -305,7 +310,7 @@ func TestNearest(t *testing.T) {
305310
locator.UpdateHealth(i.Registration.Hostname, *i.Health)
306311
}
307312

308-
gotTargets, gotURLs, err := locator.Nearest(tt.service, tt.typ, tt.lat, tt.lon)
313+
gotTargets, gotURLs, err := locator.Nearest(tt.service, tt.typ, "", tt.lat, tt.lon)
309314

310315
if !reflect.DeepEqual(gotTargets, tt.expectedTargets) {
311316
t.Errorf("Nearest() targets got: %+v, want %+v", gotTargets, tt.expectedTargets)
@@ -335,6 +340,7 @@ func TestFilterSites(t *testing.T) {
335340
name string
336341
service string
337342
typ string
343+
country string
338344
lat float64
339345
lon float64
340346
expected []site
@@ -343,6 +349,7 @@ func TestFilterSites(t *testing.T) {
343349
name: "NDT7-any-type",
344350
service: "ndt/ndt7",
345351
typ: "",
352+
country: "US",
346353
lat: 43.1988,
347354
lon: -75.3242,
348355
expected: []site{virtualSite, physicalSite},
@@ -351,6 +358,7 @@ func TestFilterSites(t *testing.T) {
351358
name: "NDT7-physical",
352359
service: "ndt/ndt7",
353360
typ: "physical",
361+
country: "US",
354362
lat: 43.1988,
355363
lon: -75.3242,
356364
expected: []site{physicalSite},
@@ -359,6 +367,7 @@ func TestFilterSites(t *testing.T) {
359367
name: "NDT7-virtual",
360368
service: "ndt/ndt7",
361369
typ: "virtual",
370+
country: "US",
362371
lat: 43.1988,
363372
lon: -75.3242,
364373
expected: []site{virtualSite},
@@ -367,6 +376,7 @@ func TestFilterSites(t *testing.T) {
367376
name: "wehe",
368377
service: "wehe/replay",
369378
typ: "",
379+
country: "US",
370380
lat: 43.1988,
371381
lon: -75.3242,
372382
expected: []site{weheSite},
@@ -375,6 +385,7 @@ func TestFilterSites(t *testing.T) {
375385
name: "too-far",
376386
service: "ndt-ndt7",
377387
typ: "",
388+
country: "",
378389
lat: 1000,
379390
lon: 1000,
380391
expected: []site{},
@@ -383,7 +394,7 @@ func TestFilterSites(t *testing.T) {
383394

384395
for _, tt := range tests {
385396
t.Run(tt.name, func(t *testing.T) {
386-
got := filterSites(tt.service, tt.typ, tt.lat, tt.lon, instances)
397+
got := filterSites(tt.service, tt.typ, tt.country, tt.lat, tt.lon, instances)
387398

388399
sortSites(got)
389400
for _, v := range got {
@@ -766,3 +777,60 @@ func TestPickWithProbability(t *testing.T) {
766777
})
767778
}
768779
}
780+
781+
func TestBiasedDistance(t *testing.T) {
782+
tests := []struct {
783+
name string
784+
country string
785+
r *v2.Registration
786+
distance float64
787+
want float64
788+
}{
789+
{
790+
name: "empty-country",
791+
country: "",
792+
r: &v2.Registration{
793+
CountryCode: "foo",
794+
},
795+
distance: 100,
796+
want: 100,
797+
},
798+
{
799+
name: "unknown-country",
800+
country: "ZZ",
801+
r: &v2.Registration{
802+
CountryCode: "foo",
803+
},
804+
distance: 100,
805+
want: 100,
806+
},
807+
{
808+
name: "same-country",
809+
country: "foo",
810+
r: &v2.Registration{
811+
CountryCode: "foo",
812+
},
813+
distance: 100,
814+
want: 100,
815+
},
816+
{
817+
name: "different-country",
818+
country: "bar",
819+
r: &v2.Registration{
820+
CountryCode: "foo",
821+
},
822+
distance: 100,
823+
want: 200,
824+
},
825+
}
826+
827+
for _, tt := range tests {
828+
t.Run(tt.name, func(t *testing.T) {
829+
got := biasedDistance(tt.country, tt.r, tt.distance)
830+
831+
if got != tt.want {
832+
t.Errorf("biasedDistance() got: %f, want: %f", got, tt.want)
833+
}
834+
})
835+
}
836+
}

locatetest/locatetest.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type LocatorV2 struct {
5555
}
5656

5757
// Nearest returns the pre-configured LocatorV2 Servers or Err.
58-
func (l *LocatorV2) Nearest(service, typ string, lat, lon float64) ([]v2.Target, []url.URL, error) {
58+
func (l *LocatorV2) Nearest(service, typ, country string, lat, lon float64) ([]v2.Target, []url.URL, error) {
5959
if l.Err != nil {
6060
return nil, nil, l.Err
6161
}

0 commit comments

Comments
 (0)