{"action":"created","comment":{"url":"https://api.github.com/repos/basho/riak_kv/pulls/comments/93227425","pull_request_review_id":13748948,"id":93227425,"diff_hunk":"@@ -0,0 +1,641 @@\n+%% -------------------------------------------------------------------\n+%%\n+%% riak_kv_sweeper_fold: Riak sweep scheduler callbacks\n+%%\n+%% Copyright (c) 2016 Basho Technologies, Inc.  All Rights Reserved.\n+%%\n+%% This file is provided to you under the Apache License,\n+%% Version 2.0 (the \"License\"); you may not use this file\n+%% except in compliance with the License.  You may obtain\n+%% 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,\n+%% software distributed under the License is distributed on an\n+%% \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n+%% KIND, either express or implied.  See the License for the\n+%% specific language governing permissions and limitations\n+%% under the License.\n+%%\n+%% -------------------------------------------------------------------\n+-module(riak_kv_sweeper_fold).\n+\n+-export([do_sweep/8]).\n+-export([get_sweep_throttle/0]).\n+-export([send_to_sweep_worker/2]).\n+\n+-export_type([sweep_result/0]).\n+\n+-include(\"riak_kv_sweeper.hrl\").\n+-ifdef(TEST).\n+-include_lib(\"eunit/include/eunit.hrl\").\n+-endif.\n+\n+-define(MAX_SWEEP_CRASHES, 10).\n+%% Throttle used when sweeping over K/V data: {Type, Limit, Wait}.\n+%% Type can be pace or obj_size.Default: 1 MB limit / 100 ms wait\n+-define(DEFAULT_SWEEP_THROTTLE, {obj_size, 1000000, 100}).\n+%% Default value for how much faster the sweeper throttle should be\n+%% during sweep window.\n+-define(SWEEP_WINDOW_THROTTLE_DIV, 1).\n+\n+%% ====================================================================\n+%% Types\n+%% ====================================================================\n+-type opaque() :: any(). % Opaque types should not be inspected\n+\n+-type sweep_result() :: {SweptKeys :: non_neg_integer(),\n+                         [{riak_kv_sweeper:participant_module(),\n+                           'succ' | 'fail'}]}.\n+\n+%% ====================================================================\n+%% API And callbacks\n+%% ====================================================================\n+-spec do_sweep([ActiveParticipants :: #sweep_participant{}],\n+               EstimatedKeys       :: non_neg_integer(),\n+               Sender              :: opaque(),\n+               Opts                :: [],\n+               Index               :: riak_kv_sweeper:index(),\n+               Mod                 :: atom(),\n+               ModState            :: opaque(),\n+               VnodeState          :: opaque()) ->\n+    {reply, Acc :: #sa{}, VnodeState :: opaque()}\n+  | {reply, Reason :: opaque(), VnodeState :: opaque()}\n+  | {async, {sweep,\n+             Work :: opaque(),\n+             FinishFun :: fun((#sa{}) -> #sa{})},\n+     Sender :: opaque(),\n+     VnodeState :: opaque()}.\n+\n+do_sweep(ActiveParticipants, EstimatedKeys, Sender, Opts, Index,\n+         Mod, ModState, VnodeState) ->\n+    InitialAcc = make_initial_acc(Index, ActiveParticipants, EstimatedKeys),\n+    case Mod:fold_objects(fun fold_req_fun/4, InitialAcc, Opts, ModState) of\n+        {ok, #sa{} = Acc} ->\n+            inform_participants(Acc, Index),\n+            riak_kv_sweeper:sweep_result(Index, format_result(Acc)),\n+            Acc1 = stat_send(Index, {final, Acc}),\n+            {reply, Acc1, VnodeState};\n+        {async, Work} ->\n+            FinishFun =\n+                fun(Acc) ->\n+                        inform_participants(Acc, Index),\n+                        riak_kv_sweeper:sweep_result(Index, format_result(Acc)),\n+                        stat_send(Index, {final, Acc})\n+                end,\n+            {async, {sweep, Work, FinishFun}, Sender, VnodeState};\n+        Reason ->\n+            failed_sweep(ActiveParticipants, Index, Reason),\n+            {reply, Reason, VnodeState}\n+    end.\n+\n+-spec fold_req_fun(riak_object:bucket(), riak_object:key(), RObjBin :: binary(),\n+                   #sa{}) -> #sa{} | no_return().\n+fold_req_fun(_Bucket, _Key, _RObjBin,\n+             #sa{active_p = [], failed_p = Failed} = Acc) when Failed =/= [] ->\n+    lager:error(\"No more participants in sweep of Index ~p Failed: ~p\",\n+                [Acc#sa.index, Failed]),\n+    throw({stop_sweep, Acc});\n+\n+fold_req_fun(Bucket, Key, RObjBin,\n+             #sa{index = Index, swept_keys = SweptKeys} = Acc) ->\n+    Acc1 = maybe_throttle_sweep(RObjBin, Acc),\n+    Acc3 =\n+        case SweptKeys rem 1000 of\n+            0 ->\n+                riak_kv_sweeper:update_progress(Index, SweptKeys),\n+                Acc2 = maybe_receive_request(Acc1),\n+                stat_send(Index, {in_progress, Acc2});\n+            _ ->\n+                Acc1\n+        end,\n+    RObj = riak_object:from_binary(Bucket, Key, RObjBin),\n+    {NumMutated, NumDeleted, Acc4} =\n+        apply_sweep_participant_funs({{Bucket, Key}, RObj},\n+                                     Acc3#sa{swept_keys = SweptKeys + 1}),\n+    update_stat_counters(NumMutated, NumDeleted, byte_size(RObjBin), Acc4).\n+\n+-spec make_initial_acc(riak_kv_sweeper:index(),\n+                       [#sweep_participant{}],\n+                       EstimatedKeys :: non_neg_integer()) -> #sa{}.\n+make_initial_acc(Index, ActiveParticipants, EstiamatedKeys) ->\n+    #sa{index = Index,\n+        active_p = sort_participants(ActiveParticipants),\n+        estimated_keys = EstiamatedKeys}.\n+\n+-spec sort_participants([#sweep_participant{}]) ->\n+    [#sweep_participant{}].\n+sort_participants(Participants) ->\n+    lists:sort(fun compare_participants/2, Participants).\n+\n+-spec compare_participants(#sweep_participant{},\n+                           #sweep_participant{}) -> boolean().\n+compare_participants(A, B) ->\n+    TypeA = A#sweep_participant.fun_type,\n+    TypeB = B#sweep_participant.fun_type,\n+    fun_type_rank(TypeA) =< fun_type_rank(TypeB).\n+\n+-spec fun_type_rank(riak_kv_sweeper:fun_type()) -> 1 | 2 | 3.\n+fun_type_rank(delete_fun) -> 1;\n+fun_type_rank(modify_fun) -> 2;\n+fun_type_rank(observe_fun) -> 3.\n+\n+-spec inform_participants(#sa{}, riak_kv_sweeper:index()) -> [].\n+inform_participants(#sa{active_p = Succ, failed_p = Failed}, Index) ->\n+    successful_sweep(Succ, Index),\n+    failed_sweep(Failed, Index).\n+\n+-spec successful_sweep([#sweep_participant{}], riak_kv_sweeper:index()) -> [].\n+successful_sweep(Succ, Index) ->\n+    [Module:successful_sweep(Index, FinalAcc) ||\n+       #sweep_participant{module = Module, acc = FinalAcc} <- Succ].\n+\n+-spec failed_sweep([#sweep_participant{}], riak_kv_sweeper:index()) -> [].\n+failed_sweep(Failed, Index) ->\n+    [Module:failed_sweep(Index, Reason) ||\n+       #sweep_participant{module = Module, fail_reason = Reason } <- Failed].\n+\n+failed_sweep(Failed, Index, Reason) ->\n+    [Module:failed_sweep(Index, Reason) ||\n+       #sweep_participant{module = Module} <- Failed].\n+\n+-spec format_result(#sa{}) -> sweep_result().\n+format_result(#sa{swept_keys = SweptKeys,\n+                  active_p = Succ, failed_p = Failed}) ->\n+    {SweptKeys,\n+     format_result(succ, Succ) ++ format_result(fail, Failed)}.\n+\n+-spec format_result('succ'|'fail', [#sweep_participant{}]) ->\n+    [{riak_kv_sweeper:participant_module(), 'succ' | 'fail'}].\n+format_result(SuccFail, Results) ->\n+    [{Module, SuccFail} || #sweep_participant{module = Module} <- Results].\n+\n+%% Throttle depending on swept keys.\n+-spec maybe_throttle_sweep(binary(), #sa{}) -> #sa{}.\n+maybe_throttle_sweep(_RObjBin, #sa{throttle = {pace, _, _}} = Acc0) ->\n+    #sa{throttle = {pace, Limit, Wait},\n+        swept_keys = SweepKeys,\n+        throttle_total_wait_msecs = TotalWait} = Acc0,\n+    case SweepKeys rem Limit of\n+        0 ->\n+            %% We use receive after to throttle instead of sleep.\n+            %% This way we can respond on requests while throttling\n+            Acc1 = Acc0#sa{throttle = get_sweep_throttle(),\n+                           throttle_total_wait_msecs = TotalWait + Wait},\n+            timer:sleep(Wait),\n+            maybe_extra_throttle(Acc1);\n+        _ ->\n+            maybe_extra_throttle(Acc0)\n+    end;\n+\n+%% Throttle depending on total obj_size.\n+maybe_throttle_sweep(RObjBin, #sa{throttle = {obj_size, _, _}} = Acc0) ->\n+    #sa{throttle = {obj_size, Limit, Wait},\n+        total_obj_size = TotalObjSize,\n+        throttle_total_wait_msecs = TotalWait} = Acc0,\n+    ObjSize = byte_size(RObjBin),\n+    TotalObjSize1 = ObjSize + TotalObjSize,\n+    case (Limit =/= 0) andalso (TotalObjSize1 > Limit) of\n+        true ->\n+            %% We use receive after to throttle instead of sleep.\n+            %% This way we can respond on requests while throttling\n+            Acc1 = Acc0#sa{throttle = get_sweep_throttle(),\n+                           throttle_total_wait_msecs = TotalWait + Wait},\n+            timer:sleep(Wait),\n+            maybe_extra_throttle(Acc1#sa{total_obj_size = 0});\n+        _ ->\n+            maybe_extra_throttle(Acc0#sa{total_obj_size = TotalObjSize1})\n+    end.\n+\n+%% Throttle depending on how many objects the sweep modify.\n+-spec maybe_extra_throttle(#sa{}) -> #sa{}.\n+maybe_extra_throttle(Acc) ->\n+    #sa{throttle = Throttle,\n+        throttle_total_wait_msecs = TotalWait,\n+        num_mutated = NumMutated,\n+        num_deleted = NumDeleted} = Acc,\n+    {Limit, Wait} =\n+        case Throttle of\n+            {pace, Limit0, Wait0} ->\n+                {Limit0, Wait0};\n+            {obj_size, _Limit0, Wait0} ->\n+                {100, Wait0}\n+        end,\n+    %% +1 since some sweeps doesn't modify any objects\n+    case (NumMutated + NumDeleted + 1) rem Limit of\n+        0 ->\n+            timer:sleep(Wait),\n+            Acc#sa{throttle_total_wait_msecs = TotalWait + Wait};\n+        _ ->\n+            Acc\n+    end.\n+\n+%% Throttle depending on how many objects the sweep modify.\n+-spec get_sweep_throttle() -> {'obj_size'|'pace',\n+                               Limit :: non_neg_integer(),\n+                               Sleep :: non_neg_integer()}.\n+get_sweep_throttle() ->\n+    {Type, Limit, Sleep} =\n+        app_helper:get_env(riak_kv, sweep_throttle, ?DEFAULT_SWEEP_THROTTLE),\n+    case riak_kv_sweeper:in_sweep_window() of\n+        true ->\n+            Div = app_helper:get_env(riak_kv, sweep_window_throttle_div,\n+                                     ?SWEEP_WINDOW_THROTTLE_DIV),\n+            {Type, Limit, Sleep div Div};\n+        false ->\n+            {Type, Limit, Sleep}\n+    end.\n+\n+-spec send_to_sweep_worker('stop'\n+                           | {'disable', riak_kv_sweeper:participant_module()},\n+                           #sweep{}) -> 'ok' | 'no_pid'.\n+send_to_sweep_worker(Msg, #sweep{pid = Pid}) when is_pid(Pid) ->\n+    lager:debug(\"Send to sweep worker ~p: ~p\", [Pid, Msg]),\n+    Pid ! Msg;\n+\n+send_to_sweep_worker(Msg, #sweep{index = Index}) ->\n+    lager:info(\"no pid ~p to ~p \" , [Msg, Index]),\n+    no_pid.\n+\n+-spec maybe_receive_request(#sa{}) -> #sa{}.\n+maybe_receive_request(#sa{active_p = Active, failed_p = Fail } = Acc) ->\n+    receive\n+        stop ->\n+            Active1 =\n+                [ActiveSP#sweep_participant{fail_reason = sweep_stop } ||\n+                 #sweep_participant{} = ActiveSP <- Active],\n+            Acc#sa{active_p = [],  failed_p = Active1 ++ Fail};\n+        {disable, Module} ->\n+            case lists:keytake(Module, #sweep_participant.module, Active) of\n+                {value, SP, Active1} ->\n+                    Acc#sa{active_p = Active1,\n+                           failed_p = [SP#sweep_participant{fail_reason = disable} | Fail]};\n+                _ ->\n+                    Acc\n+            end\n+    after 0 ->\n+        Acc\n+    end.\n+\n+-spec apply_sweep_participant_funs({{riak_object:bucket(),\n+                                     riak_object:key()},\n+                                    riak_object:riak_object()}, #sa{}) ->\n+    {NumMutated :: non_neg_integer(), NumDeleted :: non_neg_integer(), #sa{}}.\n+apply_sweep_participant_funs({BKey, RObj}, SweepAcc) ->\n+    ActiveParticipants = SweepAcc#sa.active_p,\n+    FailedParticipants = SweepAcc#sa.failed_p,\n+    Index = SweepAcc#sa.index,\n+    BucketProps = SweepAcc#sa.bucket_props,\n+\n+    InitialAcc = {RObj,\n+                  FailedParticipants,\n+                  _Successful = [],\n+                  _NumMutated = 0,\n+                  _NumDeleted = 0,\n+                  BucketProps},\n+    FoldFun = fun(E, Acc) -> run_participant_fun(E, Index, BKey, Acc) end,\n+    Results = lists:foldl(FoldFun, InitialAcc, ActiveParticipants),\n+\n+    {_, Failed, Successful, NumMutated, NumDeleted, NewBucketProps} = Results,\n+\n+    SweepAcc1 = update_modified_objs_count(SweepAcc, NumMutated, NumDeleted),\n+    SweepAcc2 = SweepAcc1#sa{bucket_props = NewBucketProps},\n+    SweepAcc3 = update_sa_with_participant_results(SweepAcc2, Failed, Successful),\n+    {NumMutated, NumDeleted, SweepAcc3}.\n+\n+\n+%% Check if the sweep_participant have reached crash limit.\n+-spec run_participant_fun(#sweep_participant{},\n+                          riak_kv_sweeper:index(),\n+                          {riak_object:bucket(), riak_object:key()},\n+                          {riak_object:riak_object(),\n+                           [#sweep_participant{}],\n+                           [#sweep_participant{}],\n+                           NumMutated :: non_neg_integer(),\n+                           NumDeleted :: non_neg_integer(),\n+                           BucketProps :: []}) ->\n+    {riak_object:riak_object(),","path":"src/riak_kv_sweeper_fold.erl","position":null,"original_position":318,"commit_id":"d526acdcfe7a0967780017223f56f27c9f9f632b","original_commit_id":"1f4eaf7d814ecc72778a34d03dc2ec2dcda0eba8","user":{"login":"lehoff","id":193034,"avatar_url":"https://avatars.githubusercontent.com/u/193034?v=3","gravatar_id":"","url":"https://api.github.com/users/lehoff","html_url":"https://github.com/lehoff","followers_url":"https://api.github.com/users/lehoff/followers","following_url":"https://api.github.com/users/lehoff/following{/other_user}","gists_url":"https://api.github.com/users/lehoff/gists{/gist_id}","starred_url":"https://api.github.com/users/lehoff/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lehoff/subscriptions","organizations_url":"https://api.github.com/users/lehoff/orgs","repos_url":"https://api.github.com/users/lehoff/repos","events_url":"https://api.github.com/users/lehoff/events{/privacy}","received_events_url":"https://api.github.com/users/lehoff/received_events","type":"User","site_admin":false},"body":"Thx.","created_at":"2016-12-20T12:28:02Z","updated_at":"2016-12-20T12:28:02Z","html_url":"https://github.com/basho/riak_kv/pull/1562#discussion_r93227425","pull_request_url":"https://api.github.com/repos/basho/riak_kv/pulls/1562","_links":{"self":{"href":"https://api.github.com/repos/basho/riak_kv/pulls/comments/93227425"},"html":{"href":"https://github.com/basho/riak_kv/pull/1562#discussion_r93227425"},"pull_request":{"href":"https://api.github.com/repos/basho/riak_kv/pulls/1562"}}},"pull_request":{"url":"https://api.github.com/repos/basho/riak_kv/pulls/1562","id":96807526,"html_url":"https://github.com/basho/riak_kv/pull/1562","diff_url":"https://github.com/basho/riak_kv/pull/1562.diff","patch_url":"https://github.com/basho/riak_kv/pull/1562.patch","issue_url":"https://api.github.com/repos/basho/riak_kv/issues/1562","number":1562,"state":"open","locked":false,"title":"Merge riak_kv sweeper code to develop","user":{"login":"nickelization","id":4108538,"avatar_url":"https://avatars.githubusercontent.com/u/4108538?v=3","gravatar_id":"","url":"https://api.github.com/users/nickelization","html_url":"https://github.com/nickelization","followers_url":"https://api.github.com/users/nickelization/followers","following_url":"https://api.github.com/users/nickelization/following{/other_user}","gists_url":"https://api.github.com/users/nickelization/gists{/gist_id}","starred_url":"https://api.github.com/users/nickelization/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nickelization/subscriptions","organizations_url":"https://api.github.com/users/nickelization/orgs","repos_url":"https://api.github.com/users/nickelization/repos","events_url":"https://api.github.com/users/nickelization/events{/privacy}","received_events_url":"https://api.github.com/users/nickelization/received_events","type":"User","site_admin":false},"body":"There are still some things left to do before we release this code, but it's solid enough to merge it to develop.\r\n\r\nThe original sweeper code was written by a contractor, Mikael Lixenstrand. His development work still exist in various repos under branches name 'object_ttl'. This builds off of his work, adding lots of additional test coverage, fixing a bunch of bugs, and generally improving the quality of the code.\r\n\r\nThis PR depends on three other pull requests being merged first:\r\n\r\n- [ ] https://github.com/basho/riak_repl/pull/765\r\n- [ ] https://github.com/basho/riak_pb/pull/216\r\n- [ ] https://github.com/basho/riak_core/pull/882","created_at":"2016-12-06T21:19:15Z","updated_at":"2016-12-20T12:28:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"827742bd38e9ff19ca546210de34c6910876ef83","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/basho/riak_kv/pulls/1562/commits","review_comments_url":"https://api.github.com/repos/basho/riak_kv/pulls/1562/comments","review_comment_url":"https://api.github.com/repos/basho/riak_kv/pulls/comments{/number}","comments_url":"https://api.github.com/repos/basho/riak_kv/issues/1562/comments","statuses_url":"https://api.github.com/repos/basho/riak_kv/statuses/d526acdcfe7a0967780017223f56f27c9f9f632b","head":{"label":"basho:sweeper-2.3-integration","ref":"sweeper-2.3-integration","sha":"d526acdcfe7a0967780017223f56f27c9f9f632b","user":{"login":"basho","id":176293,"avatar_url":"https://avatars.githubusercontent.com/u/176293?v=3","gravatar_id":"","url":"https://api.github.com/users/basho","html_url":"https://github.com/basho","followers_url":"https://api.github.com/users/basho/followers","following_url":"https://api.github.com/users/basho/following{/other_user}","gists_url":"https://api.github.com/users/basho/gists{/gist_id}","starred_url":"https://api.github.com/users/basho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basho/subscriptions","organizations_url":"https://api.github.com/users/basho/orgs","repos_url":"https://api.github.com/users/basho/repos","events_url":"https://api.github.com/users/basho/events{/privacy}","received_events_url":"https://api.github.com/users/basho/received_events","type":"Organization","site_admin":false},"repo":{"id":791522,"name":"riak_kv","full_name":"basho/riak_kv","owner":{"login":"basho","id":176293,"avatar_url":"https://avatars.githubusercontent.com/u/176293?v=3","gravatar_id":"","url":"https://api.github.com/users/basho","html_url":"https://github.com/basho","followers_url":"https://api.github.com/users/basho/followers","following_url":"https://api.github.com/users/basho/following{/other_user}","gists_url":"https://api.github.com/users/basho/gists{/gist_id}","starred_url":"https://api.github.com/users/basho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basho/subscriptions","organizations_url":"https://api.github.com/users/basho/orgs","repos_url":"https://api.github.com/users/basho/repos","events_url":"https://api.github.com/users/basho/events{/privacy}","received_events_url":"https://api.github.com/users/basho/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/basho/riak_kv","description":"Riak Key/Value Store","fork":false,"url":"https://api.github.com/repos/basho/riak_kv","forks_url":"https://api.github.com/repos/basho/riak_kv/forks","keys_url":"https://api.github.com/repos/basho/riak_kv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/basho/riak_kv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/basho/riak_kv/teams","hooks_url":"https://api.github.com/repos/basho/riak_kv/hooks","issue_events_url":"https://api.github.com/repos/basho/riak_kv/issues/events{/number}","events_url":"https://api.github.com/repos/basho/riak_kv/events","assignees_url":"https://api.github.com/repos/basho/riak_kv/assignees{/user}","branches_url":"https://api.github.com/repos/basho/riak_kv/branches{/branch}","tags_url":"https://api.github.com/repos/basho/riak_kv/tags","blobs_url":"https://api.github.com/repos/basho/riak_kv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/basho/riak_kv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/basho/riak_kv/git/refs{/sha}","trees_url":"https://api.github.com/repos/basho/riak_kv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/basho/riak_kv/statuses/{sha}","languages_url":"https://api.github.com/repos/basho/riak_kv/languages","stargazers_url":"https://api.github.com/repos/basho/riak_kv/stargazers","contributors_url":"https://api.github.com/repos/basho/riak_kv/contributors","subscribers_url":"https://api.github.com/repos/basho/riak_kv/subscribers","subscription_url":"https://api.github.com/repos/basho/riak_kv/subscription","commits_url":"https://api.github.com/repos/basho/riak_kv/commits{/sha}","git_commits_url":"https://api.github.com/repos/basho/riak_kv/git/commits{/sha}","comments_url":"https://api.github.com/repos/basho/riak_kv/comments{/number}","issue_comment_url":"https://api.github.com/repos/basho/riak_kv/issues/comments{/number}","contents_url":"https://api.github.com/repos/basho/riak_kv/contents/{+path}","compare_url":"https://api.github.com/repos/basho/riak_kv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/basho/riak_kv/merges","archive_url":"https://api.github.com/repos/basho/riak_kv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/basho/riak_kv/downloads","issues_url":"https://api.github.com/repos/basho/riak_kv/issues{/number}","pulls_url":"https://api.github.com/repos/basho/riak_kv/pulls{/number}","milestones_url":"https://api.github.com/repos/basho/riak_kv/milestones{/number}","notifications_url":"https://api.github.com/repos/basho/riak_kv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/basho/riak_kv/labels{/name}","releases_url":"https://api.github.com/repos/basho/riak_kv/releases{/id}","deployments_url":"https://api.github.com/repos/basho/riak_kv/deployments","created_at":"2010-07-22T17:27:35Z","updated_at":"2016-12-10T12:39:12Z","pushed_at":"2016-12-20T12:12:00Z","git_url":"git://github.com/basho/riak_kv.git","ssh_url":"git@github.com:basho/riak_kv.git","clone_url":"https://github.com/basho/riak_kv.git","svn_url":"https://github.com/basho/riak_kv","homepage":"","size":13017,"stargazers_count":351,"watchers_count":351,"language":"Erlang","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":139,"mirror_url":null,"open_issues_count":180,"forks":139,"open_issues":180,"watchers":351,"default_branch":"develop"}},"base":{"label":"basho:develop","ref":"develop","sha":"9f0ddacee9e518f430477639aa1f5bc3f6e6890f","user":{"login":"basho","id":176293,"avatar_url":"https://avatars.githubusercontent.com/u/176293?v=3","gravatar_id":"","url":"https://api.github.com/users/basho","html_url":"https://github.com/basho","followers_url":"https://api.github.com/users/basho/followers","following_url":"https://api.github.com/users/basho/following{/other_user}","gists_url":"https://api.github.com/users/basho/gists{/gist_id}","starred_url":"https://api.github.com/users/basho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basho/subscriptions","organizations_url":"https://api.github.com/users/basho/orgs","repos_url":"https://api.github.com/users/basho/repos","events_url":"https://api.github.com/users/basho/events{/privacy}","received_events_url":"https://api.github.com/users/basho/received_events","type":"Organization","site_admin":false},"repo":{"id":791522,"name":"riak_kv","full_name":"basho/riak_kv","owner":{"login":"basho","id":176293,"avatar_url":"https://avatars.githubusercontent.com/u/176293?v=3","gravatar_id":"","url":"https://api.github.com/users/basho","html_url":"https://github.com/basho","followers_url":"https://api.github.com/users/basho/followers","following_url":"https://api.github.com/users/basho/following{/other_user}","gists_url":"https://api.github.com/users/basho/gists{/gist_id}","starred_url":"https://api.github.com/users/basho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basho/subscriptions","organizations_url":"https://api.github.com/users/basho/orgs","repos_url":"https://api.github.com/users/basho/repos","events_url":"https://api.github.com/users/basho/events{/privacy}","received_events_url":"https://api.github.com/users/basho/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/basho/riak_kv","description":"Riak Key/Value Store","fork":false,"url":"https://api.github.com/repos/basho/riak_kv","forks_url":"https://api.github.com/repos/basho/riak_kv/forks","keys_url":"https://api.github.com/repos/basho/riak_kv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/basho/riak_kv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/basho/riak_kv/teams","hooks_url":"https://api.github.com/repos/basho/riak_kv/hooks","issue_events_url":"https://api.github.com/repos/basho/riak_kv/issues/events{/number}","events_url":"https://api.github.com/repos/basho/riak_kv/events","assignees_url":"https://api.github.com/repos/basho/riak_kv/assignees{/user}","branches_url":"https://api.github.com/repos/basho/riak_kv/branches{/branch}","tags_url":"https://api.github.com/repos/basho/riak_kv/tags","blobs_url":"https://api.github.com/repos/basho/riak_kv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/basho/riak_kv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/basho/riak_kv/git/refs{/sha}","trees_url":"https://api.github.com/repos/basho/riak_kv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/basho/riak_kv/statuses/{sha}","languages_url":"https://api.github.com/repos/basho/riak_kv/languages","stargazers_url":"https://api.github.com/repos/basho/riak_kv/stargazers","contributors_url":"https://api.github.com/repos/basho/riak_kv/contributors","subscribers_url":"https://api.github.com/repos/basho/riak_kv/subscribers","subscription_url":"https://api.github.com/repos/basho/riak_kv/subscription","commits_url":"https://api.github.com/repos/basho/riak_kv/commits{/sha}","git_commits_url":"https://api.github.com/repos/basho/riak_kv/git/commits{/sha}","comments_url":"https://api.github.com/repos/basho/riak_kv/comments{/number}","issue_comment_url":"https://api.github.com/repos/basho/riak_kv/issues/comments{/number}","contents_url":"https://api.github.com/repos/basho/riak_kv/contents/{+path}","compare_url":"https://api.github.com/repos/basho/riak_kv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/basho/riak_kv/merges","archive_url":"https://api.github.com/repos/basho/riak_kv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/basho/riak_kv/downloads","issues_url":"https://api.github.com/repos/basho/riak_kv/issues{/number}","pulls_url":"https://api.github.com/repos/basho/riak_kv/pulls{/number}","milestones_url":"https://api.github.com/repos/basho/riak_kv/milestones{/number}","notifications_url":"https://api.github.com/repos/basho/riak_kv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/basho/riak_kv/labels{/name}","releases_url":"https://api.github.com/repos/basho/riak_kv/releases{/id}","deployments_url":"https://api.github.com/repos/basho/riak_kv/deployments","created_at":"2010-07-22T17:27:35Z","updated_at":"2016-12-10T12:39:12Z","pushed_at":"2016-12-20T12:12:00Z","git_url":"git://github.com/basho/riak_kv.git","ssh_url":"git@github.com:basho/riak_kv.git","clone_url":"https://github.com/basho/riak_kv.git","svn_url":"https://github.com/basho/riak_kv","homepage":"","size":13017,"stargazers_count":351,"watchers_count":351,"language":"Erlang","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":139,"mirror_url":null,"open_issues_count":180,"forks":139,"open_issues":180,"watchers":351,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/basho/riak_kv/pulls/1562"},"html":{"href":"https://github.com/basho/riak_kv/pull/1562"},"issue":{"href":"https://api.github.com/repos/basho/riak_kv/issues/1562"},"comments":{"href":"https://api.github.com/repos/basho/riak_kv/issues/1562/comments"},"review_comments":{"href":"https://api.github.com/repos/basho/riak_kv/pulls/1562/comments"},"review_comment":{"href":"https://api.github.com/repos/basho/riak_kv/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/basho/riak_kv/pulls/1562/commits"},"statuses":{"href":"https://api.github.com/repos/basho/riak_kv/statuses/d526acdcfe7a0967780017223f56f27c9f9f632b"}}}}
{"actor":{"display_login":"lehoff"}}