Skip to content

Commit

Permalink
sql: add new system table for locality locations
Browse files Browse the repository at this point in the history
Adds a new table, `system.locations`, which maps a string locality
(i.e. "city=New York City") to geographic coordinates, specified as
a latitude, longitude decimal pair, specified in degrees.

A new admin API call returns the contents of the table, for use from
the admin UI.

Fixes cockroachdb#19532

Release note (ui): A system table to allow operators to designate
geographic coordinates for any locality. For use with frontend
cluster visualization.
  • Loading branch information
spencerkimball committed Dec 11, 2017
1 parent c2b7dea commit b98b431
Show file tree
Hide file tree
Showing 18 changed files with 917 additions and 194 deletions.
10 changes: 7 additions & 3 deletions pkg/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ func Example_ranges() {
// 0: node-id=1 store-id=1
///System/"tse"-"ranges3" [6]
// 0: node-id=1 store-id=1
//"ranges3"-/Table/SystemConfigSpan/Start [18]
//"ranges3"-/Table/SystemConfigSpan/Start [19]
// 0: node-id=1 store-id=1
///Table/SystemConfigSpan/Start-/Table/11 [7]
// 0: node-id=1 store-id=1
Expand All @@ -418,9 +418,11 @@ func Example_ranges() {
// 0: node-id=1 store-id=1
///Table/19-/Table/20 [16]
// 0: node-id=1 store-id=1
///Table/20-/Max [17]
///Table/20-/Table/21 [17]
// 0: node-id=1 store-id=1
//18 result(s)
///Table/21-/Max [18]
// 0: node-id=1 store-id=1
//19 result(s)

}

Expand Down Expand Up @@ -1862,11 +1864,13 @@ writing ` + os.DevNull + `
debug/nodes/1/ranges/15
debug/nodes/1/ranges/16
debug/nodes/1/ranges/17
debug/nodes/1/ranges/18
debug/schema/system@details
debug/schema/system/descriptor
debug/schema/system/eventlog
debug/schema/system/jobs
debug/schema/system/lease
debug/schema/system/locations
debug/schema/system/namespace
debug/schema/system/rangelog
debug/schema/system/settings
Expand Down
1 change: 1 addition & 0 deletions pkg/config/system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ func TestGetZoneConfigForKey(t *testing.T) {
{tkey(keys.ZonesTableID), keys.SystemDatabaseID},
{tkey(keys.LeaseTableID), keys.SystemDatabaseID},
{tkey(keys.JobsTableID), keys.SystemDatabaseID},
{tkey(keys.LocationsTableID), keys.SystemDatabaseID},
{tkey(keys.MaxReservedDescID + 1), keys.MaxReservedDescID + 1},
{tkey(keys.MaxReservedDescID + 23), keys.MaxReservedDescID + 23},
{roachpb.RKeyMax, keys.RootNamespaceID},
Expand Down
1 change: 1 addition & 0 deletions pkg/keys/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,4 +315,5 @@ const (
TimeseriesRangesID = 18
WebSessionsTableID = 19
TableStatisticsTableID = 20
LocationsTableID = 21
)
45 changes: 45 additions & 0 deletions pkg/server/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

"encoding/json"

"github.com/cockroachdb/apd"
"github.com/cockroachdb/cockroach/pkg/base"
"github.com/cockroachdb/cockroach/pkg/config"
"github.com/cockroachdb/cockroach/pkg/internal/client"
Expand Down Expand Up @@ -1088,6 +1089,43 @@ func (s *adminServer) Jobs(
return &resp, nil
}

func (s *adminServer) Locations(
ctx context.Context, req *serverpb.LocationsRequest,
) (*serverpb.LocationsResponse, error) {
args := sql.SessionArgs{User: s.getUser(req)}
ctx, session := s.NewContextAndSessionForRPC(ctx, args)
defer session.Finish(s.server.sqlExecutor)

q := makeSQLQuery()
q.Append(`SELECT "localityKey", "localityValue", latitude, longitude FROM system.locations`)
r, err := s.server.sqlExecutor.ExecuteStatementsBuffered(session, q.String(), nil, 1)
if err != nil {
return nil, s.serverError(err)
}
defer r.Close(ctx)

scanner := makeResultScanner(r.ResultList[0].Columns)
resp := serverpb.LocationsResponse{
Locations: make([]serverpb.LocationsResponse_Location, r.ResultList[0].Rows.Len()),
}
for i := 0; i < len(resp.Locations); i++ {
loc := &resp.Locations[i]
lat, lon := new(apd.Decimal), new(apd.Decimal)
if err := scanner.ScanAll(
r.ResultList[0].Rows.At(i), &loc.LocalityKey, &loc.LocalityValue, lat, lon); err != nil {
return nil, s.serverError(err)
}
if loc.Latitude, err = lat.Float64(); err != nil {
return nil, s.serverError(err)
}
if loc.Longitude, err = lon.Float64(); err != nil {
return nil, s.serverError(err)
}
}

return &resp, nil
}

// QueryPlan returns a JSON representation of a distsql physical query
// plan.
func (s *adminServer) QueryPlan(
Expand Down Expand Up @@ -1440,6 +1478,13 @@ func (rs resultScanner) ScanIndex(row tree.Datums, index int, dst interface{}) e
// Yes, this copies, but this probably isn't in the critical path.
*d = []byte(*s)

case *apd.Decimal:
s, ok := src.(*tree.DDecimal)
if !ok {
return errors.Errorf("source type assertion failed")
}
*d = s.Decimal

default:
return errors.Errorf("unimplemented type for scanCol: %T", dst)
}
Expand Down
40 changes: 40 additions & 0 deletions pkg/server/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,46 @@ func TestAdminAPIJobs(t *testing.T) {
}
}

func TestAdminAPILocations(t *testing.T) {
defer leaktest.AfterTest(t)()

s, conn, _ := serverutils.StartServer(t, base.TestServerArgs{})
defer s.Stopper().Stop(context.TODO())
sqlDB := sqlutils.MakeSQLRunner(conn)

testLocations := []struct {
localityKey string
localityValue string
latitude float64
longitude float64
}{
{"city", "Des Moines", 41.60054, -93.60911},
{"city", "New York City", 40.71427, -74.00597},
{"city", "Seattle", 47.60621, -122.33207},
}
for _, loc := range testLocations {
sqlDB.Exec(t,
`INSERT INTO system.locations ("localityKey", "localityValue", latitude, longitude) VALUES ($1, $2, $3, $4)`,
loc.localityKey, loc.localityValue, loc.latitude, loc.longitude,
)
}
var res serverpb.LocationsResponse
if err := getAdminJSONProto(s, "locations", &res); err != nil {
t.Fatal(err)
}
for i, loc := range testLocations {
expLoc := serverpb.LocationsResponse_Location{
LocalityKey: loc.localityKey,
LocalityValue: loc.localityValue,
Latitude: loc.latitude,
Longitude: loc.longitude,
}
if !reflect.DeepEqual(res.Locations[i], expLoc) {
t.Errorf("%d: expected location %v, but got %v", i, expLoc, res.Locations[i])
}
}
}

func TestAdminAPIQueryPlan(t *testing.T) {
defer leaktest.AfterTest(t)()

Expand Down
Loading

0 comments on commit b98b431

Please sign in to comment.