{"action":"created","comment":{"url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/comments/39403772","id":39403772,"diff_hunk":"@@ -0,0 +1,209 @@\n+// Copyright 2015 The Cockroach Authors.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n+// implied. See the License for the specific language governing\n+// permissions and limitations under the License. See the AUTHORS file\n+// for names of contributors.\n+//\n+// Author: Marc Berhault (marc@cockroachlabs.com)\n+\n+package config\n+\n+import (\n+\t\"bytes\"\n+\t\"fmt\"\n+\t\"sort\"\n+\n+\t\"github.com/cockroachdb/cockroach/keys\"\n+\t\"github.com/cockroachdb/cockroach/proto\"\n+\t\"github.com/cockroachdb/cockroach/util\"\n+\t\"github.com/cockroachdb/cockroach/util/encoding\"\n+\t\"github.com/cockroachdb/cockroach/util/log\"\n+)\n+\n+var (\n+\t// DefaultZoneConfig is the default zone configuration\n+\t// used when no custom config has been specified.\n+\tDefaultZoneConfig = &ZoneConfig{\n+\t\tReplicaAttrs: []proto.Attributes{\n+\t\t\t{},\n+\t\t\t{},\n+\t\t\t{},\n+\t\t},\n+\t\tRangeMinBytes: 1048576,\n+\t\tRangeMaxBytes: 67108864,\n+\t\tGC: &GCPolicy{\n+\t\t\tTTLSeconds: 24 * 60 * 60, // 1 day\n+\t\t},\n+\t}\n+\n+\t// ZoneConfigHook is a function used to lookup a zone config given a table\n+\t// or database ID.\n+\t// This is also used by testing to simplify fake configs.\n+\tZoneConfigHook func(*SystemConfig, uint32) (*ZoneConfig, error)\n+\n+\t// TestingLargestIDHook is a function used to bypass GetLargestObjectID\n+\t// in tests.\n+\tTestingLargestIDHook func() uint32\n+\n+\t// TestingDisableTableSplits is a testing-only variable that disables\n+\t// splits of tables into separate ranges.\n+\tTestingDisableTableSplits bool\n+)\n+\n+// ObjectIDForKey returns the object ID (table or database) for 'key',\n+// or (_, false) if not within the structured key space.\n+func ObjectIDForKey(key proto.Key) (uint32, bool) {\n+\tif key.Equal(proto.KeyMax) {\n+\t\treturn 0, false\n+\t}\n+\tif key.Equal(keys.TableDataPrefix) {\n+\t\t// TODO(marc): this should eventually return SystemDatabaseID.\n+\t\treturn 0, false\n+\t}\n+\tremaining := bytes.TrimPrefix(key, keys.TableDataPrefix)\n+\tif len(remaining) == len(key) {\n+\t\t// TrimPrefix returns the input untouched if the prefix doesn't match.\n+\t\treturn 0, false\n+\t}\n+\t// Consume first encoded int.\n+\t// TODO(marc): we really need to be able to handle non-Uvarint keys.\n+\t_, id64 := encoding.DecodeUvarint(remaining)\n+\treturn uint32(id64), true\n+}\n+\n+// Get searches the kv list for 'key' and returns its\n+// raw byte value if found. ok is true only if the key is found.\n+func (s *SystemConfig) Get(key proto.Key) ([]byte, bool) {\n+\tl := len(s.Values)\n+\tindex := sort.Search(l, func(i int) bool {\n+\t\treturn bytes.Compare(s.Values[i].Key, key) >= 0\n+\t})\n+\tif index == l {\n+\t\treturn nil, false\n+\t}\n+\t// TODO(marc): I'm pretty sure a Value returned by MVCCScan can\n+\t// never be nil. Should check.\n+\treturn s.Values[index].Value.Bytes, true\n+}\n+\n+// GetLargestObjectID returns the largest object ID found in the config.\n+// This could be either a table or a database.\n+func (s *SystemConfig) GetLargestObjectID() (uint32, error) {\n+\tif TestingLargestIDHook != nil {\n+\t\treturn TestingLargestIDHook(), nil\n+\t}\n+\n+\tif len(s.Values) == 0 {\n+\t\treturn 0, fmt.Errorf(\"empty system values in config\")\n+\t}\n+\n+\t// descriptorTableID = 3\n+\t// Search for the first key after the descriptor table.\n+\t// We can't use Get as we don't mind if there is nothing after\n+\t// the descriptor table.\n+\tkey := keys.MakeTablePrefix(keys.DescriptorTableID + 1)\n+\tindex := sort.Search(len(s.Values), func(i int) bool {\n+\t\treturn bytes.Compare(s.Values[i].Key, key) >= 0\n+\t})\n+\n+\tif index == 0 {\n+\t\treturn 0, fmt.Errorf(\"descriptor table not found in system config of %d values\", len(s.Values))\n+\t}\n+\n+\tkey = s.Values[index-1].Key\n+\n+\t// Extract object ID from key.\n+\t// TODO(marc): move sql/keys.go to keys (or similar) and use a DecodeDescMetadataKey.\n+\t// We should also check proper encoding.\n+\tdescriptorPrefix := keys.MakeTablePrefix(keys.DescriptorTableID)\n+\tremaining := bytes.TrimPrefix(key, descriptorPrefix)\n+\t// TrimPrefix returns the bytes unchanged if the prefix does not match.\n+\tif len(remaining) == len(key) {\n+\t\treturn 0, fmt.Errorf(\"descriptor table not found in system config of %d values\", len(s.Values))\n+\t}\n+\t// DescriptorTable.PrimaryIndex.ID\n+\tremaining, _ = encoding.DecodeUvarint(remaining)\n+\t// descID\n+\t_, id := encoding.DecodeUvarint(remaining)\n+\treturn uint32(id), nil\n+}\n+\n+// GetZoneConfigForKey looks up the zone config for the range containing 'key'.\n+// It is the caller's responsibility to ensure that the range does not need to be split.\n+func (s *SystemConfig) GetZoneConfigForKey(key proto.Key) (*ZoneConfig, error) {\n+\tif objectID, ok := ObjectIDForKey(key); ok {\n+\t\treturn s.GetZoneConfigForID(objectID)\n+\t}\n+\t// Not in the structured data namespace.\n+\treturn DefaultZoneConfig, nil\n+}\n+\n+// GetZoneConfigForID looks up the zone config for the object (table or database)\n+// with 'id'.\n+func (s *SystemConfig) GetZoneConfigForID(id uint32) (*ZoneConfig, error) {\n+\tif ZoneConfigHook == nil {\n+\t\treturn nil, util.Errorf(\"ZoneConfigHook not set, unable to lookup zone config\")\n+\t}\n+\t// For now, only user databases and tables get custom zone configs.\n+\tif id <= keys.MaxReservedDescID {\n+\t\treturn DefaultZoneConfig, nil\n+\t}\n+\treturn ZoneConfigHook(s, id)\n+}\n+\n+// ComputeSplitKeys takes a start and end key and returns an array of keys\n+// at which to split the span [start, end).\n+// The only required splits are at each user table prefix.\n+func (s *SystemConfig) ComputeSplitKeys(startKey, endKey proto.Key) []proto.Key {\n+\tif TestingDisableTableSplits {\n+\t\treturn nil\n+\t}\n+\n+\ttableStart := proto.Key(keys.UserTableDataMin)\n+\tif !tableStart.Less(endKey) {\n+\t\t// This range is before the user tables span: no required splits.\n+\t\treturn nil\n+\t}\n+\n+\tstartID, ok := ObjectIDForKey(startKey)\n+\tif !ok || startID <= keys.MaxReservedDescID {\n+\t\t// Start key is not in the structured data space, or part of the\n+\t\t// system space, use the first non-reserved ID.\n+\t\tstartID = keys.MaxReservedDescID + 1\n+\t}\n+\n+\t// Find the largest object ID.\n+\tendID, err := s.GetLargestObjectID()","path":"config/config.go","position":185,"original_position":185,"commit_id":"d07f87a7b532c3d9a4a0f38ecc05efb9afe35203","original_commit_id":"d07f87a7b532c3d9a4a0f38ecc05efb9afe35203","user":{"login":"petermattis","id":6201335,"avatar_url":"https://avatars.githubusercontent.com/u/6201335?v=3","gravatar_id":"","url":"https://api.github.com/users/petermattis","html_url":"https://github.com/petermattis","followers_url":"https://api.github.com/users/petermattis/followers","following_url":"https://api.github.com/users/petermattis/following{/other_user}","gists_url":"https://api.github.com/users/petermattis/gists{/gist_id}","starred_url":"https://api.github.com/users/petermattis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petermattis/subscriptions","organizations_url":"https://api.github.com/users/petermattis/orgs","repos_url":"https://api.github.com/users/petermattis/repos","events_url":"https://api.github.com/users/petermattis/events{/privacy}","received_events_url":"https://api.github.com/users/petermattis/received_events","type":"User","site_admin":false},"body":"Why aren't you finding the `ObjectID` of the `endKey`? Using the largest object ID in the system seems unnecessary. I'm probably missing something which means an explanatory comment is in order.","created_at":"2015-09-14T14:58:41Z","updated_at":"2015-09-14T14:58:41Z","html_url":"https://github.com/cockroachdb/cockroach/pull/2495#discussion_r39403772","pull_request_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/2495","_links":{"self":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/comments/39403772"},"html":{"href":"https://github.com/cockroachdb/cockroach/pull/2495#discussion_r39403772"},"pull_request":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/2495"}}},"pull_request":{"url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/2495","id":44927676,"html_url":"https://github.com/cockroachdb/cockroach/pull/2495","diff_url":"https://github.com/cockroachdb/cockroach/pull/2495.diff","patch_url":"https://github.com/cockroachdb/cockroach/pull/2495.patch","issue_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/2495","number":2495,"state":"open","locked":false,"title":"Use zone configs from system table.","user":{"login":"mberhault","id":5901480,"avatar_url":"https://avatars.githubusercontent.com/u/5901480?v=3","gravatar_id":"","url":"https://api.github.com/users/mberhault","html_url":"https://github.com/mberhault","followers_url":"https://api.github.com/users/mberhault/followers","following_url":"https://api.github.com/users/mberhault/following{/other_user}","gists_url":"https://api.github.com/users/mberhault/gists{/gist_id}","starred_url":"https://api.github.com/users/mberhault/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mberhault/subscriptions","organizations_url":"https://api.github.com/users/mberhault/orgs","repos_url":"https://api.github.com/users/mberhault/repos","events_url":"https://api.github.com/users/mberhault/events{/privacy}","received_events_url":"https://api.github.com/users/mberhault/received_events","type":"User","site_admin":false},"body":"Fixes #2090 (make configs aware of structured layout)\r\nFixes #2179 (sync system DB across nodes)\r\nFixes #2272 (split at table boundaries)\r\n\r\nThis switches all zone configs from the old-school prefix-based one to\r\nthe system.zones table.\r\n\r\nBasic summary:\r\n* the systemdb span (all system tables) gets gossiped as\r\n  config.SystemConfig\r\n* config.SystemConfig has two basic methods: ComputeSplitKeys(start,\r\n  end) and GetZoneConfigForKey(range start key)\r\n* we split at every user object ID (database or table) below the\r\n  currently allocated one\r\n* if a table does not have a zone config, we use its databases's config.\r\n* if a database does not have a zone config, we use the hard-coded\r\n  default.\r\n\r\nSome likely changes in the very near future:\r\n* pre-build list of actual zones from existing tables (not databases)\r\n  and only split on those.\r\n* allow setting the system span config\r\n* allow setting the non-structured span config (probably a reserved ID)\r\n\r\nRemaining TODOs for this PR:\r\n* failing: no idea why: storage/gc_queue_test.go TestGCQueueIntentResolution\r\n* need to port: kv/split_test.go TestRangeSplitsWithWritePressure\r\n* failing kv and client tests with --race --cpu 1. no clue why.\r\n* port cli zones commands\r\n* complete tests on config/config and sql/config","created_at":"2015-09-14T13:09:46Z","updated_at":"2015-09-14T14:58:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c38b2254ce4c77f5f3c566d52c34fbb89478bdee","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/2495/commits","review_comments_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/2495/comments","review_comment_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/2495/comments","statuses_url":"https://api.github.com/repos/cockroachdb/cockroach/statuses/d07f87a7b532c3d9a4a0f38ecc05efb9afe35203","head":{"label":"cockroachdb:marc/zones_from_system_config","ref":"marc/zones_from_system_config","sha":"d07f87a7b532c3d9a4a0f38ecc05efb9afe35203","user":{"login":"cockroachdb","id":6748139,"avatar_url":"https://avatars.githubusercontent.com/u/6748139?v=3","gravatar_id":"","url":"https://api.github.com/users/cockroachdb","html_url":"https://github.com/cockroachdb","followers_url":"https://api.github.com/users/cockroachdb/followers","following_url":"https://api.github.com/users/cockroachdb/following{/other_user}","gists_url":"https://api.github.com/users/cockroachdb/gists{/gist_id}","starred_url":"https://api.github.com/users/cockroachdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cockroachdb/subscriptions","organizations_url":"https://api.github.com/users/cockroachdb/orgs","repos_url":"https://api.github.com/users/cockroachdb/repos","events_url":"https://api.github.com/users/cockroachdb/events{/privacy}","received_events_url":"https://api.github.com/users/cockroachdb/received_events","type":"Organization","site_admin":false},"repo":{"id":16563587,"name":"cockroach","full_name":"cockroachdb/cockroach","owner":{"login":"cockroachdb","id":6748139,"avatar_url":"https://avatars.githubusercontent.com/u/6748139?v=3","gravatar_id":"","url":"https://api.github.com/users/cockroachdb","html_url":"https://github.com/cockroachdb","followers_url":"https://api.github.com/users/cockroachdb/followers","following_url":"https://api.github.com/users/cockroachdb/following{/other_user}","gists_url":"https://api.github.com/users/cockroachdb/gists{/gist_id}","starred_url":"https://api.github.com/users/cockroachdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cockroachdb/subscriptions","organizations_url":"https://api.github.com/users/cockroachdb/orgs","repos_url":"https://api.github.com/users/cockroachdb/repos","events_url":"https://api.github.com/users/cockroachdb/events{/privacy}","received_events_url":"https://api.github.com/users/cockroachdb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cockroachdb/cockroach","description":"A Scalable, Geo-Replicated, Transactional Datastore","fork":false,"url":"https://api.github.com/repos/cockroachdb/cockroach","forks_url":"https://api.github.com/repos/cockroachdb/cockroach/forks","keys_url":"https://api.github.com/repos/cockroachdb/cockroach/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cockroachdb/cockroach/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cockroachdb/cockroach/teams","hooks_url":"https://api.github.com/repos/cockroachdb/cockroach/hooks","issue_events_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/events{/number}","events_url":"https://api.github.com/repos/cockroachdb/cockroach/events","assignees_url":"https://api.github.com/repos/cockroachdb/cockroach/assignees{/user}","branches_url":"https://api.github.com/repos/cockroachdb/cockroach/branches{/branch}","tags_url":"https://api.github.com/repos/cockroachdb/cockroach/tags","blobs_url":"https://api.github.com/repos/cockroachdb/cockroach/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cockroachdb/cockroach/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cockroachdb/cockroach/git/refs{/sha}","trees_url":"https://api.github.com/repos/cockroachdb/cockroach/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cockroachdb/cockroach/statuses/{sha}","languages_url":"https://api.github.com/repos/cockroachdb/cockroach/languages","stargazers_url":"https://api.github.com/repos/cockroachdb/cockroach/stargazers","contributors_url":"https://api.github.com/repos/cockroachdb/cockroach/contributors","subscribers_url":"https://api.github.com/repos/cockroachdb/cockroach/subscribers","subscription_url":"https://api.github.com/repos/cockroachdb/cockroach/subscription","commits_url":"https://api.github.com/repos/cockroachdb/cockroach/commits{/sha}","git_commits_url":"https://api.github.com/repos/cockroachdb/cockroach/git/commits{/sha}","comments_url":"https://api.github.com/repos/cockroachdb/cockroach/comments{/number}","issue_comment_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/comments{/number}","contents_url":"https://api.github.com/repos/cockroachdb/cockroach/contents/{+path}","compare_url":"https://api.github.com/repos/cockroachdb/cockroach/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cockroachdb/cockroach/merges","archive_url":"https://api.github.com/repos/cockroachdb/cockroach/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cockroachdb/cockroach/downloads","issues_url":"https://api.github.com/repos/cockroachdb/cockroach/issues{/number}","pulls_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls{/number}","milestones_url":"https://api.github.com/repos/cockroachdb/cockroach/milestones{/number}","notifications_url":"https://api.github.com/repos/cockroachdb/cockroach/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cockroachdb/cockroach/labels{/name}","releases_url":"https://api.github.com/repos/cockroachdb/cockroach/releases{/id}","created_at":"2014-02-06T00:18:47Z","updated_at":"2015-09-14T14:26:19Z","pushed_at":"2015-09-14T13:09:47Z","git_url":"git://github.com/cockroachdb/cockroach.git","ssh_url":"git@github.com:cockroachdb/cockroach.git","clone_url":"https://github.com/cockroachdb/cockroach.git","svn_url":"https://github.com/cockroachdb/cockroach","homepage":null,"size":53982,"stargazers_count":5247,"watchers_count":5247,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":425,"mirror_url":null,"open_issues_count":177,"forks":425,"open_issues":177,"watchers":5247,"default_branch":"master"}},"base":{"label":"cockroachdb:master","ref":"master","sha":"bd8b2de68d85ae6af495826f17f8daae48d2f06d","user":{"login":"cockroachdb","id":6748139,"avatar_url":"https://avatars.githubusercontent.com/u/6748139?v=3","gravatar_id":"","url":"https://api.github.com/users/cockroachdb","html_url":"https://github.com/cockroachdb","followers_url":"https://api.github.com/users/cockroachdb/followers","following_url":"https://api.github.com/users/cockroachdb/following{/other_user}","gists_url":"https://api.github.com/users/cockroachdb/gists{/gist_id}","starred_url":"https://api.github.com/users/cockroachdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cockroachdb/subscriptions","organizations_url":"https://api.github.com/users/cockroachdb/orgs","repos_url":"https://api.github.com/users/cockroachdb/repos","events_url":"https://api.github.com/users/cockroachdb/events{/privacy}","received_events_url":"https://api.github.com/users/cockroachdb/received_events","type":"Organization","site_admin":false},"repo":{"id":16563587,"name":"cockroach","full_name":"cockroachdb/cockroach","owner":{"login":"cockroachdb","id":6748139,"avatar_url":"https://avatars.githubusercontent.com/u/6748139?v=3","gravatar_id":"","url":"https://api.github.com/users/cockroachdb","html_url":"https://github.com/cockroachdb","followers_url":"https://api.github.com/users/cockroachdb/followers","following_url":"https://api.github.com/users/cockroachdb/following{/other_user}","gists_url":"https://api.github.com/users/cockroachdb/gists{/gist_id}","starred_url":"https://api.github.com/users/cockroachdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cockroachdb/subscriptions","organizations_url":"https://api.github.com/users/cockroachdb/orgs","repos_url":"https://api.github.com/users/cockroachdb/repos","events_url":"https://api.github.com/users/cockroachdb/events{/privacy}","received_events_url":"https://api.github.com/users/cockroachdb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/cockroachdb/cockroach","description":"A Scalable, Geo-Replicated, Transactional Datastore","fork":false,"url":"https://api.github.com/repos/cockroachdb/cockroach","forks_url":"https://api.github.com/repos/cockroachdb/cockroach/forks","keys_url":"https://api.github.com/repos/cockroachdb/cockroach/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cockroachdb/cockroach/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cockroachdb/cockroach/teams","hooks_url":"https://api.github.com/repos/cockroachdb/cockroach/hooks","issue_events_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/events{/number}","events_url":"https://api.github.com/repos/cockroachdb/cockroach/events","assignees_url":"https://api.github.com/repos/cockroachdb/cockroach/assignees{/user}","branches_url":"https://api.github.com/repos/cockroachdb/cockroach/branches{/branch}","tags_url":"https://api.github.com/repos/cockroachdb/cockroach/tags","blobs_url":"https://api.github.com/repos/cockroachdb/cockroach/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cockroachdb/cockroach/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cockroachdb/cockroach/git/refs{/sha}","trees_url":"https://api.github.com/repos/cockroachdb/cockroach/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cockroachdb/cockroach/statuses/{sha}","languages_url":"https://api.github.com/repos/cockroachdb/cockroach/languages","stargazers_url":"https://api.github.com/repos/cockroachdb/cockroach/stargazers","contributors_url":"https://api.github.com/repos/cockroachdb/cockroach/contributors","subscribers_url":"https://api.github.com/repos/cockroachdb/cockroach/subscribers","subscription_url":"https://api.github.com/repos/cockroachdb/cockroach/subscription","commits_url":"https://api.github.com/repos/cockroachdb/cockroach/commits{/sha}","git_commits_url":"https://api.github.com/repos/cockroachdb/cockroach/git/commits{/sha}","comments_url":"https://api.github.com/repos/cockroachdb/cockroach/comments{/number}","issue_comment_url":"https://api.github.com/repos/cockroachdb/cockroach/issues/comments{/number}","contents_url":"https://api.github.com/repos/cockroachdb/cockroach/contents/{+path}","compare_url":"https://api.github.com/repos/cockroachdb/cockroach/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cockroachdb/cockroach/merges","archive_url":"https://api.github.com/repos/cockroachdb/cockroach/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cockroachdb/cockroach/downloads","issues_url":"https://api.github.com/repos/cockroachdb/cockroach/issues{/number}","pulls_url":"https://api.github.com/repos/cockroachdb/cockroach/pulls{/number}","milestones_url":"https://api.github.com/repos/cockroachdb/cockroach/milestones{/number}","notifications_url":"https://api.github.com/repos/cockroachdb/cockroach/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cockroachdb/cockroach/labels{/name}","releases_url":"https://api.github.com/repos/cockroachdb/cockroach/releases{/id}","created_at":"2014-02-06T00:18:47Z","updated_at":"2015-09-14T14:26:19Z","pushed_at":"2015-09-14T13:09:47Z","git_url":"git://github.com/cockroachdb/cockroach.git","ssh_url":"git@github.com:cockroachdb/cockroach.git","clone_url":"https://github.com/cockroachdb/cockroach.git","svn_url":"https://github.com/cockroachdb/cockroach","homepage":null,"size":53982,"stargazers_count":5247,"watchers_count":5247,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":425,"mirror_url":null,"open_issues_count":177,"forks":425,"open_issues":177,"watchers":5247,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/2495"},"html":{"href":"https://github.com/cockroachdb/cockroach/pull/2495"},"issue":{"href":"https://api.github.com/repos/cockroachdb/cockroach/issues/2495"},"comments":{"href":"https://api.github.com/repos/cockroachdb/cockroach/issues/2495/comments"},"review_comments":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/2495/comments"},"review_comment":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cockroachdb/cockroach/pulls/2495/commits"},"statuses":{"href":"https://api.github.com/repos/cockroachdb/cockroach/statuses/d07f87a7b532c3d9a4a0f38ecc05efb9afe35203"}}}}