{"action":"created","comment":{"url":"https://api.github.com/repos/pingcap/tikv/pulls/comments/71321397","id":71321397,"diff_hunk":"@@ -11,92 +11,593 @@\n // See the License for the specific language governing permissions and\n // limitations under the License.\n \n+use std::time::Duration;\n+use std::boxed::Box;\n+\n use std::sync::Arc;\n-use threadpool::ThreadPool;\n-use storage::Engine;\n-use storage::Command;\n-use super::store::TxnStore;\n+use storage::{Engine, Command, Snapshot};\n+use kvproto::kvrpcpb::Context;\n+use storage::mvcc::{MvccTxn, Error as MvccError};\n+use storage::{Key, Value};\n+use std::collections::HashMap;\n+use mio::{self, EventLoop};\n+\n+use storage::engine::{Result as EngineResult, Callback};\n+use super::Result;\n+use super::Error;\n+use super::SchedCh;\n+\n+use super::store::SnapshotStore;\n+use super::mem_rowlock::MemRowLocks;\n+\n+const REPORT_STATISTIC_INTERVAL: u64 = 60000; // 60 seconds\n+\n+pub enum Tick {\n+    ReportStatistic,\n+}\n+\n+pub enum ProcessResult {\n+    ResultSet {\n+        result: Vec<Result<()>>,\n+    },\n+    Value {\n+        value: Option<Value>,\n+    },\n+    Nothing,\n+}\n+\n+pub enum Msg {\n+    // outer message\n+    Quit,\n+    RawCmd {\n+        cmd: Command,\n+    },\n \n-// TODO: make this number configurable.\n-const DEFAULT_POOL_SIZE: usize = 8;\n+    // inner message\n+    AcquireLock {\n+        cid: u64,\n+    },\n+    GetSnapshot {\n+        cid: u64,\n+    },\n+    SnapshotFinish {\n+        cid: u64,\n+        snapshot: EngineResult<Box<Snapshot>>,\n+    },\n+    WriteFinish {\n+        cid: u64,\n+        pr: ProcessResult,\n+        result: EngineResult<()>,\n+    },\n+}\n+\n+type LockIdxs = Vec<usize>;\n+\n+struct RunningCtx {\n+    pub cid: u64,\n+    pub cmd: Command,\n+    needed_locks: LockIdxs,\n+    pub owned_lock_count: usize,\n+}\n+\n+impl RunningCtx {\n+    pub fn new(cid: u64, cmd: Command, needed_locks: LockIdxs) -> RunningCtx {\n+        RunningCtx {\n+            cid: cid,\n+            cmd: cmd,\n+            needed_locks: needed_locks,\n+            owned_lock_count: 0,\n+        }\n+    }\n+\n+    pub fn needed_locks(&self) -> &LockIdxs {\n+        &self.needed_locks\n+    }\n+\n+    pub fn all_lock_acquired(&self) -> bool {\n+        self.needed_locks.len() == self.owned_lock_count\n+    }\n+}\n+\n+fn make_write_cb(pr: ProcessResult, cid: u64, ch: SchedCh) -> Callback<()> {\n+    Box::new(move |result: EngineResult<()>| {\n+        if let Err(e) = ch.send(Msg::WriteFinish{\n+            cid: cid,\n+            pr: pr,\n+            result: result,\n+        }) {\n+            error!(\"write engine failed cmd id {}, err {:?}\",\n+            cid,\n+            e);\n+        }\n+    })\n+}\n \n pub struct Scheduler {\n-    store: Arc<TxnStore>,\n-    pool: ThreadPool,\n+    engine: Arc<Box<Engine>>,\n+\n+    // cid -> context\n+    cmd_ctxs: HashMap<u64, RunningCtx>,\n+\n+    //\n+    schedch: SchedCh,\n+\n+    // cmd id generator\n+    idalloc: u64,\n+\n+    // simulate memory row locks\n+    rowlocks: MemRowLocks,\n+\n+    // statistics for flow control\n+    running_cmd_count: u64,\n+}\n+\n+fn readonly_cmd(cmd: &Command) -> bool {\n+    match *cmd {\n+        Command::Get { .. } |\n+        Command::BatchGet { .. } |\n+        Command::Scan { .. } => true,\n+        _ => false,\n+    }\n }\n \n impl Scheduler {\n-    pub fn new(engine: Arc<Box<Engine>>) -> Scheduler {\n+    pub fn new(engine: Arc<Box<Engine>>, schedch: SchedCh, concurrency: usize) -> Scheduler {\n         Scheduler {\n-            store: Arc::new(TxnStore::new(engine)),\n-            pool: ThreadPool::new_with_name(thd_name!(\"txn-scheduler-pool\"), DEFAULT_POOL_SIZE),\n+            engine: engine,\n+            cmd_ctxs: HashMap::new(),\n+            schedch: schedch,\n+            idalloc: 0,\n+            rowlocks: MemRowLocks::new(concurrency),\n+            running_cmd_count: 0,\n         }\n     }\n \n-    pub fn exec(&self, cmd: Command) {\n-        let store = self.store.clone();\n-        self.pool.execute(move || handle_cmd(store, cmd));\n+    fn next_id(&mut self) -> u64 {\n+        self.idalloc += 1;\n+        self.idalloc\n     }\n-}\n \n-fn handle_cmd(store: Arc<TxnStore>, cmd: Command) {\n-    let cmd_str = format!(\"{}\", cmd);\n-    debug!(\"scheduler::handle_cmd begin: {}\", cmd_str);\n-    match cmd {\n-        Command::Get { ctx, key, start_ts, callback } => {\n-            callback(store.get(ctx, &key, start_ts).map_err(::storage::Error::from));\n-        }\n-        Command::BatchGet { ctx, keys, start_ts, callback } => {\n-            callback(match store.batch_get(ctx, &keys, start_ts) {\n-                Ok(results) => {\n-                    let mut res = vec![];\n-                    for (k, v) in keys.into_iter().zip(results.into_iter()) {\n-                        match v {\n-                            Ok(Some(x)) => res.push(Ok((k.raw().unwrap(), x))),\n-                            Ok(None) => {}\n-                            Err(e) => res.push(Err(::storage::Error::from(e))),\n-                        }\n+    fn incr_running_cmd(&mut self) {\n+        self.running_cmd_count += 1;\n+    }\n+\n+    fn decr_running_cmd(&mut self) {\n+        self.running_cmd_count -= 1;\n+    }\n+\n+    fn save_cmd_context(&mut self, cid: u64, ctx: RunningCtx) {\n+        if let Some(_) = self.cmd_ctxs.insert(cid, ctx) {\n+            panic!(\"cid = {} existed, fatal\", cid);\n+        }\n+    }\n+\n+    fn finish_cmd(&mut self, cid: u64) {\n+        {\n+            // running ctx must existed\n+            let rctx = self.cmd_ctxs.get(&cid).unwrap();\n+            assert_eq!(cid, rctx.cid);\n+\n+            // release lock and wake up waiting commands\n+            if !rctx.needed_locks().is_empty() {\n+                let wakeup_list = self.rowlocks.release_by_indexs(rctx.needed_locks(), cid);\n+                for wcid in wakeup_list {\n+                    if let Err(e) = self.schedch.send(Msg::AcquireLock { cid: wcid }) {\n+                        error!(\"wake up cmd failed, cid = {}, error = {:?}\", wcid, e);\n+                    } else {\n+                        debug!(\"wake up cmd for acquire lock, cid = {}\", wcid);\n                     }\n-                    Ok(res)\n                 }\n-                Err(e) => Err(e.into()),\n-            });\n+            }\n+        }\n+\n+        self.cmd_ctxs.remove(&cid);\n+        self.decr_running_cmd();\n+    }\n+\n+    pub fn dispatch_cmd(&self, cmd: Command) {\n+        if let Err(e) = self.schedch.send(Msg::RawCmd{ cmd: cmd }) {\n+            error!(\"dispatch cmd failed, error = {:?}\", e);\n+        }\n+    }\n+\n+    fn calc_lock_indexs(&self, cmd: &Command) -> Vec<usize> {\n+        match *cmd {\n+            Command::Prewrite { ref mutations, .. } => {\n+                let locked_keys: Vec<&Key> = mutations.iter().map(|x| x.key()).collect();\n+                self.rowlocks.calc_lock_indexs(&locked_keys)\n+            }\n+            Command::Commit { ref keys, .. } |\n+            Command::Rollback { ref keys, .. } => {\n+                self.rowlocks.calc_lock_indexs(keys)\n+            }\n+            Command::CommitThenGet { ref key, .. } |\n+            Command::Cleanup { ref key, .. } |\n+            Command::RollbackThenGet { ref key, .. } => {\n+                self.rowlocks.calc_lock_indexs(&[key])\n+            }\n+            _ => {\n+                panic!(\"unsupport cmd that need lock\");\n+            }\n         }\n-        Command::Scan { ctx, start_key, limit, start_ts, callback } => {\n-            callback(match store.scan(ctx, start_key, limit, start_ts) {\n-                Ok(mut results) => {\n-                    Ok(results.drain(..).map(|x| x.map_err(::storage::Error::from)).collect())\n+    }\n+\n+    fn process_cmd_with_snapshot(&mut self, cid: u64, snapshot: &Snapshot) -> Result<()> {\n+        debug!(\"process cmd with snapshot, cid = {}\", cid);\n+\n+        let rctx = self.cmd_ctxs.get_mut(&cid).unwrap();\n+        match rctx.cmd {\n+            Command::Get { ref key, start_ts, ref mut callback, .. } => {\n+                let snap_store = SnapshotStore::new(snapshot, start_ts);\n+                callback.take().unwrap()(snap_store.get(key).map_err(::storage::Error::from));\n+            }\n+\n+            Command::BatchGet { ref keys, start_ts, ref mut callback, .. } => {\n+                let snap_store = SnapshotStore::new(snapshot, start_ts);\n+                callback.take().unwrap()(match snap_store.batch_get(keys) {\n+                    Ok(results) => {\n+                        let mut res = vec![];\n+                        for (k, v) in keys.into_iter().zip(results.into_iter()) {\n+                            match v {\n+                                Ok(Some(x)) => res.push(Ok((k.raw().unwrap(), x))),\n+                                Ok(None) => {}\n+                                Err(e) => res.push(Err(::storage::Error::from(e))),\n+                            }\n+                        }\n+                        Ok(res)\n+                    }\n+                    Err(e) => Err(e.into()),\n+                });\n+            }\n+\n+            Command::Scan { ref start_key, limit, start_ts, ref mut callback, .. } => {\n+                let snap_store = SnapshotStore::new(snapshot, start_ts);\n+                let mut scanner = try!(snap_store.scanner());\n+                let key = start_key.clone();\n+                callback.take().unwrap()(match scanner.scan(key, limit) {\n+                    Ok(mut results) => {\n+                        Ok(results.drain(..).map(|x| x.map_err(::storage::Error::from)).collect())\n+                    }\n+                    Err(e) => Err(e.into()),\n+                });\n+            }\n+\n+            Command::Prewrite { ref ctx, ref mutations, ref primary, start_ts, .. } => {\n+                let mut txn = MvccTxn::new(snapshot, start_ts);\n+\n+                let mut results = vec![];\n+                for m in mutations {\n+                    match txn.prewrite(m.clone(), primary) {\n+                        Ok(_) => results.push(Ok(())),\n+                        e @ Err(MvccError::KeyIsLocked { .. }) => results.push(e.map_err(Error::from)),\n+                        Err(e) => return Err(Error::from(e)),\n+                    }\n                 }\n-                Err(e) => Err(e.into()),\n-            });\n+\n+                let pr: ProcessResult = ProcessResult::ResultSet { result: results };\n+                let cb = make_write_cb(pr, cid, self.schedch.clone());\n+\n+                try!(self.engine.async_write(ctx, txn.modifies(), cb));\n+            }\n+\n+            Command::Commit { ref ctx, ref keys, lock_ts, commit_ts, .. } => {\n+                let mut txn = MvccTxn::new(snapshot, lock_ts);\n+\n+                for k in keys {\n+                    try!(txn.commit(&k, commit_ts));\n+                }\n+\n+                let pr: ProcessResult = ProcessResult::Nothing;\n+                let cb = make_write_cb(pr, cid, self.schedch.clone());\n+                try!(self.engine.async_write(ctx, txn.modifies(), cb));\n+            }\n+\n+            Command::CommitThenGet { ref ctx, ref key, lock_ts, commit_ts, get_ts, .. } => {\n+                let mut txn = MvccTxn::new(snapshot, lock_ts);\n+\n+                let val = try!(txn.commit_then_get(&key, commit_ts, get_ts));\n+\n+                let pr: ProcessResult = ProcessResult::Value { value: val };\n+                let cb = make_write_cb(pr, cid, self.schedch.clone());\n+                try!(self.engine.async_write(ctx, txn.modifies(), cb));\n+            }\n+\n+            Command::Cleanup { ref ctx, ref key, start_ts, .. } => {\n+                let mut txn = MvccTxn::new(snapshot, start_ts);\n+\n+                try!(txn.rollback(&key));\n+\n+                let pr: ProcessResult = ProcessResult::Nothing;\n+                let cb = make_write_cb(pr, cid, self.schedch.clone());\n+                try!(self.engine.async_write(&ctx, txn.modifies(), cb));\n+            }\n+            Command::Rollback { ref ctx, ref keys, start_ts, .. } => {\n+                let mut txn = MvccTxn::new(snapshot, start_ts);\n+\n+                for k in keys {\n+                    try!(txn.rollback(&k));\n+                }\n+\n+                let pr: ProcessResult = ProcessResult::Nothing;\n+                let cb = make_write_cb(pr, cid, self.schedch.clone());\n+                try!(self.engine.async_write(ctx, txn.modifies(), cb));\n+            }\n+            Command::RollbackThenGet { ref ctx, ref key, lock_ts, .. } => {\n+                let mut txn = MvccTxn::new(snapshot, lock_ts);\n+","path":"src/storage/txn/scheduler.rs","position":358,"original_position":358,"commit_id":"4b8ad6e53a534db23dcdc13c7ea89efb5b29efc3","original_commit_id":"4b8ad6e53a534db23dcdc13c7ea89efb5b29efc3","user":{"login":"ngaut","id":878009,"avatar_url":"https://avatars.githubusercontent.com/u/878009?v=3","gravatar_id":"","url":"https://api.github.com/users/ngaut","html_url":"https://github.com/ngaut","followers_url":"https://api.github.com/users/ngaut/followers","following_url":"https://api.github.com/users/ngaut/following{/other_user}","gists_url":"https://api.github.com/users/ngaut/gists{/gist_id}","starred_url":"https://api.github.com/users/ngaut/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ngaut/subscriptions","organizations_url":"https://api.github.com/users/ngaut/orgs","repos_url":"https://api.github.com/users/ngaut/repos","events_url":"https://api.github.com/users/ngaut/events{/privacy}","received_events_url":"https://api.github.com/users/ngaut/received_events","type":"User","site_admin":false},"body":"Remove blank line.","created_at":"2016-07-19T11:36:40Z","updated_at":"2016-07-19T11:36:40Z","html_url":"https://github.com/pingcap/tikv/pull/850#discussion_r71321397","pull_request_url":"https://api.github.com/repos/pingcap/tikv/pulls/850","_links":{"self":{"href":"https://api.github.com/repos/pingcap/tikv/pulls/comments/71321397"},"html":{"href":"https://github.com/pingcap/tikv/pull/850#discussion_r71321397"},"pull_request":{"href":"https://api.github.com/repos/pingcap/tikv/pulls/850"}}},"pull_request":{"url":"https://api.github.com/repos/pingcap/tikv/pulls/850","id":77922954,"html_url":"https://github.com/pingcap/tikv/pull/850","diff_url":"https://github.com/pingcap/tikv/pull/850.diff","patch_url":"https://github.com/pingcap/tikv/pull/850.patch","issue_url":"https://api.github.com/repos/pingcap/tikv/issues/850","number":850,"state":"open","locked":false,"title":"*: async scheduler, async handle KvReq","user":{"login":"zhangjinpeng1987","id":19700528,"avatar_url":"https://avatars.githubusercontent.com/u/19700528?v=3","gravatar_id":"","url":"https://api.github.com/users/zhangjinpeng1987","html_url":"https://github.com/zhangjinpeng1987","followers_url":"https://api.github.com/users/zhangjinpeng1987/followers","following_url":"https://api.github.com/users/zhangjinpeng1987/following{/other_user}","gists_url":"https://api.github.com/users/zhangjinpeng1987/gists{/gist_id}","starred_url":"https://api.github.com/users/zhangjinpeng1987/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zhangjinpeng1987/subscriptions","organizations_url":"https://api.github.com/users/zhangjinpeng1987/orgs","repos_url":"https://api.github.com/users/zhangjinpeng1987/repos","events_url":"https://api.github.com/users/zhangjinpeng1987/events{/privacy}","received_events_url":"https://api.github.com/users/zhangjinpeng1987/received_events","type":"User","site_admin":false},"body":"async handle KvReq","created_at":"2016-07-19T06:17:44Z","updated_at":"2016-07-19T11:36:40Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7cf97e35ece35ffcb93ae29f65aa12362d7bccfb","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/pingcap/tikv/pulls/850/commits","review_comments_url":"https://api.github.com/repos/pingcap/tikv/pulls/850/comments","review_comment_url":"https://api.github.com/repos/pingcap/tikv/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pingcap/tikv/issues/850/comments","statuses_url":"https://api.github.com/repos/pingcap/tikv/statuses/4b8ad6e53a534db23dcdc13c7ea89efb5b29efc3","head":{"label":"pingcap:zhangjinpeng/async-scheduler","ref":"zhangjinpeng/async-scheduler","sha":"4b8ad6e53a534db23dcdc13c7ea89efb5b29efc3","user":{"login":"pingcap","id":11855343,"avatar_url":"https://avatars.githubusercontent.com/u/11855343?v=3","gravatar_id":"","url":"https://api.github.com/users/pingcap","html_url":"https://github.com/pingcap","followers_url":"https://api.github.com/users/pingcap/followers","following_url":"https://api.github.com/users/pingcap/following{/other_user}","gists_url":"https://api.github.com/users/pingcap/gists{/gist_id}","starred_url":"https://api.github.com/users/pingcap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pingcap/subscriptions","organizations_url":"https://api.github.com/users/pingcap/orgs","repos_url":"https://api.github.com/users/pingcap/repos","events_url":"https://api.github.com/users/pingcap/events{/privacy}","received_events_url":"https://api.github.com/users/pingcap/received_events","type":"Organization","site_admin":false},"repo":{"id":48833910,"name":"tikv","full_name":"pingcap/tikv","owner":{"login":"pingcap","id":11855343,"avatar_url":"https://avatars.githubusercontent.com/u/11855343?v=3","gravatar_id":"","url":"https://api.github.com/users/pingcap","html_url":"https://github.com/pingcap","followers_url":"https://api.github.com/users/pingcap/followers","following_url":"https://api.github.com/users/pingcap/following{/other_user}","gists_url":"https://api.github.com/users/pingcap/gists{/gist_id}","starred_url":"https://api.github.com/users/pingcap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pingcap/subscriptions","organizations_url":"https://api.github.com/users/pingcap/orgs","repos_url":"https://api.github.com/users/pingcap/repos","events_url":"https://api.github.com/users/pingcap/events{/privacy}","received_events_url":"https://api.github.com/users/pingcap/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pingcap/tikv","description":"Distributed transactional key value database powered by Rust and Raft","fork":false,"url":"https://api.github.com/repos/pingcap/tikv","forks_url":"https://api.github.com/repos/pingcap/tikv/forks","keys_url":"https://api.github.com/repos/pingcap/tikv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pingcap/tikv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pingcap/tikv/teams","hooks_url":"https://api.github.com/repos/pingcap/tikv/hooks","issue_events_url":"https://api.github.com/repos/pingcap/tikv/issues/events{/number}","events_url":"https://api.github.com/repos/pingcap/tikv/events","assignees_url":"https://api.github.com/repos/pingcap/tikv/assignees{/user}","branches_url":"https://api.github.com/repos/pingcap/tikv/branches{/branch}","tags_url":"https://api.github.com/repos/pingcap/tikv/tags","blobs_url":"https://api.github.com/repos/pingcap/tikv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pingcap/tikv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pingcap/tikv/git/refs{/sha}","trees_url":"https://api.github.com/repos/pingcap/tikv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pingcap/tikv/statuses/{sha}","languages_url":"https://api.github.com/repos/pingcap/tikv/languages","stargazers_url":"https://api.github.com/repos/pingcap/tikv/stargazers","contributors_url":"https://api.github.com/repos/pingcap/tikv/contributors","subscribers_url":"https://api.github.com/repos/pingcap/tikv/subscribers","subscription_url":"https://api.github.com/repos/pingcap/tikv/subscription","commits_url":"https://api.github.com/repos/pingcap/tikv/commits{/sha}","git_commits_url":"https://api.github.com/repos/pingcap/tikv/git/commits{/sha}","comments_url":"https://api.github.com/repos/pingcap/tikv/comments{/number}","issue_comment_url":"https://api.github.com/repos/pingcap/tikv/issues/comments{/number}","contents_url":"https://api.github.com/repos/pingcap/tikv/contents/{+path}","compare_url":"https://api.github.com/repos/pingcap/tikv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pingcap/tikv/merges","archive_url":"https://api.github.com/repos/pingcap/tikv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pingcap/tikv/downloads","issues_url":"https://api.github.com/repos/pingcap/tikv/issues{/number}","pulls_url":"https://api.github.com/repos/pingcap/tikv/pulls{/number}","milestones_url":"https://api.github.com/repos/pingcap/tikv/milestones{/number}","notifications_url":"https://api.github.com/repos/pingcap/tikv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pingcap/tikv/labels{/name}","releases_url":"https://api.github.com/repos/pingcap/tikv/releases{/id}","deployments_url":"https://api.github.com/repos/pingcap/tikv/deployments","created_at":"2015-12-31T04:51:32Z","updated_at":"2016-07-18T15:16:51Z","pushed_at":"2016-07-19T11:34:16Z","git_url":"git://github.com/pingcap/tikv.git","ssh_url":"git@github.com:pingcap/tikv.git","clone_url":"https://github.com/pingcap/tikv.git","svn_url":"https://github.com/pingcap/tikv","homepage":"","size":4682,"stargazers_count":792,"watchers_count":792,"language":"Rust","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":64,"mirror_url":null,"open_issues_count":34,"forks":64,"open_issues":34,"watchers":792,"default_branch":"master"}},"base":{"label":"pingcap:master","ref":"master","sha":"bc81bbea46b1ed965a023157e60663b5d8369dfc","user":{"login":"pingcap","id":11855343,"avatar_url":"https://avatars.githubusercontent.com/u/11855343?v=3","gravatar_id":"","url":"https://api.github.com/users/pingcap","html_url":"https://github.com/pingcap","followers_url":"https://api.github.com/users/pingcap/followers","following_url":"https://api.github.com/users/pingcap/following{/other_user}","gists_url":"https://api.github.com/users/pingcap/gists{/gist_id}","starred_url":"https://api.github.com/users/pingcap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pingcap/subscriptions","organizations_url":"https://api.github.com/users/pingcap/orgs","repos_url":"https://api.github.com/users/pingcap/repos","events_url":"https://api.github.com/users/pingcap/events{/privacy}","received_events_url":"https://api.github.com/users/pingcap/received_events","type":"Organization","site_admin":false},"repo":{"id":48833910,"name":"tikv","full_name":"pingcap/tikv","owner":{"login":"pingcap","id":11855343,"avatar_url":"https://avatars.githubusercontent.com/u/11855343?v=3","gravatar_id":"","url":"https://api.github.com/users/pingcap","html_url":"https://github.com/pingcap","followers_url":"https://api.github.com/users/pingcap/followers","following_url":"https://api.github.com/users/pingcap/following{/other_user}","gists_url":"https://api.github.com/users/pingcap/gists{/gist_id}","starred_url":"https://api.github.com/users/pingcap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pingcap/subscriptions","organizations_url":"https://api.github.com/users/pingcap/orgs","repos_url":"https://api.github.com/users/pingcap/repos","events_url":"https://api.github.com/users/pingcap/events{/privacy}","received_events_url":"https://api.github.com/users/pingcap/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pingcap/tikv","description":"Distributed transactional key value database powered by Rust and Raft","fork":false,"url":"https://api.github.com/repos/pingcap/tikv","forks_url":"https://api.github.com/repos/pingcap/tikv/forks","keys_url":"https://api.github.com/repos/pingcap/tikv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pingcap/tikv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pingcap/tikv/teams","hooks_url":"https://api.github.com/repos/pingcap/tikv/hooks","issue_events_url":"https://api.github.com/repos/pingcap/tikv/issues/events{/number}","events_url":"https://api.github.com/repos/pingcap/tikv/events","assignees_url":"https://api.github.com/repos/pingcap/tikv/assignees{/user}","branches_url":"https://api.github.com/repos/pingcap/tikv/branches{/branch}","tags_url":"https://api.github.com/repos/pingcap/tikv/tags","blobs_url":"https://api.github.com/repos/pingcap/tikv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pingcap/tikv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pingcap/tikv/git/refs{/sha}","trees_url":"https://api.github.com/repos/pingcap/tikv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pingcap/tikv/statuses/{sha}","languages_url":"https://api.github.com/repos/pingcap/tikv/languages","stargazers_url":"https://api.github.com/repos/pingcap/tikv/stargazers","contributors_url":"https://api.github.com/repos/pingcap/tikv/contributors","subscribers_url":"https://api.github.com/repos/pingcap/tikv/subscribers","subscription_url":"https://api.github.com/repos/pingcap/tikv/subscription","commits_url":"https://api.github.com/repos/pingcap/tikv/commits{/sha}","git_commits_url":"https://api.github.com/repos/pingcap/tikv/git/commits{/sha}","comments_url":"https://api.github.com/repos/pingcap/tikv/comments{/number}","issue_comment_url":"https://api.github.com/repos/pingcap/tikv/issues/comments{/number}","contents_url":"https://api.github.com/repos/pingcap/tikv/contents/{+path}","compare_url":"https://api.github.com/repos/pingcap/tikv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pingcap/tikv/merges","archive_url":"https://api.github.com/repos/pingcap/tikv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pingcap/tikv/downloads","issues_url":"https://api.github.com/repos/pingcap/tikv/issues{/number}","pulls_url":"https://api.github.com/repos/pingcap/tikv/pulls{/number}","milestones_url":"https://api.github.com/repos/pingcap/tikv/milestones{/number}","notifications_url":"https://api.github.com/repos/pingcap/tikv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pingcap/tikv/labels{/name}","releases_url":"https://api.github.com/repos/pingcap/tikv/releases{/id}","deployments_url":"https://api.github.com/repos/pingcap/tikv/deployments","created_at":"2015-12-31T04:51:32Z","updated_at":"2016-07-18T15:16:51Z","pushed_at":"2016-07-19T11:34:16Z","git_url":"git://github.com/pingcap/tikv.git","ssh_url":"git@github.com:pingcap/tikv.git","clone_url":"https://github.com/pingcap/tikv.git","svn_url":"https://github.com/pingcap/tikv","homepage":"","size":4682,"stargazers_count":792,"watchers_count":792,"language":"Rust","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":64,"mirror_url":null,"open_issues_count":34,"forks":64,"open_issues":34,"watchers":792,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pingcap/tikv/pulls/850"},"html":{"href":"https://github.com/pingcap/tikv/pull/850"},"issue":{"href":"https://api.github.com/repos/pingcap/tikv/issues/850"},"comments":{"href":"https://api.github.com/repos/pingcap/tikv/issues/850/comments"},"review_comments":{"href":"https://api.github.com/repos/pingcap/tikv/pulls/850/comments"},"review_comment":{"href":"https://api.github.com/repos/pingcap/tikv/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pingcap/tikv/pulls/850/commits"},"statuses":{"href":"https://api.github.com/repos/pingcap/tikv/statuses/4b8ad6e53a534db23dcdc13c7ea89efb5b29efc3"}}}}
{"actor":{"display_login":"ngaut"}}