{"action":"created","comment":{"url":"https://api.github.com/repos/openvstorage/alba/pulls/comments/65912943","id":65912943,"diff_hunk":"@@ -0,0 +1,350 @@\n+(*\n+Copyright (C) 2016 iNuron NV\n+\n+This file is part of Open vStorage Open Source Edition (OSE), as available from\n+\n+\n+    http://www.openvstorage.org and\n+    http://www.openvstorage.com.\n+\n+This file is free software; you can redistribute it and/or modify it\n+under the terms of the GNU Affero General Public License v3 (GNU AGPLv3)\n+as published by the Free Software Foundation, in version 3 as it comes\n+in the <LICENSE.txt> file of the Open vStorage OSE distribution.\n+\n+Open vStorage is distributed in the hope that it will be useful,\n+but WITHOUT ANY WARRANTY of any kind.\n+*)\n+\n+open Blob_access\n+open Lwt.Infix\n+open Gobjfs\n+open Asd_protocol\n+open Asd_statistics\n+\n+class g_directory_info statistics config service_handle =\n+  let _throw_ex_from ec function_name par =\n+    (* TODO: do we need to be specific ? *)\n+    let error = Unix.EUNKNOWNERR (- Int32.to_int ec) in\n+    let ex =Unix.Unix_error(error, function_name, par) in\n+    Lwt.fail ex\n+  in\n+\n+  let _process_results get_ec function_name get_par results =\n+    let bad = List.filter (fun t -> get_ec t <> 0l) results in\n+    match bad with\n+    | [] -> Lwt.return_unit\n+    | t ::_  -> _throw_ex_from (get_ec t) function_name  (get_par t)\n+  in\n+\n+  let event_channel = IOExecFile.open_event_channel service_handle in\n+  let reap_fd = IOExecFile.get_reap_fd event_channel in\n+object(self)\n+  inherit blob_access statistics\n+  inherit default_directory_access config.files_path\n+\n+\n+  initializer\n+    begin\n+      Lwt.ignore_result (self # _inner ())\n+    end\n+\n+  val _next_id = ref 0L\n+  val _outstanding =  (Hashtbl.create 16 : (int64, int32 Lwt.u) Hashtbl.t)\n+  val _event_channel= event_channel\n+  val _reap_fd = reap_fd\n+\n+  method private next_completion_id =\n+    let r = !_next_id in\n+    let () = _next_id := Int64.succ r in\n+    r\n+\n+  method config = config\n+\n+  method get_blob fnr len =\n+    let fn = self # _get_file_path fnr in\n+    let completion_id = self # next_completion_id in\n+\n+    IOExecFile.file_open service_handle fn [Unix.O_RDONLY]\n+    >>= fun handle ->\n+    let len' =\n+          let remainder = len mod 4096 in\n+          if remainder = 0\n+          then len\n+          else len + (4096 - remainder)\n+        in\n+    let bytes = GMemPool.alloc len' in\n+\n+    Lwt.finalize\n+      (fun () ->\n+        let fragment = Fragment.make completion_id 0 len' bytes in\n+        let fragments = [ fragment ] in\n+        let batch = Batch.make fragments in\n+        IOExecFile.file_read handle batch _event_channel >>= fun () ->\n+        self # _wait_for_completion completion_id >>= fun ec ->\n+        begin\n+          if ec <> 0l\n+          then _throw_ex_from ec \"get_blob\" fn\n+          else Lwt.return_unit\n+        end\n+        >>= fun () ->\n+        (* TODO: don't blit, but this is only used\n+                 - in a rare path: get blob from fs to compare with expectation\n+                 - in multiget1 : which is deprecated only used by antique proxies\n+         *)\n+        let src = Fragment.get_bytes fragment in\n+        let tgt = Bytes.create len in\n+        Lwt_bytes.blit_to_bytes src 0 tgt 0 len;\n+        Lwt.return tgt\n+      )\n+      (fun () ->\n+        IOExecFile.file_close handle >>= fun () ->\n+        GMemPool.free bytes;\n+        Lwt.return_unit\n+      )\n+\n+\n+  method _push_blob_data fnr len slices _f =\n+    let fn = self # _get_file_path fnr in\n+\n+    Prelude.with_timing_lwt\n+      (fun () ->IOExecFile.file_open service_handle fn [Unix.O_RDONLY] )\n+    >>= fun (took, handle) ->\n+    AsdStatistics.new_delta _statistics _FILE_OPEN took;\n+\n+    let corrections =\n+      List.map\n+        (fun (off,len) ->\n+          let completion_id = self # next_completion_id in\n+          (*\n+              (for now) gobjfs does not support arbitrary partial reads\n+              but offset and len need to be multiples of 4096\n+\n+              |---- left ------- | ----- len ----- |----- right -----|\n+             off'               off             off+len          off'+len'\n+\n+           *)\n+\n+          let left = off mod 4096 in\n+          let off' =\n+            if left = 0\n+            then off\n+            else off - left\n+          in\n+          let remainder = (left + len) mod 4096 in\n+          let len' =\n+            if remainder = 0\n+            then left + len\n+            else left + len + (4096 - remainder)\n+          in\n+          let bytes = GMemPool.alloc len' in\n+          let fragment = Fragment.make completion_id off' len' bytes in\n+          (completion_id, off', off, len, len', fragment)\n+        ) slices\n+    in\n+    let fragments =\n+      List.map\n+        (fun (completion_id, off', off, len, len', fragment) -> fragment)\n+        corrections\n+    in\n+    let batch = Batch.make fragments in\n+\n+    Lwt.finalize\n+      (fun () ->\n+        Prelude.with_timing_lwt\n+          (fun () ->\n+            IOExecFile.file_read handle batch _event_channel >>= fun () ->\n+            Lwt_list.map_p\n+              (fun correction ->\n+                let completion_id, _, _, _,_, _ = correction in\n+                Prelude.with_timing_lwt (fun () -> self # _wait_for_completion completion_id)\n+                >>= fun (took,ec) ->\n+                let () = AsdStatistics.new_delta _statistics _READ_WAIT took in\n+                Lwt.return (correction, ec)\n+              )\n+              corrections\n+          )\n+        >>= fun (took,results) ->\n+\n+        let () = AsdStatistics.new_delta _statistics _READ_BATCH took in\n+\n+        _process_results\n+          (fun (_,ec) -> ec)\n+          \"IOExecFile.file_read\"\n+          (fun _ -> fn)\n+          results\n+        >>= fun () ->\n+        Prelude.with_timing_lwt\n+          (fun () -> Lwt_list.iter_s _f results)\n+        >>= fun (took,()) ->\n+        let () = AsdStatistics.new_delta _statistics _READ_BATCH_CB took in\n+        Lwt.return_unit\n+      )\n+      (fun () ->\n+        List.iter Fragment.free_bytes fragments;\n+        Prelude.with_timing_lwt\n+          (fun () ->IOExecFile.file_close handle)\n+        >>= fun (took,()) ->\n+        AsdStatistics.new_delta _statistics _FILE_CLOSE took;\n+        Lwt.return_unit\n+      )\n+\n+  method push_blob_data fnr len slices f =\n+    let _f (correction,ec) =\n+      let (completion_id,off',off,len, len',fragment) = correction in\n+      let buffer = Fragment.get_bytes  fragment in\n+      let left = off - off' in\n+      f (off, len) buffer left\n+    in\n+    self # _push_blob_data fnr len slices _f\n+\n+  method send_blob_data_to fnr len slices nfd =\n+    let _f (correction, ec) =\n+      let (completion_id,off',off, len,len',fragment) = correction in\n+      let buffer = Fragment.get_bytes fragment in\n+      let left = off' - off in\n+      Net_fd.write_all_lwt_bytes nfd buffer left len\n+    in\n+    self # _push_blob_data fnr len slices _f\n+\n+\n+  method private _inner () : unit Lwt.t =\n+    Lwt_log.debug \"_inner ()\" >>= fun () ->\n+    let buf = Lwt_bytes.create 256 in\n+    let rec loop () =\n+      Lwt_unix.wait_read _reap_fd  >>= fun () ->\n+      IOExecFile.reap _reap_fd buf >>= fun (n, ss) ->\n+      (*Lwt_log.debug_f \"reaped:%i\" n >>= fun () -> *)\n+      List.iter\n+        (fun s ->\n+          let completion_id = IOExecFile.get_completion_id s in\n+          let ec = IOExecFile.get_error_code s in\n+          let awake = Hashtbl.find _outstanding completion_id in\n+          Lwt.wakeup awake ec;\n+          Hashtbl.remove _outstanding completion_id\n+        ) ss;\n+      loop ()\n+    in\n+    loop ()\n+\n+  method _wait_for_completion completion_id : int32 Lwt.t =\n+    let sleep, awake = Lwt.wait () in\n+    let () = Hashtbl.add _outstanding completion_id awake in\n+    sleep\n+\n+  method write_blob fnr blob ~post_write ~sync_parent_dirs =\n+\n+    let dir, _, file_path = self # _get_file_dir_name_path fnr in\n+    self # ensure_dir_exists dir ~sync:sync_parent_dirs >>= fun () ->\n+\n+    let completion_id = self # next_completion_id  in\n+    Lwt_log.debug_f \"write_blob ~fnr:%Li completion_id:%Li\" fnr completion_id >>= fun () ->\n+    let len = Blob.length blob in\n+    let alignment = 4096 in\n+    let remainder = len mod alignment in\n+    if remainder <> 0\n+    then\n+      begin\n+        Lwt_log.info_f\n+          \"ragged blob size: fnr:%Li len:%i (0x%0x) using ordinary write\"\n+          fnr len len\n+        >>= fun () ->\n+        Lwt_extra2.with_fd\n+          file_path\n+          ~flags:Lwt_unix.([ O_WRONLY; O_CREAT; O_EXCL; ])","path":"ocaml/src/gblob_access.ml","position":254,"original_position":254,"commit_id":"401e6737cf7b9240b487e7926b2d94cae8fe1f78","original_commit_id":"401e6737cf7b9240b487e7926b2d94cae8fe1f78","user":{"login":"domsj","id":89326,"avatar_url":"https://avatars.githubusercontent.com/u/89326?v=3","gravatar_id":"","url":"https://api.github.com/users/domsj","html_url":"https://github.com/domsj","followers_url":"https://api.github.com/users/domsj/followers","following_url":"https://api.github.com/users/domsj/following{/other_user}","gists_url":"https://api.github.com/users/domsj/gists{/gist_id}","starred_url":"https://api.github.com/users/domsj/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/domsj/subscriptions","organizations_url":"https://api.github.com/users/domsj/orgs","repos_url":"https://api.github.com/users/domsj/repos","events_url":"https://api.github.com/users/domsj/events{/privacy}","received_events_url":"https://api.github.com/users/domsj/received_events","type":"User","site_admin":false},"body":"As the file is not opened using O_DIRECT there should (at least optionally) an fadvise DONTNEED be done after the data is synced to disk.","created_at":"2016-06-06T15:36:23Z","updated_at":"2016-06-06T15:36:23Z","html_url":"https://github.com/openvstorage/alba/pull/225#discussion_r65912943","pull_request_url":"https://api.github.com/repos/openvstorage/alba/pulls/225","_links":{"self":{"href":"https://api.github.com/repos/openvstorage/alba/pulls/comments/65912943"},"html":{"href":"https://github.com/openvstorage/alba/pull/225#discussion_r65912943"},"pull_request":{"href":"https://api.github.com/repos/openvstorage/alba/pulls/225"}}},"pull_request":{"url":"https://api.github.com/repos/openvstorage/alba/pulls/225","id":72464323,"html_url":"https://github.com/openvstorage/alba/pull/225","diff_url":"https://github.com/openvstorage/alba/pull/225.diff","patch_url":"https://github.com/openvstorage/alba/pull/225.patch","issue_url":"https://api.github.com/repos/openvstorage/alba/issues/225","number":225,"state":"open","locked":false,"title":"GIOExecFile","user":{"login":"toolslive","id":1249832,"avatar_url":"https://avatars.githubusercontent.com/u/1249832?v=3","gravatar_id":"","url":"https://api.github.com/users/toolslive","html_url":"https://github.com/toolslive","followers_url":"https://api.github.com/users/toolslive/followers","following_url":"https://api.github.com/users/toolslive/following{/other_user}","gists_url":"https://api.github.com/users/toolslive/gists{/gist_id}","starred_url":"https://api.github.com/users/toolslive/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/toolslive/subscriptions","organizations_url":"https://api.github.com/users/toolslive/orgs","repos_url":"https://api.github.com/users/toolslive/repos","events_url":"https://api.github.com/users/toolslive/events{/privacy}","received_events_url":"https://api.github.com/users/toolslive/received_events","type":"User","site_admin":false},"body":"unsolved:\r\n- libgobjfs logs to stderr as we don't call  `google::InitGoogleLogging()` (calling it will make it log to file,\r\n  which is also not what we want)\r\n- libgogjfs will read it's config from a file, even if the ASD's config comes from etcd.","created_at":"2016-06-03T07:23:54Z","updated_at":"2016-06-06T15:36:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f54a796948104e3351df0faf9b1663399f3ec600","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/openvstorage/alba/pulls/225/commits","review_comments_url":"https://api.github.com/repos/openvstorage/alba/pulls/225/comments","review_comment_url":"https://api.github.com/repos/openvstorage/alba/pulls/comments{/number}","comments_url":"https://api.github.com/repos/openvstorage/alba/issues/225/comments","statuses_url":"https://api.github.com/repos/openvstorage/alba/statuses/401e6737cf7b9240b487e7926b2d94cae8fe1f78","head":{"label":"openvstorage:gIOExecFile","ref":"gIOExecFile","sha":"401e6737cf7b9240b487e7926b2d94cae8fe1f78","user":{"login":"openvstorage","id":12763393,"avatar_url":"https://avatars.githubusercontent.com/u/12763393?v=3","gravatar_id":"","url":"https://api.github.com/users/openvstorage","html_url":"https://github.com/openvstorage","followers_url":"https://api.github.com/users/openvstorage/followers","following_url":"https://api.github.com/users/openvstorage/following{/other_user}","gists_url":"https://api.github.com/users/openvstorage/gists{/gist_id}","starred_url":"https://api.github.com/users/openvstorage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openvstorage/subscriptions","organizations_url":"https://api.github.com/users/openvstorage/orgs","repos_url":"https://api.github.com/users/openvstorage/repos","events_url":"https://api.github.com/users/openvstorage/events{/privacy}","received_events_url":"https://api.github.com/users/openvstorage/received_events","type":"Organization","site_admin":false},"repo":{"id":38867740,"name":"alba","full_name":"openvstorage/alba","owner":{"login":"openvstorage","id":12763393,"avatar_url":"https://avatars.githubusercontent.com/u/12763393?v=3","gravatar_id":"","url":"https://api.github.com/users/openvstorage","html_url":"https://github.com/openvstorage","followers_url":"https://api.github.com/users/openvstorage/followers","following_url":"https://api.github.com/users/openvstorage/following{/other_user}","gists_url":"https://api.github.com/users/openvstorage/gists{/gist_id}","starred_url":"https://api.github.com/users/openvstorage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openvstorage/subscriptions","organizations_url":"https://api.github.com/users/openvstorage/orgs","repos_url":"https://api.github.com/users/openvstorage/repos","events_url":"https://api.github.com/users/openvstorage/events{/privacy}","received_events_url":"https://api.github.com/users/openvstorage/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/openvstorage/alba","description":"Open vStorage ALBA (alternate backend) creates a replicated or flexible network raid’ed  object storage backend out of Seagate Kinetic drives and local disk supporting compression, encryption.","fork":false,"url":"https://api.github.com/repos/openvstorage/alba","forks_url":"https://api.github.com/repos/openvstorage/alba/forks","keys_url":"https://api.github.com/repos/openvstorage/alba/keys{/key_id}","collaborators_url":"https://api.github.com/repos/openvstorage/alba/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/openvstorage/alba/teams","hooks_url":"https://api.github.com/repos/openvstorage/alba/hooks","issue_events_url":"https://api.github.com/repos/openvstorage/alba/issues/events{/number}","events_url":"https://api.github.com/repos/openvstorage/alba/events","assignees_url":"https://api.github.com/repos/openvstorage/alba/assignees{/user}","branches_url":"https://api.github.com/repos/openvstorage/alba/branches{/branch}","tags_url":"https://api.github.com/repos/openvstorage/alba/tags","blobs_url":"https://api.github.com/repos/openvstorage/alba/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/openvstorage/alba/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/openvstorage/alba/git/refs{/sha}","trees_url":"https://api.github.com/repos/openvstorage/alba/git/trees{/sha}","statuses_url":"https://api.github.com/repos/openvstorage/alba/statuses/{sha}","languages_url":"https://api.github.com/repos/openvstorage/alba/languages","stargazers_url":"https://api.github.com/repos/openvstorage/alba/stargazers","contributors_url":"https://api.github.com/repos/openvstorage/alba/contributors","subscribers_url":"https://api.github.com/repos/openvstorage/alba/subscribers","subscription_url":"https://api.github.com/repos/openvstorage/alba/subscription","commits_url":"https://api.github.com/repos/openvstorage/alba/commits{/sha}","git_commits_url":"https://api.github.com/repos/openvstorage/alba/git/commits{/sha}","comments_url":"https://api.github.com/repos/openvstorage/alba/comments{/number}","issue_comment_url":"https://api.github.com/repos/openvstorage/alba/issues/comments{/number}","contents_url":"https://api.github.com/repos/openvstorage/alba/contents/{+path}","compare_url":"https://api.github.com/repos/openvstorage/alba/compare/{base}...{head}","merges_url":"https://api.github.com/repos/openvstorage/alba/merges","archive_url":"https://api.github.com/repos/openvstorage/alba/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/openvstorage/alba/downloads","issues_url":"https://api.github.com/repos/openvstorage/alba/issues{/number}","pulls_url":"https://api.github.com/repos/openvstorage/alba/pulls{/number}","milestones_url":"https://api.github.com/repos/openvstorage/alba/milestones{/number}","notifications_url":"https://api.github.com/repos/openvstorage/alba/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/openvstorage/alba/labels{/name}","releases_url":"https://api.github.com/repos/openvstorage/alba/releases{/id}","deployments_url":"https://api.github.com/repos/openvstorage/alba/deployments","created_at":"2015-07-10T07:40:52Z","updated_at":"2016-06-01T07:48:17Z","pushed_at":"2016-06-06T09:01:59Z","git_url":"git://github.com/openvstorage/alba.git","ssh_url":"git@github.com:openvstorage/alba.git","clone_url":"https://github.com/openvstorage/alba.git","svn_url":"https://github.com/openvstorage/alba","homepage":"","size":21666,"stargazers_count":13,"watchers_count":13,"language":"OCaml","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":14,"forks":5,"open_issues":14,"watchers":13,"default_branch":"master"}},"base":{"label":"openvstorage:master","ref":"master","sha":"8cc289bb1f10bd713a489ad292dfacc0778b76b4","user":{"login":"openvstorage","id":12763393,"avatar_url":"https://avatars.githubusercontent.com/u/12763393?v=3","gravatar_id":"","url":"https://api.github.com/users/openvstorage","html_url":"https://github.com/openvstorage","followers_url":"https://api.github.com/users/openvstorage/followers","following_url":"https://api.github.com/users/openvstorage/following{/other_user}","gists_url":"https://api.github.com/users/openvstorage/gists{/gist_id}","starred_url":"https://api.github.com/users/openvstorage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openvstorage/subscriptions","organizations_url":"https://api.github.com/users/openvstorage/orgs","repos_url":"https://api.github.com/users/openvstorage/repos","events_url":"https://api.github.com/users/openvstorage/events{/privacy}","received_events_url":"https://api.github.com/users/openvstorage/received_events","type":"Organization","site_admin":false},"repo":{"id":38867740,"name":"alba","full_name":"openvstorage/alba","owner":{"login":"openvstorage","id":12763393,"avatar_url":"https://avatars.githubusercontent.com/u/12763393?v=3","gravatar_id":"","url":"https://api.github.com/users/openvstorage","html_url":"https://github.com/openvstorage","followers_url":"https://api.github.com/users/openvstorage/followers","following_url":"https://api.github.com/users/openvstorage/following{/other_user}","gists_url":"https://api.github.com/users/openvstorage/gists{/gist_id}","starred_url":"https://api.github.com/users/openvstorage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openvstorage/subscriptions","organizations_url":"https://api.github.com/users/openvstorage/orgs","repos_url":"https://api.github.com/users/openvstorage/repos","events_url":"https://api.github.com/users/openvstorage/events{/privacy}","received_events_url":"https://api.github.com/users/openvstorage/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/openvstorage/alba","description":"Open vStorage ALBA (alternate backend) creates a replicated or flexible network raid’ed  object storage backend out of Seagate Kinetic drives and local disk supporting compression, encryption.","fork":false,"url":"https://api.github.com/repos/openvstorage/alba","forks_url":"https://api.github.com/repos/openvstorage/alba/forks","keys_url":"https://api.github.com/repos/openvstorage/alba/keys{/key_id}","collaborators_url":"https://api.github.com/repos/openvstorage/alba/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/openvstorage/alba/teams","hooks_url":"https://api.github.com/repos/openvstorage/alba/hooks","issue_events_url":"https://api.github.com/repos/openvstorage/alba/issues/events{/number}","events_url":"https://api.github.com/repos/openvstorage/alba/events","assignees_url":"https://api.github.com/repos/openvstorage/alba/assignees{/user}","branches_url":"https://api.github.com/repos/openvstorage/alba/branches{/branch}","tags_url":"https://api.github.com/repos/openvstorage/alba/tags","blobs_url":"https://api.github.com/repos/openvstorage/alba/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/openvstorage/alba/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/openvstorage/alba/git/refs{/sha}","trees_url":"https://api.github.com/repos/openvstorage/alba/git/trees{/sha}","statuses_url":"https://api.github.com/repos/openvstorage/alba/statuses/{sha}","languages_url":"https://api.github.com/repos/openvstorage/alba/languages","stargazers_url":"https://api.github.com/repos/openvstorage/alba/stargazers","contributors_url":"https://api.github.com/repos/openvstorage/alba/contributors","subscribers_url":"https://api.github.com/repos/openvstorage/alba/subscribers","subscription_url":"https://api.github.com/repos/openvstorage/alba/subscription","commits_url":"https://api.github.com/repos/openvstorage/alba/commits{/sha}","git_commits_url":"https://api.github.com/repos/openvstorage/alba/git/commits{/sha}","comments_url":"https://api.github.com/repos/openvstorage/alba/comments{/number}","issue_comment_url":"https://api.github.com/repos/openvstorage/alba/issues/comments{/number}","contents_url":"https://api.github.com/repos/openvstorage/alba/contents/{+path}","compare_url":"https://api.github.com/repos/openvstorage/alba/compare/{base}...{head}","merges_url":"https://api.github.com/repos/openvstorage/alba/merges","archive_url":"https://api.github.com/repos/openvstorage/alba/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/openvstorage/alba/downloads","issues_url":"https://api.github.com/repos/openvstorage/alba/issues{/number}","pulls_url":"https://api.github.com/repos/openvstorage/alba/pulls{/number}","milestones_url":"https://api.github.com/repos/openvstorage/alba/milestones{/number}","notifications_url":"https://api.github.com/repos/openvstorage/alba/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/openvstorage/alba/labels{/name}","releases_url":"https://api.github.com/repos/openvstorage/alba/releases{/id}","deployments_url":"https://api.github.com/repos/openvstorage/alba/deployments","created_at":"2015-07-10T07:40:52Z","updated_at":"2016-06-01T07:48:17Z","pushed_at":"2016-06-06T09:01:59Z","git_url":"git://github.com/openvstorage/alba.git","ssh_url":"git@github.com:openvstorage/alba.git","clone_url":"https://github.com/openvstorage/alba.git","svn_url":"https://github.com/openvstorage/alba","homepage":"","size":21666,"stargazers_count":13,"watchers_count":13,"language":"OCaml","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":14,"forks":5,"open_issues":14,"watchers":13,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/openvstorage/alba/pulls/225"},"html":{"href":"https://github.com/openvstorage/alba/pull/225"},"issue":{"href":"https://api.github.com/repos/openvstorage/alba/issues/225"},"comments":{"href":"https://api.github.com/repos/openvstorage/alba/issues/225/comments"},"review_comments":{"href":"https://api.github.com/repos/openvstorage/alba/pulls/225/comments"},"review_comment":{"href":"https://api.github.com/repos/openvstorage/alba/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/openvstorage/alba/pulls/225/commits"},"statuses":{"href":"https://api.github.com/repos/openvstorage/alba/statuses/401e6737cf7b9240b487e7926b2d94cae8fe1f78"}}}}
{"actor":{"display_login":"domsj"}}