{"action":"created","comment":{"url":"https://api.github.com/repos/go-redis/redis/pulls/comments/26114021","id":26114021,"diff_hunk":"@@ -0,0 +1,464 @@\n+package redis\n+\n+import (\n+\t\"container/list\"\n+\t\"errors\"\n+\t\"io\"\n+\t\"math/rand\"\n+\t\"net\"\n+\t\"strings\"\n+\t\"sync\"\n+\t\"sync/atomic\"\n+\t\"time\"\n+)\n+\n+type ClusterClient struct {\n+\tcommandable\n+\n+\taddrs []string\n+\tslots [][]string\n+\tconns *lruPool\n+\topt   *ClusterOptions\n+\n+\tcachemx sync.RWMutex\n+\t_reload uint32\n+}\n+\n+func NewClusterClient(opt *ClusterOptions) (*ClusterClient, error) {\n+\taddrs, err := opt.getAddrs()\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\tclient := &ClusterClient{\n+\t\taddrs:   addrs,\n+\t\tconns:   newLRUPool(opt),\n+\t\topt:     opt,\n+\t\t_reload: 1,\n+\t}\n+\tclient.commandable.process = client.process\n+\tclient.reloadIfDue()\n+\treturn client, nil\n+}\n+\n+// Closes the pool\n+func (c *ClusterClient) Close() error {\n+\tc.cachemx.Lock()\n+\tdefer c.cachemx.Unlock()\n+\n+\treturn c.reset()\n+}\n+\n+// GetMasterAddrBySlot finds the current master address for a given hash slot\n+func (c *ClusterClient) GetMasterAddrBySlot(hashSlot int) string {\n+\tif addrs := c.slots[hashSlot]; len(addrs) > 0 {\n+\t\treturn addrs[0]\n+\t}\n+\treturn \"\"\n+}\n+\n+// GetSlaveAddrsBySlot finds the current slave addresses for a given hash slot\n+func (c *ClusterClient) GetSlaveAddrsBySlot(hashSlot int) []string {\n+\tif addrs := c.slots[hashSlot]; len(addrs) > 0 {\n+\t\treturn addrs[1:]\n+\t}\n+\treturn []string{}\n+}\n+\n+// GetNodeClientByAddr returns a node's client for a given address\n+func (c *ClusterClient) GetNodeClientByAddr(addr string) *Client {\n+\treturn c.conns.Fetch(addr)\n+}\n+\n+// ------------------------------------------------------------------------\n+\n+func (c *ClusterClient) process(cmd Cmder) {\n+\tvar hashSlot int\n+\n+\tc.reloadIfDue()\n+\task := false\n+\n+\tkey := cmd.firstKey()\n+\tif key != \"\" {\n+\t\thashSlot = HashSlot(key)\n+\t} else {\n+\t\thashSlot = rand.Intn(HashSlots)\n+\t}\n+\n+\tc.cachemx.RLock()\n+\tdefer c.cachemx.RUnlock()\n+\n+\ttried := make(map[string]struct{}, len(c.addrs))\n+\taddr := c.GetMasterAddrBySlot(hashSlot)\n+\tfor attempt := 0; attempt < c.opt.getMaxRedirects(); attempt++ {\n+\t\ttried[addr] = struct{}{}\n+\n+\t\t// Pick the connection, process request\n+\t\tconn := c.GetNodeClientByAddr(addr)\n+\t\tif ask {\n+\t\t\tpipe := conn.Pipeline()\n+\t\t\tpipe.Process(NewCmd(\"ASKING\"))\n+\t\t\tpipe.Process(cmd)\n+\t\t\t_, _ = pipe.Exec()\n+\t\t\task = false\n+\t\t} else {\n+\t\t\tconn.Process(cmd)\n+\t\t}\n+\n+\t\t// If there is no (real) error, we are done!\n+\t\terr := cmd.Err()\n+\t\tif err == nil || err == Nil {\n+\t\t\treturn\n+\t\t}\n+\n+\t\t// On connection errors, pick the next (not previosuly) tried connection\n+\t\t// and try again\n+\t\tif _, ok := err.(*net.OpError); ok || err == io.EOF {\n+\t\t\tif addr = c.next(tried); addr == \"\" {\n+\t\t\t\treturn\n+\t\t\t}\n+\t\t\tcmd.reset()\n+\t\t\tcontinue\n+\t\t}\n+\n+\t\t// Check the error message, return if unexpected\n+\t\tparts := strings.SplitN(err.Error(), \" \", 3)\n+\t\tif len(parts) != 3 {\n+\t\t\treturn\n+\t\t}\n+\n+\t\t// Handle MOVE and ASK redirections, return on any other error\n+\t\tswitch parts[0] {\n+\t\tcase \"MOVED\":\n+\t\t\tc.forceReload()\n+\t\t\taddr = parts[2]\n+\t\tcase \"ASK\":\n+\t\t\task = true\n+\t\t\taddr = parts[2]\n+\t\tdefault:\n+\t\t\treturn\n+\t\t}\n+\t\tcmd.reset()\n+\t}\n+}\n+\n+// Closes all connections and reloads slot cache, if due\n+func (c *ClusterClient) reloadIfDue() (err error) {\n+\tif !atomic.CompareAndSwapUint32(&c._reload, 1, 0) {\n+\t\treturn\n+\t}\n+\n+\tvar infos []ClusterSlotInfo\n+\n+\tc.cachemx.Lock()\n+\tdefer c.cachemx.Unlock()\n+\n+\tfor _, addr := range c.addrs {\n+\t\tc.reset()\n+\n+\t\tinfos, err = c.fetch(addr)\n+\t\tif err == nil {\n+\t\t\tc.update(infos)\n+\t\t\tbreak\n+\t\t}\n+\t}\n+\treturn\n+}\n+\n+// Closes all connections and flushes slots cache\n+func (c *ClusterClient) reset() error {\n+\terr := c.conns.Clear()\n+\tc.slots = make([][]string, HashSlots)\n+\treturn err\n+}\n+\n+// Forces a cache reload on next request\n+func (c *ClusterClient) forceReload() {\n+\tatomic.StoreUint32(&c._reload, 1)\n+}\n+\n+// Find the next unseen address\n+func (c *ClusterClient) next(seen map[string]struct{}) string {\n+\tfor _, addr := range c.addrs {\n+\t\tif _, ok := seen[addr]; !ok {\n+\t\t\treturn addr\n+\t\t}\n+\t}\n+\treturn \"\"\n+}\n+\n+// Fetch slot information\n+func (c *ClusterClient) fetch(addr string) ([]ClusterSlotInfo, error) {\n+\tclient := NewClient(c.opt.ClientOptions(addr))\n+\tdefer client.Close()\n+\n+\treturn client.ClusterSlots().Result()\n+}\n+\n+// Update slot information\n+func (c *ClusterClient) update(infos []ClusterSlotInfo) {\n+\n+\t// Create a map of known nodes\n+\tknown := make(map[string]struct{}, len(c.addrs))\n+\tfor _, addr := range c.addrs {\n+\t\tknown[addr] = struct{}{}\n+\t}\n+\n+\t// Populate slots, store unknown nodes\n+\tfor _, info := range infos {\n+\t\tfor i := info.Min; i <= info.Max; i++ {\n+\t\t\tc.slots[i] = info.Addrs\n+\t\t}\n+\n+\t\tfor _, addr := range info.Addrs {\n+\t\t\tif _, ok := known[addr]; !ok {\n+\t\t\t\tc.addrs = append(c.addrs, addr)\n+\t\t\t\tknown[addr] = struct{}{}\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t// Shuffle addresses\n+\tfor i := range c.addrs {\n+\t\tj := rand.Intn(i + 1)\n+\t\tc.addrs[i], c.addrs[j] = c.addrs[j], c.addrs[i]\n+\t}\n+}\n+\n+//------------------------------------------------------------------------------\n+\n+var errNoAddrs = errors.New(\"redis: no addresses\")\n+\n+type ClusterOptions struct {\n+\t// A seed-list of host:port addresses of known cluster nodes\n+\tAddrs []string\n+\n+\t// An optional password\n+\tPassword string\n+\n+\t// The maximum number of MOVED/ASK redirects to follow, before\n+\t// giving up. Default: 16\n+\tMaxRedirects int\n+\n+\t// The maximum number of open connections to cluster nodes. Default: 10.\n+\t// WARNING: Each connection maintains its own connection pool. The maximum\n+\t// possible number of socket connections is therefore MaxConns x PoolSize\n+\tMaxConns int\n+\n+\t// The maximum number of TCP sockets per connection. Default: 5\n+\tPoolSize int\n+\n+\t// Timeout settings\n+\tDialTimeout, ReadTimeout, WriteTimeout, IdleTimeout time.Duration\n+}\n+\n+func (opt *ClusterOptions) getPoolSize() int {\n+\tif opt.PoolSize < 1 {\n+\t\treturn 5\n+\t}\n+\treturn opt.PoolSize\n+}\n+\n+func (opt *ClusterOptions) getMaxConns() int {\n+\tif opt.MaxConns < 1 {\n+\t\treturn 10\n+\t}\n+\treturn opt.PoolSize\n+}\n+\n+func (opt *ClusterOptions) getDialTimeout() time.Duration {\n+\tif opt.DialTimeout == 0 {\n+\t\treturn 5 * time.Second\n+\t}\n+\treturn opt.DialTimeout\n+}\n+\n+func (opt *ClusterOptions) getMaxRedirects() int {\n+\tif opt.MaxRedirects < 1 {\n+\t\treturn 16\n+\t}\n+\treturn opt.MaxRedirects\n+}\n+\n+func (opt *ClusterOptions) getAddrs() ([]string, error) {\n+\tif len(opt.Addrs) < 1 {\n+\t\treturn nil, errNoAddrs\n+\t}\n+\treturn opt.Addrs, nil\n+}\n+\n+func (opt *ClusterOptions) ClientOptions(addr string) *Options {","path":"cluster.go","position":290,"original_position":290,"commit_id":"641a75e0f1753268ef9e38e7a9517b5ea8c28019","original_commit_id":"641a75e0f1753268ef9e38e7a9517b5ea8c28019","user":{"login":"dim","id":16614,"avatar_url":"https://avatars.githubusercontent.com/u/16614?v=3","gravatar_id":"","url":"https://api.github.com/users/dim","html_url":"https://github.com/dim","followers_url":"https://api.github.com/users/dim/followers","following_url":"https://api.github.com/users/dim/following{/other_user}","gists_url":"https://api.github.com/users/dim/gists{/gist_id}","starred_url":"https://api.github.com/users/dim/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dim/subscriptions","organizations_url":"https://api.github.com/users/dim/orgs","repos_url":"https://api.github.com/users/dim/repos","events_url":"https://api.github.com/users/dim/events{/privacy}","received_events_url":"https://api.github.com/users/dim/received_events","type":"User","site_admin":false},"body":"Because we required it in one of our projects :)","created_at":"2015-03-10T11:32:17Z","updated_at":"2015-03-10T11:32:17Z","html_url":"https://github.com/go-redis/redis/pull/71#discussion_r26114021","pull_request_url":"https://api.github.com/repos/go-redis/redis/pulls/71","_links":{"self":{"href":"https://api.github.com/repos/go-redis/redis/pulls/comments/26114021"},"html":{"href":"https://github.com/go-redis/redis/pull/71#discussion_r26114021"},"pull_request":{"href":"https://api.github.com/repos/go-redis/redis/pulls/71"}}},"pull_request":{"url":"https://api.github.com/repos/go-redis/redis/pulls/71","id":30718227,"html_url":"https://github.com/go-redis/redis/pull/71","diff_url":"https://github.com/go-redis/redis/pull/71.diff","patch_url":"https://github.com/go-redis/redis/pull/71.patch","issue_url":"https://api.github.com/repos/go-redis/redis/issues/71","number":71,"state":"open","locked":false,"title":"Cluster intergration","user":{"login":"dim","id":16614,"avatar_url":"https://avatars.githubusercontent.com/u/16614?v=3","gravatar_id":"","url":"https://api.github.com/users/dim","html_url":"https://github.com/dim","followers_url":"https://api.github.com/users/dim/followers","following_url":"https://api.github.com/users/dim/following{/other_user}","gists_url":"https://api.github.com/users/dim/gists{/gist_id}","starred_url":"https://api.github.com/users/dim/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dim/subscriptions","organizations_url":"https://api.github.com/users/dim/orgs","repos_url":"https://api.github.com/users/dim/repos","events_url":"https://api.github.com/users/dim/events{/privacy}","received_events_url":"https://api.github.com/users/dim/received_events","type":"User","site_admin":false},"body":"This is a my attempt at integrating cluster features. I tried to keep the API consistent and make as few changes as possible. Looking forward to your review.","created_at":"2015-03-08T11:26:05Z","updated_at":"2015-03-10T11:32:17Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0b79298b45fe94ca62b83168a7c53a3f9373b8fc","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/go-redis/redis/pulls/71/commits","review_comments_url":"https://api.github.com/repos/go-redis/redis/pulls/71/comments","review_comment_url":"https://api.github.com/repos/go-redis/redis/pulls/comments{/number}","comments_url":"https://api.github.com/repos/go-redis/redis/issues/71/comments","statuses_url":"https://api.github.com/repos/go-redis/redis/statuses/641a75e0f1753268ef9e38e7a9517b5ea8c28019","head":{"label":"go-redis:feature/cluster","ref":"feature/cluster","sha":"641a75e0f1753268ef9e38e7a9517b5ea8c28019","user":{"login":"go-redis","id":7314137,"avatar_url":"https://avatars.githubusercontent.com/u/7314137?v=3","gravatar_id":"","url":"https://api.github.com/users/go-redis","html_url":"https://github.com/go-redis","followers_url":"https://api.github.com/users/go-redis/followers","following_url":"https://api.github.com/users/go-redis/following{/other_user}","gists_url":"https://api.github.com/users/go-redis/gists{/gist_id}","starred_url":"https://api.github.com/users/go-redis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-redis/subscriptions","organizations_url":"https://api.github.com/users/go-redis/orgs","repos_url":"https://api.github.com/users/go-redis/repos","events_url":"https://api.github.com/users/go-redis/events{/privacy}","received_events_url":"https://api.github.com/users/go-redis/received_events","type":"Organization","site_admin":false},"repo":{"id":5179099,"name":"redis","full_name":"go-redis/redis","owner":{"login":"go-redis","id":7314137,"avatar_url":"https://avatars.githubusercontent.com/u/7314137?v=3","gravatar_id":"","url":"https://api.github.com/users/go-redis","html_url":"https://github.com/go-redis","followers_url":"https://api.github.com/users/go-redis/followers","following_url":"https://api.github.com/users/go-redis/following{/other_user}","gists_url":"https://api.github.com/users/go-redis/gists{/gist_id}","starred_url":"https://api.github.com/users/go-redis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-redis/subscriptions","organizations_url":"https://api.github.com/users/go-redis/orgs","repos_url":"https://api.github.com/users/go-redis/repos","events_url":"https://api.github.com/users/go-redis/events{/privacy}","received_events_url":"https://api.github.com/users/go-redis/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/go-redis/redis","description":"Redis client for Golang.","fork":false,"url":"https://api.github.com/repos/go-redis/redis","forks_url":"https://api.github.com/repos/go-redis/redis/forks","keys_url":"https://api.github.com/repos/go-redis/redis/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-redis/redis/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-redis/redis/teams","hooks_url":"https://api.github.com/repos/go-redis/redis/hooks","issue_events_url":"https://api.github.com/repos/go-redis/redis/issues/events{/number}","events_url":"https://api.github.com/repos/go-redis/redis/events","assignees_url":"https://api.github.com/repos/go-redis/redis/assignees{/user}","branches_url":"https://api.github.com/repos/go-redis/redis/branches{/branch}","tags_url":"https://api.github.com/repos/go-redis/redis/tags","blobs_url":"https://api.github.com/repos/go-redis/redis/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-redis/redis/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-redis/redis/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-redis/redis/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-redis/redis/statuses/{sha}","languages_url":"https://api.github.com/repos/go-redis/redis/languages","stargazers_url":"https://api.github.com/repos/go-redis/redis/stargazers","contributors_url":"https://api.github.com/repos/go-redis/redis/contributors","subscribers_url":"https://api.github.com/repos/go-redis/redis/subscribers","subscription_url":"https://api.github.com/repos/go-redis/redis/subscription","commits_url":"https://api.github.com/repos/go-redis/redis/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-redis/redis/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-redis/redis/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-redis/redis/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-redis/redis/contents/{+path}","compare_url":"https://api.github.com/repos/go-redis/redis/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-redis/redis/merges","archive_url":"https://api.github.com/repos/go-redis/redis/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-redis/redis/downloads","issues_url":"https://api.github.com/repos/go-redis/redis/issues{/number}","pulls_url":"https://api.github.com/repos/go-redis/redis/pulls{/number}","milestones_url":"https://api.github.com/repos/go-redis/redis/milestones{/number}","notifications_url":"https://api.github.com/repos/go-redis/redis/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-redis/redis/labels{/name}","releases_url":"https://api.github.com/repos/go-redis/redis/releases{/id}","created_at":"2012-07-25T13:01:39Z","updated_at":"2015-03-05T22:10:45Z","pushed_at":"2015-03-04T18:36:42Z","git_url":"git://github.com/go-redis/redis.git","ssh_url":"git@github.com:go-redis/redis.git","clone_url":"https://github.com/go-redis/redis.git","svn_url":"https://github.com/go-redis/redis","homepage":"http://godoc.org/gopkg.in/redis.v2","size":1595,"stargazers_count":235,"watchers_count":235,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":37,"mirror_url":null,"open_issues_count":10,"forks":37,"open_issues":10,"watchers":235,"default_branch":"master"}},"base":{"label":"go-redis:master","ref":"master","sha":"66cbad53a1466a9d072701a9ee7cc9e067fde5c8","user":{"login":"go-redis","id":7314137,"avatar_url":"https://avatars.githubusercontent.com/u/7314137?v=3","gravatar_id":"","url":"https://api.github.com/users/go-redis","html_url":"https://github.com/go-redis","followers_url":"https://api.github.com/users/go-redis/followers","following_url":"https://api.github.com/users/go-redis/following{/other_user}","gists_url":"https://api.github.com/users/go-redis/gists{/gist_id}","starred_url":"https://api.github.com/users/go-redis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-redis/subscriptions","organizations_url":"https://api.github.com/users/go-redis/orgs","repos_url":"https://api.github.com/users/go-redis/repos","events_url":"https://api.github.com/users/go-redis/events{/privacy}","received_events_url":"https://api.github.com/users/go-redis/received_events","type":"Organization","site_admin":false},"repo":{"id":5179099,"name":"redis","full_name":"go-redis/redis","owner":{"login":"go-redis","id":7314137,"avatar_url":"https://avatars.githubusercontent.com/u/7314137?v=3","gravatar_id":"","url":"https://api.github.com/users/go-redis","html_url":"https://github.com/go-redis","followers_url":"https://api.github.com/users/go-redis/followers","following_url":"https://api.github.com/users/go-redis/following{/other_user}","gists_url":"https://api.github.com/users/go-redis/gists{/gist_id}","starred_url":"https://api.github.com/users/go-redis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-redis/subscriptions","organizations_url":"https://api.github.com/users/go-redis/orgs","repos_url":"https://api.github.com/users/go-redis/repos","events_url":"https://api.github.com/users/go-redis/events{/privacy}","received_events_url":"https://api.github.com/users/go-redis/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/go-redis/redis","description":"Redis client for Golang.","fork":false,"url":"https://api.github.com/repos/go-redis/redis","forks_url":"https://api.github.com/repos/go-redis/redis/forks","keys_url":"https://api.github.com/repos/go-redis/redis/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-redis/redis/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-redis/redis/teams","hooks_url":"https://api.github.com/repos/go-redis/redis/hooks","issue_events_url":"https://api.github.com/repos/go-redis/redis/issues/events{/number}","events_url":"https://api.github.com/repos/go-redis/redis/events","assignees_url":"https://api.github.com/repos/go-redis/redis/assignees{/user}","branches_url":"https://api.github.com/repos/go-redis/redis/branches{/branch}","tags_url":"https://api.github.com/repos/go-redis/redis/tags","blobs_url":"https://api.github.com/repos/go-redis/redis/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-redis/redis/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-redis/redis/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-redis/redis/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-redis/redis/statuses/{sha}","languages_url":"https://api.github.com/repos/go-redis/redis/languages","stargazers_url":"https://api.github.com/repos/go-redis/redis/stargazers","contributors_url":"https://api.github.com/repos/go-redis/redis/contributors","subscribers_url":"https://api.github.com/repos/go-redis/redis/subscribers","subscription_url":"https://api.github.com/repos/go-redis/redis/subscription","commits_url":"https://api.github.com/repos/go-redis/redis/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-redis/redis/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-redis/redis/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-redis/redis/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-redis/redis/contents/{+path}","compare_url":"https://api.github.com/repos/go-redis/redis/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-redis/redis/merges","archive_url":"https://api.github.com/repos/go-redis/redis/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-redis/redis/downloads","issues_url":"https://api.github.com/repos/go-redis/redis/issues{/number}","pulls_url":"https://api.github.com/repos/go-redis/redis/pulls{/number}","milestones_url":"https://api.github.com/repos/go-redis/redis/milestones{/number}","notifications_url":"https://api.github.com/repos/go-redis/redis/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-redis/redis/labels{/name}","releases_url":"https://api.github.com/repos/go-redis/redis/releases{/id}","created_at":"2012-07-25T13:01:39Z","updated_at":"2015-03-05T22:10:45Z","pushed_at":"2015-03-04T18:36:42Z","git_url":"git://github.com/go-redis/redis.git","ssh_url":"git@github.com:go-redis/redis.git","clone_url":"https://github.com/go-redis/redis.git","svn_url":"https://github.com/go-redis/redis","homepage":"http://godoc.org/gopkg.in/redis.v2","size":1595,"stargazers_count":235,"watchers_count":235,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":37,"mirror_url":null,"open_issues_count":10,"forks":37,"open_issues":10,"watchers":235,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/go-redis/redis/pulls/71"},"html":{"href":"https://github.com/go-redis/redis/pull/71"},"issue":{"href":"https://api.github.com/repos/go-redis/redis/issues/71"},"comments":{"href":"https://api.github.com/repos/go-redis/redis/issues/71/comments"},"review_comments":{"href":"https://api.github.com/repos/go-redis/redis/pulls/71/comments"},"review_comment":{"href":"https://api.github.com/repos/go-redis/redis/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/go-redis/redis/pulls/71/commits"},"statuses":{"href":"https://api.github.com/repos/go-redis/redis/statuses/641a75e0f1753268ef9e38e7a9517b5ea8c28019"}}}}