From d66a102bb2bfc17a2697190e92dc9173475848ef Mon Sep 17 00:00:00 2001 From: Jared Morrow Date: Thu, 4 Sep 2014 16:13:31 -0600 Subject: [PATCH 01/25] Set rebar.config deps back to 2.0 branches --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 80acd85..9f4f6bd 100644 --- a/rebar.config +++ b/rebar.config @@ -6,7 +6,7 @@ {cover_enabled, true}. {xref_checks, [undefined_function_calls]}. {deps, [{lager, "2.0.3", {git, "git://github.com/basho/lager.git", {tag, "2.0.3"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.0.0"}}}]}. + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {branch, "2.0"}}}]}. {port_specs, [{".*", "priv/riak_ensemble.so", From 1eb20eb3491326aaa0d733448f13864a826bcc19 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Thu, 2 Apr 2015 11:33:03 -0500 Subject: [PATCH 02/25] Add Apache 2 License. --- LICENSE | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e454a52 --- /dev/null +++ b/LICENSE @@ -0,0 +1,178 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + From 05713b17351e79ea0963447f27553888af3f8f8f Mon Sep 17 00:00:00 2001 From: Ian Milligan Date: Tue, 26 May 2015 23:34:11 -0700 Subject: [PATCH 03/25] Avoid locking when opening a synctree leveldb (cherry picked from commit 4f936c3cc85bef5ef3d968e62736bd88e804b8ae) --- src/synctree_leveldb.erl | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/synctree_leveldb.erl b/src/synctree_leveldb.erl index 046f218..01e7f60 100644 --- a/src/synctree_leveldb.erl +++ b/src/synctree_leveldb.erl @@ -60,33 +60,30 @@ init_ets() -> -spec new(_) -> state(). new(Opts) -> Path = get_path(Opts), - {ok, DB} = get_leveldb_ref(Path), + {ok, DB} = maybe_open_leveldb(Path, ?RETRIES), Id = get_tree_id(Opts), ?STATE{id=Id, path=Path, db=DB}. -get_leveldb_ref(Path) -> - %% Serialize in case multiple same-path trees are created at the same time - global:trans({{?MODULE, Path}, self()}, - fun() -> maybe_open_leveldb(Path) end). - -maybe_open_leveldb(Path) -> +maybe_open_leveldb(Path, Retries) -> %% Check if we have already opened this LevelDB instance, which can %% occur when peers are sharing the same on-disk instance. case ets:lookup(?MODULE, Path) of [{_, DB}] -> {ok, DB}; _ -> - ok = filelib:ensure_dir(Path), - {ok, DB} = safe_open(?RETRIES, Path, leveldb_opts()), - %% TODO: Storing LevelDB refs in ETS prevents DBs from ever - %% closing. If a given node is no longer part of any - %% ensembles that need a given synctree we should close - %% it. For now, users will need to restart a node to - %% close unneeded. - true = ets:insert_new(?MODULE, {Path, DB}), - {ok, DB} + ok = filelib:ensure_dir(Path), + case eleveldb:open(Path, leveldb_opts()) of + {ok, DB} -> + %% If eleveldb:open succeeded, we should have the only ref + true = ets:insert_new(?MODULE, {Path, DB}), + {ok, DB}; + _ when Retries > 0 -> + timer:sleep(100), + maybe_open_leveldb(Path, Retries - 1) + end end. + get_path(Opts) -> case proplists:get_value(path, Opts) of undefined -> From 966862f79af6319911219be3b1ad1af8858f3255 Mon Sep 17 00:00:00 2001 From: Ian Milligan Date: Thu, 28 May 2015 14:50:18 -0700 Subject: [PATCH 04/25] Remove unused safe_open and reopen functions from synctree_leveldb (cherry picked from commit 7744c34c925c446607ada423d26c83d0b580e25b) --- src/synctree_leveldb.erl | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/synctree_leveldb.erl b/src/synctree_leveldb.erl index 01e7f60..69d005d 100644 --- a/src/synctree_leveldb.erl +++ b/src/synctree_leveldb.erl @@ -21,7 +21,6 @@ -export([init_ets/0, new/1, - reopen/1, fetch/3, exists/2, store/3, @@ -152,21 +151,6 @@ store(Updates, State=?STATE{id=Id, db=DB}) -> _ = eleveldb:write(DB, DBUpdates, []), State. -reopen(State=?STATE{db=DB, path=Path}) -> - _ = eleveldb:close(DB), - ok = filelib:ensure_dir(Path), - {ok, NewDB} = safe_open(?RETRIES, Path, leveldb_opts()), - State?STATE{db=NewDB}. - -safe_open(Retries, Path, Opts) -> - case eleveldb:open(Path, Opts) of - {ok, DB} -> - {ok, DB}; - _ when (Retries > 0) -> - timer:sleep(100), - safe_open(Retries-1, Path, Opts) - end. - timestamp({Mega, Secs, Micro}) -> Mega*1000*1000*1000*1000 + Secs * 1000 * 1000 + Micro. From c9012615112eb08ebee77664dd097f6f14db05ca Mon Sep 17 00:00:00 2001 From: Christopher Meiklejohn Date: Tue, 24 Feb 2015 21:04:29 -0500 Subject: [PATCH 05/25] Use custom script for tests. Use the custom script for running the tests; override the tools.mk target. (cherry picked from commit f70cf2b234aade6383b2bb736772f8e55b6a0927) --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 11426e0..6be77c6 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,6 @@ testdeps: deps $(REBAR) -C rebar.test.config get-deps $(REBAR) -C rebar.test.config compile -test: failtest - failtest: $(error Do not use 'make test', use 'make runtests') @@ -36,3 +34,6 @@ ifeq ($(REBAR),) $(error "Rebar not found. Please set REBAR variable or update PATH") endif +## Override test after tools.mk; use custom test runner for isolation. +test: testdeps + bash test/run.sh From 50417bb9465d9fd37961e4c2525d101aed3cbb72 Mon Sep 17 00:00:00 2001 From: Doug Rohrer Date: Thu, 2 Jul 2015 14:22:24 -0400 Subject: [PATCH 06/25] Update rebar.config to float on 2.1 branch of eleveldb. --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index d158177..98484d7 100644 --- a/rebar.config +++ b/rebar.config @@ -8,7 +8,7 @@ {cover_enabled, true}. {xref_checks, [undefined_function_calls]}. {deps, [{lager, "2.0.3", {git, "git://github.com/basho/lager.git", {tag, "2.0.3"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.1.0"}}}]}. + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {branch, "2.1"}}}]}. {port_specs, [{".*", "priv/riak_ensemble.so", From c788aa6d7a4917b6b1c773b4e73f34142ddcc35c Mon Sep 17 00:00:00 2001 From: Brett Hazen Date: Tue, 13 Oct 2015 14:39:05 -0600 Subject: [PATCH 07/25] Update the lager version to 2.2.0 --- rebar.config | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 98484d7..473ca41 100644 --- a/rebar.config +++ b/rebar.config @@ -7,8 +7,10 @@ {dir, "edoc"}]}. {cover_enabled, true}. {xref_checks, [undefined_function_calls]}. -{deps, [{lager, "2.0.3", {git, "git://github.com/basho/lager.git", {tag, "2.0.3"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {branch, "2.1"}}}]}. +{deps, [ + {lager, "(2.0|2.1|2.2).*", {git, "git://github.com/basho/lager.git", {tag, "2.2.0"}}}, + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {branch, "2.1"}}} +]}. {port_specs, [{".*", "priv/riak_ensemble.so", From abad051dc2ccc350e7d1734dee266f93868c3e2a Mon Sep 17 00:00:00 2001 From: Doug Rohrer Date: Wed, 24 Feb 2016 12:00:17 -0500 Subject: [PATCH 08/25] Bump eleveldb to 2.2.14 for Riak 2.2.0 release --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 473ca41..c2b4ed1 100644 --- a/rebar.config +++ b/rebar.config @@ -9,7 +9,7 @@ {xref_checks, [undefined_function_calls]}. {deps, [ {lager, "(2.0|2.1|2.2).*", {git, "git://github.com/basho/lager.git", {tag, "2.2.0"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {branch, "2.1"}}} + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {branch, "2.2"}}} ]}. {port_specs, From a14b2fb2bbce2917c2cdb0959cf27152f0108dd5 Mon Sep 17 00:00:00 2001 From: Brian Sparrow Date: Thu, 24 Mar 2016 14:40:05 -0400 Subject: [PATCH 09/25] Upgrade eleveldb to 2.2.15 Pick up fix for write buffer data loss bug --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index c2b4ed1..f9c7c5a 100644 --- a/rebar.config +++ b/rebar.config @@ -9,7 +9,7 @@ {xref_checks, [undefined_function_calls]}. {deps, [ {lager, "(2.0|2.1|2.2).*", {git, "git://github.com/basho/lager.git", {tag, "2.2.0"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {branch, "2.2"}}} + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.2.15"}}} ]}. {port_specs, From bf3093805b0596f5d89e4867f562e24466bb4224 Mon Sep 17 00:00:00 2001 From: Nick Marino Date: Wed, 29 Jun 2016 10:56:04 -0400 Subject: [PATCH 10/25] Use native get_env/3 instead of our own version No reason to reimplement this functionality when we can just use the code in the application module. --- src/riak_ensemble_config.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/riak_ensemble_config.erl b/src/riak_ensemble_config.erl index 4bbf2b9..7d4aa3d 100644 --- a/src/riak_ensemble_config.erl +++ b/src/riak_ensemble_config.erl @@ -127,9 +127,4 @@ notfound_read_delay() -> get_env(notfound_read_delay, 1). get_env(Key, Default) -> - case application:get_env(riak_ensemble, Key) of - undefined -> - Default; - {_, Val} -> - Val - end. + application:get_env(riak_ensemble, Key, Default). From 7ebcb11102e5d1fb46a8ac1046fcea988cc72778 Mon Sep 17 00:00:00 2001 From: Nick Marino Date: Wed, 29 Jun 2016 13:02:17 -0400 Subject: [PATCH 11/25] Only compile and export debug_local_get for tests --- src/riak_ensemble_peer.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/riak_ensemble_peer.erl b/src/riak_ensemble_peer.erl index 45c536b..1f41c07 100644 --- a/src/riak_ensemble_peer.erl +++ b/src/riak_ensemble_peer.erl @@ -30,7 +30,6 @@ -export([join/2, join/3, update_members/3, get_leader/1, backend_pong/1]). -export([kget/4, kget/5, kupdate/6, kput_once/5, kover/5, kmodify/6, kdelete/4, ksafe_delete/5, obj_value/2, obj_value/3]). --export([debug_local_get/2]). -export([setup/2]). -export([probe/2, election/2, prepare/2, leading/2, following/2, probe/3, election/3, prepare/3, leading/3, following/3]). @@ -45,6 +44,11 @@ get_info/1, stable_views/2, tree_info/1, watch_leader_status/1, stop_watching/1]). +%% Backdoor for unit testing +-ifdef(TEST). +-export([debug_local_get/2]). +-endif. + %% Exported internal callback functions -export([do_kupdate/4, do_kput_once/4, do_kmodify/4]). @@ -333,12 +337,14 @@ local_get(Pid, Key, Timeout) when is_pid(Pid) -> local_put(Pid, Key, Obj, Timeout) when is_pid(Pid) -> riak_ensemble_router:sync_send_event(Pid, {local_put, Key, Obj}, Timeout). +-ifdef(TEST). %% Acts like local_get, but can be used for any peer, not just the leader. %% Should only be used for testing purposes, since values obtained via %% this function provide no consistency guarantees whatsoever. -spec debug_local_get(pid(), term()) -> std_reply(). debug_local_get(Pid, Key) -> gen_fsm:sync_send_all_state_event(Pid, {debug_local_get, Key}). +-endif. %%%=================================================================== %%% Core Protocol From 555ce3fc1d9c0c23ba2a559f833dfb226bc2e17c Mon Sep 17 00:00:00 2001 From: Nick Marino Date: Wed, 29 Jun 2016 13:31:53 -0400 Subject: [PATCH 12/25] Switch leadership watcher cleanup to use monitors This takes a bit of extra code, but is more idiomatic and predictable than the old approach. --- src/riak_ensemble_peer.erl | 58 +++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/riak_ensemble_peer.erl b/src/riak_ensemble_peer.erl index 1f41c07..fe772e3 100644 --- a/src/riak_ensemble_peer.erl +++ b/src/riak_ensemble_peer.erl @@ -140,7 +140,7 @@ async :: pid(), tree :: pid(), lease :: riak_ensemble_lease:lease_ref(), - watchers = [] :: [pid()], + watchers = [] :: [{pid(), reference()}], self :: pid() }). @@ -1856,12 +1856,17 @@ handle_event({watch_leader_status, Pid}, StateName, State) when node(Pid) =/= no {next_state, StateName, State}; handle_event({watch_leader_status, Pid}, StateName, State = #state{watchers = Watchers}) -> _ = notify_leader_status(Pid, StateName, State), - %% Might as well take this opportunity to prune any dead pids that are in the list - NewWatcherList = [P || P <- [Pid | Watchers], is_process_alive(P)], - {next_state, StateName, State#state{watchers = NewWatcherList}}; + MRef = erlang:monitor(process, Pid), + {next_state, StateName, State#state{watchers = [{Pid, MRef} | Watchers]}}; handle_event({stop_watching, Pid}, StateName, State = #state{watchers = Watchers}) -> - NewWatcherList = lists:delete(Pid, Watchers), - {next_state, StateName, State#state{watchers = NewWatcherList}}; + case remove_watcher(Pid, Watchers) of + not_found -> + lager:warning("Tried to stop watching for pid ~p, but did not find it in watcher list"), + {next_state, StateName, State}; + {MRef, NewWatcherList} -> + erlang:demonitor(MRef, [flush]), + {next_state, StateName, State#state{watchers = NewWatcherList}} + end; handle_event({reply, ReqId, Peer, Reply}, StateName, State) -> State2 = handle_reply(ReqId, Peer, Reply, State), {next_state, StateName, State2}; @@ -1895,9 +1900,25 @@ handle_sync_event(_Event, _From, StateName, State) -> {reply, Reply, StateName, State}. %% -spec handle_info(_, atom(), state()) -> next_state(). -handle_info({'DOWN', Ref, _, Pid, Reason}, StateName, - #state{mod=Mod, modstate=ModState}=State) -> - case Mod:handle_down(Ref, Pid, Reason, ModState) of +handle_info({'DOWN', MRef, _, Pid, Reason}, StateName, State) -> + Watchers = State#state.watchers, + case remove_watcher(Pid, Watchers) of + {MRef, NewWatcherList} -> + {next_state, StateName, State#state{watchers = NewWatcherList}}; + not_found -> + %% If the DOWN signal was not for a watcher, we must pass it through + %% to the callback module in case that's where it's supposed to go + module_handle_down(MRef, Pid, Reason, StateName, State) + end; +handle_info(quorum_timeout, StateName, State) -> + State2 = quorum_timeout(State), + {next_state, StateName, State2}; +handle_info(_Info, StateName, State) -> + {next_state, StateName, State}. + +module_handle_down(MRef, Pid, Reason, StateName, State) -> + #state{mod=Mod, modstate=ModState} = State, + case Mod:handle_down(MRef, Pid, Reason, ModState) of false -> State2 = maybe_restart_worker(Pid, State), {next_state, StateName, State2}; @@ -1906,12 +1927,7 @@ handle_info({'DOWN', Ref, _, Pid, Reason}, StateName, {reset, ModState2} -> State2 = State#state{modstate=ModState2}, step_down(State2) - end; -handle_info(quorum_timeout, StateName, State) -> - State2 = quorum_timeout(State), - {next_state, StateName, State2}; -handle_info(_Info, StateName, State) -> - {next_state, StateName, State}. + end. -spec terminate(_,_,_) -> ok. terminate(_Reason, _StateName, _State) -> @@ -2033,8 +2049,8 @@ existing_leader(_Replies, Abandoned, #fact{epoch=Epoch, seq=Seq, leader=Leader}) undefined end. -notify_leader_status(PidList, StateName, State) when is_list(PidList) -> - [notify_leader_status(P, StateName, State) || P <- PidList]; +notify_leader_status(WatcherList, StateName, State) when is_list(WatcherList) -> + [notify_leader_status(Pid, StateName, State) || {Pid, _MRef} <- WatcherList]; notify_leader_status(Pid, leading, State = #state{id = Id, ensemble = Ensemble}) -> Pid ! {is_leading, self(), Id, Ensemble, epoch(State)}; notify_leader_status(Pid, _, State = #state{id = Id, ensemble = Ensemble}) -> @@ -2058,6 +2074,14 @@ peer(Id, #state{id=Id}) -> peer(Id, #state{ensemble=Ensemble}) -> riak_ensemble_manager:get_peer_pid(Ensemble, Id). +remove_watcher(Pid, WatcherList) -> + case lists:keytake(Pid, 1, WatcherList) of + false -> + not_found; + {value, {_Pid, MRef}, NewWatcherList} -> + {MRef, NewWatcherList} + end. + %%%=================================================================== %%% Behaviour Interface %%%=================================================================== From c2326f9c997dc0fc640f8589480cfb91b6adb2bd Mon Sep 17 00:00:00 2001 From: Nick Marino Date: Wed, 29 Jun 2016 15:00:40 -0400 Subject: [PATCH 13/25] Add wait_until function to ens_test This will come in handy for a new test I'm about to commit --- test/ens_test.erl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/ens_test.erl b/test/ens_test.erl index 312e625..76486dc 100644 --- a/test/ens_test.erl +++ b/test/ens_test.erl @@ -94,3 +94,21 @@ read_until(Key) -> timer:sleep(100), read_until(Key) end. + +%% @doc Utility function used to construct test predicates. Retries the +%% function `Fun' until it returns `true', or until the maximum +%% number of retries is reached. +wait_until(Fun) when is_function(Fun) -> + wait_until(Fun, 50, 100). + +wait_until(Fun, Retry, Delay) when Retry > 0 -> + Res = Fun(), + case Res of + true -> + ok; + _ when Retry == 1 -> + {fail, Res}; + _ -> + timer:sleep(Delay), + wait_until(Fun, Retry-1, Delay) + end. From a3f0f99991f2ef0d5a4326a035869ebeb579634f Mon Sep 17 00:00:00 2001 From: Nick Marino Date: Wed, 29 Jun 2016 15:01:26 -0400 Subject: [PATCH 14/25] Add get_watchers function for testing This allows us to verify that watchers get removed from the internal state when they unsubscribe or die. --- src/riak_ensemble_peer.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/riak_ensemble_peer.erl b/src/riak_ensemble_peer.erl index fe772e3..c240a0c 100644 --- a/src/riak_ensemble_peer.erl +++ b/src/riak_ensemble_peer.erl @@ -44,9 +44,10 @@ get_info/1, stable_views/2, tree_info/1, watch_leader_status/1, stop_watching/1]). -%% Backdoor for unit testing +%% Backdoors for unit testing -ifdef(TEST). -export([debug_local_get/2]). +-export([get_watchers/1]). -endif. %% Exported internal callback functions @@ -216,6 +217,12 @@ watch_leader_status(Pid) when is_pid(Pid) -> stop_watching(Pid) when is_pid(Pid) -> gen_fsm:send_all_state_event(Pid, {stop_watching, self()}). +-ifdef(TEST). +-spec get_watchers(pid()) -> [{pid(), reference()}]. +get_watchers(Pid) when is_pid(Pid) -> + gen_fsm:sync_send_all_state_event(Pid, get_watchers). +-endif. + get_info(Pid) when is_pid(Pid) -> gen_fsm:sync_send_all_state_event(Pid, get_info, infinity). @@ -1895,6 +1902,8 @@ handle_sync_event(tree_info, _From, StateName, State=#state{tree_trust=Trust, handle_sync_event({debug_local_get, Key}, From, StateName, State) -> State2 = do_local_get(From, Key, State), {next_state, StateName, State2}; +handle_sync_event(get_watchers, _From, StateName, State) -> + {reply, State#state.watchers, StateName, State}; handle_sync_event(_Event, _From, StateName, State) -> Reply = ok, {reply, Reply, StateName, State}. From 8430868b96e3f016a4cccc95906a0cd27202c0e2 Mon Sep 17 00:00:00 2001 From: Nick Marino Date: Wed, 29 Jun 2016 15:08:04 -0400 Subject: [PATCH 15/25] Add leadership_watchers test --- test/TESTS | 1 + test/leadership_watchers.erl | 67 ++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 test/leadership_watchers.erl diff --git a/test/TESTS b/test/TESTS index 0b8d1fe..2fd8ab8 100644 --- a/test/TESTS +++ b/test/TESTS @@ -13,3 +13,4 @@ lease_test ensemble_tests_pure replace_members_test read_tombstone_test +leadership_watchers diff --git a/test/leadership_watchers.erl b/test/leadership_watchers.erl new file mode 100644 index 0000000..43733d4 --- /dev/null +++ b/test/leadership_watchers.erl @@ -0,0 +1,67 @@ +-module(leadership_watchers). +-compile(export_all). +-include_lib("eunit/include/eunit.hrl"). + +run_test_() -> + ens_test:run(fun scenario/0, 40). + +scenario() -> + ens_test:start(3), + ens_test:wait_stable(root), + + Pid = riak_ensemble_manager:get_leader_pid(root), + + ?assertEqual(0, length(riak_ensemble_peer:get_watchers(Pid))), + ?debugMsg("Watching leader"), + riak_ensemble_peer:watch_leader_status(Pid), + ?assertEqual(1, length(riak_ensemble_peer:get_watchers(Pid))), + ?debugMsg("Waiting for is_leading notification"), + wait_status(is_leading, Pid), + + ?debugMsg("Stopping watching leader"), + riak_ensemble_peer:stop_watching(Pid), + ?assertEqual(0, length(riak_ensemble_peer:get_watchers(Pid))), + + ?debugMsg("Starting watching leader again"), + riak_ensemble_peer:watch_leader_status(Pid), + ?assertEqual(1, length(riak_ensemble_peer:get_watchers(Pid))), + wait_status(is_leading, Pid), + + ?debugMsg("Suspending leader, and waiting for new leader to be elected"), + erlang:suspend_process(Pid), + ens_test:wait_stable(root), + + ?debugMsg("Resuming former leader, and waiting for is_not_leading notification"), + erlang:resume_process(Pid), + wait_status(is_not_leading, Pid), + + ?debugMsg("Watching leader in external process"), + Watcher = spawn_link(fun() -> watcher(Pid) end), + wait_until_n_watchers(2, Pid), + + ?debugMsg("Killing external watcher process and checking peer state"), + Watcher ! die, + wait_until_n_watchers(1, Pid). + +wait_status(Status, Pid) -> + receive + {Status, Pid, _, _, _} -> + ok + after + 5000 -> + throw(timeout_waiting_for_leader_status) + end. + +%% Just a fun to spawn a process that will exit when we tell it to, so we +%% can test that the watcher gets removed from the ensemble peer state when +%% a watcher process dies. +watcher(PeerPid) -> + riak_ensemble_peer:watch_leader_status(PeerPid), + receive + die -> + ok + end. + +wait_until_n_watchers(N, Pid) -> + WatcherCountCheck = fun() -> N =:= length(riak_ensemble_peer:get_watchers(Pid)) end, + ?assertEqual(ok, ens_test:wait_until(WatcherCountCheck)). From 5b1164827deb04a5ffd8f039ac792fbe4022957d Mon Sep 17 00:00:00 2001 From: Nick Marino Date: Wed, 29 Jun 2016 17:49:20 -0400 Subject: [PATCH 16/25] Guard against the same pid watching twice --- src/riak_ensemble_peer.erl | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/riak_ensemble_peer.erl b/src/riak_ensemble_peer.erl index c240a0c..7d20e7f 100644 --- a/src/riak_ensemble_peer.erl +++ b/src/riak_ensemble_peer.erl @@ -1862,9 +1862,15 @@ handle_event({watch_leader_status, Pid}, StateName, State) when node(Pid) =/= no [Pid, State#state.id]), {next_state, StateName, State}; handle_event({watch_leader_status, Pid}, StateName, State = #state{watchers = Watchers}) -> - _ = notify_leader_status(Pid, StateName, State), - MRef = erlang:monitor(process, Pid), - {next_state, StateName, State#state{watchers = [{Pid, MRef} | Watchers]}}; + case is_watcher(Pid, Watchers) of + true -> + lager:debug("Got watch_leader_status for ~p, but pid already in watchers list"), + {next_state, StateName, State}; + false -> + _ = notify_leader_status(Pid, StateName, State), + MRef = erlang:monitor(process, Pid), + {next_state, StateName, State#state{watchers = [{Pid, MRef} | Watchers]}} + end; handle_event({stop_watching, Pid}, StateName, State = #state{watchers = Watchers}) -> case remove_watcher(Pid, Watchers) of not_found -> @@ -2083,6 +2089,14 @@ peer(Id, #state{id=Id}) -> peer(Id, #state{ensemble=Ensemble}) -> riak_ensemble_manager:get_peer_pid(Ensemble, Id). +is_watcher(Pid, WatcherList) -> + case lists:keyfind(Pid, 1, WatcherList) of + false -> + false; + _ -> + true + end. + remove_watcher(Pid, WatcherList) -> case lists:keytake(Pid, 1, WatcherList) of false -> From 8cb2b000ec6254541f786578c1109ebbea3c6014 Mon Sep 17 00:00:00 2001 From: Nick Marino Date: Wed, 29 Jun 2016 17:49:54 -0400 Subject: [PATCH 17/25] Switch leader status watch logging to debug Seeing this message may be indicative of a bug, but most operators aren't going to care about this particular message, so switch it from warning to debug. --- src/riak_ensemble_peer.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/riak_ensemble_peer.erl b/src/riak_ensemble_peer.erl index 7d20e7f..45f10e0 100644 --- a/src/riak_ensemble_peer.erl +++ b/src/riak_ensemble_peer.erl @@ -1858,8 +1858,8 @@ setup({init, Args}, State0=#state{id=Id, ensemble=Ensemble, ets=ETS, mod=Mod}) - -spec handle_event(_, atom(), state()) -> {next_state, atom(), state()}. handle_event({watch_leader_status, Pid}, StateName, State) when node(Pid) =/= node() -> - lager:warning("Remote pid ~p not allowed to watch_leader_status on ensemble peer ~p", - [Pid, State#state.id]), + lager:debug("Remote pid ~p not allowed to watch_leader_status on ensemble peer ~p", + [Pid, State#state.id]), {next_state, StateName, State}; handle_event({watch_leader_status, Pid}, StateName, State = #state{watchers = Watchers}) -> case is_watcher(Pid, Watchers) of @@ -1874,7 +1874,7 @@ handle_event({watch_leader_status, Pid}, StateName, State = #state{watchers = Wa handle_event({stop_watching, Pid}, StateName, State = #state{watchers = Watchers}) -> case remove_watcher(Pid, Watchers) of not_found -> - lager:warning("Tried to stop watching for pid ~p, but did not find it in watcher list"), + lager:debug("Tried to stop watching for pid ~p, but did not find it in watcher list"), {next_state, StateName, State}; {MRef, NewWatcherList} -> erlang:demonitor(MRef, [flush]), From b7fbac6bd074e4764b2deeec9c884797bb401778 Mon Sep 17 00:00:00 2001 From: Fred Dushin Date: Tue, 5 Jul 2016 15:56:10 -0400 Subject: [PATCH 18/25] Updated rebar.config to point to eleveldb 2.2.19 --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 7545dd0..7ad1e85 100644 --- a/rebar.config +++ b/rebar.config @@ -10,7 +10,7 @@ {xref_checks, [undefined_function_calls]}. {deps, [ {lager, "(2.0|2.1|2.2).*", {git, "git://github.com/basho/lager.git", {tag, "2.2.0"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.2.15"}}} + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.2.19"}}} ]}. {port_specs, From 57720223e998efd77d4d85b6148442063fb32144 Mon Sep 17 00:00:00 2001 From: Fred Dushin Date: Wed, 6 Jul 2016 16:06:07 -0400 Subject: [PATCH 19/25] bumped lager and eleveldb dependencies (making eleveldb dependency floating on 2.2 branch) --- rebar.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 7ad1e85..9744fe1 100644 --- a/rebar.config +++ b/rebar.config @@ -9,8 +9,8 @@ {cover_enabled, true}. {xref_checks, [undefined_function_calls]}. {deps, [ - {lager, "(2.0|2.1|2.2).*", {git, "git://github.com/basho/lager.git", {tag, "2.2.0"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.2.19"}}} + {lager, "(2.0|2.1|2.2).*", {git, "git://github.com/basho/lager.git", {tag, "2.2.3"}}}, + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {branch, "2.2"}}} ]}. {port_specs, From fe59277f35254343dae042c7c20b8fe19f1c1d21 Mon Sep 17 00:00:00 2001 From: Nick Marino Date: Wed, 24 Aug 2016 17:18:56 -0400 Subject: [PATCH 20/25] Add debug log messages for peer state transitions --- src/riak_ensemble_peer.erl | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/riak_ensemble_peer.erl b/src/riak_ensemble_peer.erl index 45f10e0..5166413 100644 --- a/src/riak_ensemble_peer.erl +++ b/src/riak_ensemble_peer.erl @@ -359,7 +359,7 @@ debug_local_get(Pid, Key) -> -spec probe(_, state()) -> next_state(). probe(init, State) -> - ?OUT("~p: probe~n", [State#state.id]), + lager:debug("~p: probe init", [State#state.id]), State2 = set_leader(undefined, State), case is_pending(State2) of true -> @@ -393,22 +393,22 @@ probe(Msg, From, State) -> common(Msg, From, State, probe). pending(init, State) -> + lager:debug("~p: pending init", [State#state.id]), State2 = set_timer(?PENDING_TIMEOUT, pending_timeout, State), {next_state, pending, State2#state{tree_trust=false}}; pending(pending_timeout, State) -> + lager:debug("~p: pending_timeout", [State#state.id]), probe({timeout, []}, State); pending({prepare, Id, NextEpoch, From}, State=#state{fact=Fact}) -> Epoch = epoch(State), case NextEpoch > Epoch of true -> - ?OUT("~p: accepting ~p from ~p (~p)~n", - [State#state.id, NextEpoch, Id, Epoch]), + lager:debug("~p: accepting ~p from ~p (~p)", [Id, NextEpoch, Id, Epoch]), reply(From, Fact, State), State2 = cancel_timer(State), prefollow({init, Id, NextEpoch}, State2); false -> - ?OUT("~p: rejecting ~p from ~p (~p)~n", - [State#state.id, NextEpoch, Id, Epoch]), + lager:debug("~p: rejecting ~p from ~p (~p)", [Id, NextEpoch, Id, Epoch]), {next_state, pending, State} end; pending({commit, NewFact, From}, State) -> @@ -416,10 +416,14 @@ pending({commit, NewFact, From}, State) -> case NewFact#fact.epoch >= Epoch of true -> reply(From, ok, State), + lager:debug("~p: accepting commit from ~p for epoch ~p", + [State#state.id, From, NewFact#fact.epoch]), State2 = local_commit(NewFact, State), State3 = cancel_timer(State2), following(init, State3); false -> + lager:debug("~p: ignoring outdated commit from ~p (~p < ~p)", + [State#state.id, From, NewFact#fact.epoch, Epoch]), {next_state, pending, State} end; pending(Msg, State) -> @@ -444,6 +448,7 @@ maybe_follow(Leader, State) -> %%%=================================================================== repair(init, State=#state{tree=Tree}) -> + lager:debug("~p: repair", [State#state.id]), riak_ensemble_peer_tree:async_repair(Tree), {next_state, repair, State#state{tree_trust=false}}; repair(repair_complete, State) -> @@ -574,7 +579,7 @@ prefollow(Msg, From, State) -> -spec prepare(_, state()) -> next_state(). prepare(init, State=#state{id=Id}) -> %% TODO: Change this hack where we keep old state and reincrement - ?OUT("~p: prepare~n", [State#state.id]), + lager:debug("~p: prepare", [State#state.id]), {NextEpoch, _} = increment_epoch(State), %% io:format("Preparing ~p to ~p :: ~p~n", [NextEpoch, %% views(State), @@ -623,7 +628,6 @@ prelead(Msg, From, State) -> -spec leading(_, state()) -> next_state(). leading(init, State=#state{id=_Id, watchers=Watchers}) -> - ?OUT("~p: Leading~n", [_Id]), _ = lager:info("~p: Leading~n", [_Id]), start_exchange(State), _ = notify_leader_status(Watchers, leading, State), @@ -791,7 +795,7 @@ reset_follower_timer(State) -> following(not_ready, State) -> following(init, State#state{ready=false}); following(init, State) -> - ?OUT("~p: Following: ~p~n", [State#state.id, leader(State)]), + lager:debug("~p: Following: ~p", [State#state.id, leader(State)]), start_exchange(State), State2 = reset_follower_timer(State), {next_state, following, State2}; @@ -800,6 +804,7 @@ following(exchange_complete, State) -> State2 = State#state{tree_trust=true}, {next_state, following, State2}; following(exchange_failed, State) -> + lager:debug("~p: exchange failed", [State#state.id]), probe(init, State); following({commit, Fact, From}, State) -> State3 = case Fact#fact.epoch >= epoch(State) of @@ -827,8 +832,7 @@ following({commit, Fact, From}, State) -> %% {next_state, following, State} %% end; following(follower_timeout, State) -> - ?OUT("~p: follower_timeout from ~p~n", [State#state.id, leader(State)]), - %% io:format("~p: follower_timeout from ~p~n", [State#state.id, leader(State)]), + lager:debug("~p: follower_timeout from ~p", [State#state.id, leader(State)]), abandon(State#state{timer=undefined}); following({check_epoch, Leader, Epoch, From}, State) -> case check_epoch(Leader, Epoch, State) of @@ -908,7 +912,7 @@ step_down(State) -> step_down(probe, State). step_down(Next, State=#state{lease=Lease, watchers=Watchers}) -> - ?OUT("~p: stepping down~n", [State#state.id]), + lager:debug("~p: stepping down", [State#state.id]), _ = notify_leader_status(Watchers, Next, State), riak_ensemble_lease:unlease(Lease), State2 = cancel_timer(State), @@ -1017,8 +1021,6 @@ common({update_hash, _, _, MaybeFrom}, State, StateName) -> maybe_reply(MaybeFrom, nack, State), {next_state, StateName, State}; common(Msg, State, StateName) -> - ?OUT("~p: ~s/ignoring: ~p~n", [State#state.id, StateName, Msg]), - %% io:format("~p/~p: ~s/ignoring: ~p~n", [State#state.id, self(), StateName, Msg]), nack(Msg, State), {next_state, StateName, State}. @@ -1030,11 +1032,11 @@ common({force_state, {Epoch, Seq}}, From, State, StateName) -> common(tree_pid, From, State, StateName) -> gen_fsm:reply(From, State#state.tree), {next_state, StateName, State}; -common(tree_corrupted, From, State, _StateName) -> +common(tree_corrupted, From, State, StateName) -> gen_fsm:reply(From, ok), + lager:debug("~p: tree_corrupted in state ~p", [State#state.id, StateName]), repair(init, State); common(_Msg, From, State, StateName) -> - ?OUT("~p: ~s/ignoring: ~p~n", [State#state.id, StateName, _Msg]), send_reply(From, nack), {next_state, StateName, State}. @@ -1814,7 +1816,7 @@ get_value(Obj, Default, State) -> -spec init([any(),...]) -> {ok, setup, state()}. init([Mod, Ensemble, Id, Args]) -> - ?OUT("~p: starting~n", [Id]), + lager:debug("~p: starting peer", [Id]), {A,B,C} = os:timestamp(), _ = random:seed(A + erlang:phash2(Id), B + erlang:phash2(node()), @@ -1838,6 +1840,7 @@ init([Mod, Ensemble, Id, Args]) -> {ok, setup, State}. setup({init, Args}, State0=#state{id=Id, ensemble=Ensemble, ets=ETS, mod=Mod}) -> + lager:debug("~p: setup", [Id]), NumWorkers = ?WORKERS, {TreeId, Path} = mod_synctree(State0), Tree = open_hashtree(Ensemble, Id, TreeId, Path), From ab49c397be4edc277f903f1be8442b94fef202be Mon Sep 17 00:00:00 2001 From: Fred Dushin Date: Wed, 7 Sep 2016 16:17:30 -0400 Subject: [PATCH 21/25] Bumped eleveldb dependency to 2.0.28 --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 9744fe1..afa9f5c 100644 --- a/rebar.config +++ b/rebar.config @@ -10,7 +10,7 @@ {xref_checks, [undefined_function_calls]}. {deps, [ {lager, "(2.0|2.1|2.2).*", {git, "git://github.com/basho/lager.git", {tag, "2.2.3"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {branch, "2.2"}}} + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.0.28"}}} ]}. {port_specs, From 3b703fd2c63ba04c80aedfb5b5db9bfb4885d729 Mon Sep 17 00:00:00 2001 From: Fred Dushin Date: Thu, 15 Sep 2016 17:33:41 -0400 Subject: [PATCH 22/25] Bumped eleveldb to 2.0.29 --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index afa9f5c..6ce73a8 100644 --- a/rebar.config +++ b/rebar.config @@ -10,7 +10,7 @@ {xref_checks, [undefined_function_calls]}. {deps, [ {lager, "(2.0|2.1|2.2).*", {git, "git://github.com/basho/lager.git", {tag, "2.2.3"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.0.28"}}} + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.0.29"}}} ]}. {port_specs, From 198205afe0711622209fd31348fd4b55fef7b506 Mon Sep 17 00:00:00 2001 From: Brian Sparrow Date: Wed, 21 Sep 2016 12:10:56 -0400 Subject: [PATCH 23/25] Update lager to 3.2.1 --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 6ce73a8..b976014 100644 --- a/rebar.config +++ b/rebar.config @@ -9,7 +9,7 @@ {cover_enabled, true}. {xref_checks, [undefined_function_calls]}. {deps, [ - {lager, "(2.0|2.1|2.2).*", {git, "git://github.com/basho/lager.git", {tag, "2.2.3"}}}, + {lager, ".*", {git, "git://github.com/basho/lager.git", {tag, "3.2.1"}}}, {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.0.29"}}} ]}. From 0f7f3d8041d5bec9b388e701501596d375dac7b4 Mon Sep 17 00:00:00 2001 From: Fred Dushin Date: Thu, 22 Sep 2016 14:52:26 -0400 Subject: [PATCH 24/25] updated lager dependency --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index b976014..ec90917 100644 --- a/rebar.config +++ b/rebar.config @@ -9,7 +9,7 @@ {cover_enabled, true}. {xref_checks, [undefined_function_calls]}. {deps, [ - {lager, ".*", {git, "git://github.com/basho/lager.git", {tag, "3.2.1"}}}, + {lager, ".*", {git, "git://github.com/basho/lager.git", {tag, "3.2.2"}}}, {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.0.29"}}} ]}. From 6c785cd7b1bd621f146c824df76f421fb7d66f13 Mon Sep 17 00:00:00 2001 From: Fred Dushin Date: Tue, 11 Oct 2016 13:53:49 -0400 Subject: [PATCH 25/25] Updated eleveldb dependency. --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index ec90917..8953fc2 100644 --- a/rebar.config +++ b/rebar.config @@ -10,7 +10,7 @@ {xref_checks, [undefined_function_calls]}. {deps, [ {lager, ".*", {git, "git://github.com/basho/lager.git", {tag, "3.2.2"}}}, - {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.0.29"}}} + {eleveldb, ".*", {git, "git://github.com/basho/eleveldb.git", {tag, "2.0.30"}}} ]}. {port_specs,