├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── doc └── genpaxospublic-090703114743-phpapp01.pdf ├── rebar ├── rebar.config └── src ├── gen_paxos.app.src ├── gen_paxos.erl └── paxos_fsm.erl /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.beam 3 | deps/* 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | APP ?=gen_paxos 3 | ERL ?=erl 4 | CT_RUN ?=ct_run 5 | ERL_FLAGS ?=+A10 6 | REBAR_FLAGS := 7 | 8 | all: deps compile 9 | 10 | deps: 11 | $(REBAR) get-deps 12 | 13 | compile: 14 | ERL_FLAGS=$(ERL_FLAGS) $(REBAR) $(REBAR_FLAGS) compile 15 | 16 | test: tests 17 | 18 | tests: 19 | @ $(REBAR) $(REBAR_FLAGS) eunit app=$(APP) 20 | @ $(REBAR) $(REBAR_FLAGS) ct app=$(APP) 21 | 22 | clean: 23 | $(REBAR) $(REBAR_FLAGS) clean 24 | -rm test/*.beam 25 | 26 | distclean: clean 27 | $(REBAR) delete-deps 28 | 29 | include rebar.mk 30 | 31 | .EXPORT_ALL_VARIABLES: 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## gen_paxos/paxos_fsm 2 | 3 | 2012.3.17 4 | Author: greg@burd.me 5 | 6 | ### Motivation 7 | 8 | `gen_paxos` is an implementation of the Paxos protocol for Erlang/OTP. Paxos 9 | implementation is to be a large finite state machine. My motivation is to 10 | reduce the cost of re-invention of wheels. PAXOS is a consensus method in 11 | distributed and masterless environment, which is mathmatically guaranteed to 12 | come to a conclusion in a finite amount of time. 13 | 14 | ### Current status 15 | 16 | **UNDER DEVELOPMENT** 17 | 18 | Prototype of paxos consensus protocol (paxos_fsm) has successfully worked, 19 | but there is major work still to do before this is viable for production 20 | use. See "future work" below for the list. 21 | 22 | ### Usage 23 | 24 | With setting (1) Name of Subject, (2) Identity Number in PAXOS group, (3) Value 25 | that PAXOS agent will propose, and (4) Other PAXOS agents into 26 | paxos_fsm:start/4, PAXOS consensus IPC (part-time parliament) starts. With 27 | calling paxos_fsm:get_result/1 you'll get result of the parliament. With 28 | calling paxos_fsm:stop/1 you'll garbage-collect the part-time parliament. 29 | 30 | #### Example 31 | 32 | in node A: 33 | 34 | ```erlang 35 | nodeA> node(). 36 | 'nodeA'. 37 | nodeA> nodes(). 38 | ['nodeB']. 39 | nodeA> gen_paxos:start_link( 0, nodes() ). 40 | nodeA> gen_paxos:ask( somekey, somevalue ). 41 | ...(paxos consensus protocol started; messages will be printed)... 42 | paxos mediation done. decided: hogehoge - (N=2) 43 | nodeA> gen_paxos:stop(). 44 | paxos_fsm terminated (normal). 45 | ok 46 | StateName: decided 47 | StateData: .... 48 | nodeA> 49 | ``` 50 | 51 | in node B: 52 | 53 | ```erlang 54 | nodeB> node(). 55 | 'nodeB'. 56 | nodeB> nodes(). 57 | ['nodeA']. 58 | nodeB> gen_paxos:start_link( 1, nodes() ). 59 | ...(paxos consensus protocol started; messages will be printed)... 60 | paxos mediation done. decided: hogehoge - (N=2) 61 | nodeB> gen_paxos:ask( somekey, void ). 62 | somevalue 63 | nodeB> 64 | ``` 65 | 66 | 67 | ### Future Work 68 | 69 | - replace use of disterl with UDP sockets 70 | - 2-phase paxos consensus algorithm works, but not so fast as other 71 | replication/consensus protocols. 72 | - to refine the code so as to work as a generic erlang module - deprecated. 73 | - automation of basic tests. 74 | - to expand coverege of tested combinations of states. 75 | - because paxos is complex FSM as a whole 76 | - to create a gen_leader[2] compatible interface? 77 | - gen_serverize 78 | - master election module with lease time, with replication consistency 79 | - performance test 80 | - quick check 81 | - dialyzer 82 | - documentation :P 83 | 84 | 85 | ### Related works 86 | 87 | - `libpaxos` ( http://sourceforge.net/projects/libpaxos ) libpaxos is almost 88 | same implementation of PAXOS consensus protocol. It has Simple version and 89 | Fast version of paxos and seems well working. Its implementation has paxos 90 | coordinator process, SPOF in a really distributed environment, which is solved 91 | in gen_paxos. 92 | 93 | - `gen_leader` ( http://www.cs.chalmers.se/~hanssv/leader_election/ ) gen_leader 94 | is a master election module whose behaviour looks like gen_server. It's 95 | siginificant in churn environment (maybe) and small rate of mis-election, 96 | controllability with proirity. Valid information and details are in [2]. 97 | 98 | ### License 99 | 100 | The first author of this package UENISHI Kota has given permission to 101 | re-license his works under the Apache Software License version 2 (ASLv2) in an 102 | email to me (Greg Burd) he wrote: "... so if your code comes successfully 103 | working you can change the license to APL and remove my signature and copyright 104 | notion." Copyright notices need not change, names need not be removed, but 105 | starting with this version of this software (released in 2012) the code is 106 | available for use under the Apache Software License (see the LICENSE file for 107 | complete terms and conditions of use). 108 | 109 | ### Authors 110 | 111 | - UENISHI Kota 112 | - Greg Burd @gregburd 113 | 114 | ### Appendix 115 | 116 | [1] Chandra, Tushar; Robert Griesemer, Joshua Redstone (2007). 117 | "Paxos Made Live – An Engineering Perspective". PODC '07: 118 | 26th ACM Symposium on Principles of Distributed Computing. 119 | http://labs.google.com/papers/paxos_made_live.html. 120 | 121 | [2] Svensson, H. and Arts, T. 2005. A new leader election implementation. 122 | In Proceedings of the 2005 ACM SIGPLAN Workshop on Erlang (Tallinn, Estonia, 123 | September 26 - 28, 2005). ERLANG '05. ACM, New York, NY, 35-39. DOI= 124 | http://doi.acm.org/10.1145/1088361.1088368 125 | -------------------------------------------------------------------------------- /doc/genpaxospublic-090703114743-phpapp01.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gburd/gen_paxos/99f8105fe40f93dbfc25d0ec24f5e375c12211fb/doc/genpaxospublic-090703114743-phpapp01.pdf -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gburd/gen_paxos/99f8105fe40f93dbfc25d0ec24f5e375c12211fb/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | {erl_opts, [fail_on_warning, debug_info]}. 3 | {xref_checks, [undefined_function_calls]}. 4 | 5 | {cover_enabled, true}. 6 | {clean_files, [".eunit", "ebin/*.beam","*~","*/*~"]}. 7 | 8 | % edown-related config (for generating github-friendly edoc) 9 | {deps, [{edown, ".*", {git, "git://github.com/esl/edown.git", "HEAD"}}]}. 10 | {edoc_opts, [{doclet, edown_doclet}, 11 | {top_level_readme, 12 | {"./README.md", "http://github.com/gburd/gen_paxos"}}]}. 13 | -------------------------------------------------------------------------------- /src/gen_paxos.app.src: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | {application, gen_paxos, 3 | [ 4 | {description, ""}, 5 | {vsn, "0.0.1"}, 6 | {registered, []}, 7 | {applications, [ 8 | kernel, 9 | stdlib 10 | ]}, 11 | {env, []} 12 | ]}. 13 | -------------------------------------------------------------------------------- /src/gen_paxos.erl: -------------------------------------------------------------------------------- 1 | %% -*- coding: utf-8 -*- 2 | %% gen_paxos 3 | %%   Copyright (C) 2009 kuenishi+paxos@gmail.com 4 | 5 | %% This program is free software: you can redistribute it and/or modify 6 | %% it under the terms of the Apache Software License 2.0 7 | 8 | %% @doc GENeric PAXOS consensus protocol executer. 9 | %% this can't be used as a behaviour, but a API for making consensus. 10 | %% @todo - add gen_server behaviour 11 | %% - add how to stop result 12 | %% - add some persistency feature. 13 | -module(gen_paxos). 14 | -author('kuenishi+paxos@gmail.com'). 15 | 16 | -export([version_info/0]). 17 | 18 | -export([ask/1, ask/2, start_link/2, 19 | stop/0, clear/0]). 20 | 21 | version_info()-> {?MODULE, 1}. %% math:exp(1)=2.718281828459045 22 | 23 | -define( DEFAULT_COORDINATOR_NUM, 3 ). 24 | 25 | %% spawns a coordinator process. 26 | %% @spec start_link( node_identifier(), initN, other_players() ) -> Pid 27 | start_link( InitN, Others )-> 28 | io:format( "starting ~p agent...", [?MODULE] ), 29 | Pongs=lists:filter(fun(X)-> 30 | case X of pong -> true; _-> false end 31 | end , 32 | lists:map( 33 | fun(Other)-> net_adm:ping(Other) end, 34 | Others )), 35 | io:format( "~p nodes ponged.~n", [length( Pongs )] ), 36 | start_link( InitN, Others, ?DEFAULT_COORDINATOR_NUM ). 37 | 38 | start_link( _InitN, _Others, 0 )-> []; 39 | start_link( InitN, Others, NumCoordinators )-> 40 | % Pid = spawn_link( ?MODULE, coordinator, [InitN, Others] ), 41 | Pid = spawn_link( fun()-> coordinator(InitN, Others) end ), 42 | ok=io:format( "starting coordinator: ~p (~p).~n", [get_process_name_from_int(NumCoordinators), Pid] ), 43 | true=register( get_process_name_from_int(NumCoordinators), Pid ), 44 | [Pid|start_link( InitN, Others, NumCoordinators-1)]. 45 | 46 | get_process_name_from_int( N )-> % 1...?DEFAULT_COORDINATOR_NUM 47 | list_to_atom( "coordinator" ++ integer_to_list(N) ). 48 | 49 | get_process_name_from_key( Key )-> 50 | get_process_name_from_int( erlang:phash( Key, ?DEFAULT_COORDINATOR_NUM ) ). % 1...?DEFAULT_COORDINATOR_NUM 51 | 52 | stop()-> 53 | stop( ?DEFAULT_COORDINATOR_NUM ). 54 | 55 | stop(0)-> ok; 56 | stop(N)-> 57 | Coordinator = get_process_name_from_int( N ), 58 | Coordinator ! {self(), stop, normal}, 59 | stop(N-1). 60 | 61 | %% if you consult a value , set Value as void. 62 | ask(Key)-> ask(Key,void). 63 | 64 | ask(Key, Value)-> 65 | Coordinator = get_process_name_from_key( Key ), 66 | Coordinator ! {self(), ask, { Key, Value }}, 67 | receive 68 | {_From, result, {Key, Value} }-> %success 69 | Value; 70 | {_From, result, {Key, Other} }-> 71 | Other 72 | end. 73 | 74 | clear()-> clear( ?DEFAULT_COORDINATOR_NUM ). 75 | 76 | clear(0)-> ok; 77 | clear(N)-> 78 | Coordinator = get_process_name_from_int( N ), 79 | Coordinator ! {self(), clear, normal}, 80 | clear(N-1). 81 | 82 | coordinator( InitN, Others )-> 83 | receive 84 | {From, ask, {Key, void}}-> 85 | From ! {self(), result, {Key, get( Key )}}; 86 | {From, ask, {Key, Value}}-> 87 | case get( Key ) of 88 | undefined-> %% when the subject not yet done 89 | io:format("starting active paxos: ~p~n", [{From, ask, {Key,Value}}]), 90 | lists:map( fun(Node)-> 91 | io:format("starting message to: ~p ! ~p~n", 92 | [{get_process_name_from_key(Key), Node}, 93 | {self(), suggest, {Key, Value}}]), 94 | {get_process_name_from_key(Key), Node} ! {self(), suggest, {Key,Value} } 95 | end, 96 | Others), 97 | paxos_fsm:start( Key, InitN, Value, Others, [self(), From]); 98 | ResultValue-> %% when the subject already done 99 | From ! {self(), result, {Key, ResultValue} } %% return the result 100 | end; 101 | {_From, suggest, {Key, Value}}-> 102 | io:format("starting passive paxos: ~p~n", [{_From, subject, {Key,Value}}]), 103 | paxos_fsm:start( Key, InitN, Value, Others, [self()] ); 104 | {_From, result, {Key, Value}}-> %% set done; send to reference 105 | put( Key, Value ); 106 | {_From, stop, normal}-> 107 | exit( stop ); 108 | Other -> 109 | io:format("~p~n", [{error, {unknown_massage, Other}}]) 110 | end, 111 | coordinator( InitN, Others ). 112 | -------------------------------------------------------------------------------- /src/paxos_fsm.erl: -------------------------------------------------------------------------------- 1 | %% -*- coding: utf-8 -*- 2 | %% gen_paxos 3 | %%   Copyright (C) 2009 kuenishi+paxos@gmail.com 4 | 5 | %% This program is free software: you can redistribute it and/or modify 6 | %% it under the terms of the Apache Software License 2.0 7 | 8 | %% @doc PAXOS Finite State Machine implementation 9 | -module(paxos_fsm). 10 | -author('kuenishi+paxos@gmail.com'). 11 | 12 | -behaviour(gen_fsm). 13 | 14 | -export([version_info/0]). 15 | 16 | %% @doc functions for users. 17 | -export([start/5, stop/1, get_result/1]). 18 | 19 | %% @doc functions for gen_fsm. 20 | -export([init/1, handle_event/3, handle_sync_event/4, 21 | handle_info/3, terminate/3, code_change/4]). 22 | 23 | %% @doc states of FSM. 24 | -export([nil/2, preparing/2, proposing/2, acceptor/2, 25 | learner/2, decided/2]). 26 | 27 | -define( DEFAULT_TIMEOUT, 3000 ). 28 | 29 | %% @doc state data for all machines, 30 | %% 31 | -record( state, {subject, n, value, 32 | all, quorum, current=0, others, init_n, 33 | return_pids=[] 34 | } ). 35 | 36 | %% -record( event, {name, 37 | %% subject, n, value, 38 | %% from} ). 39 | 40 | %% @type subject_identifier() = atom() 41 | %% @type propose_number() = int() 42 | %% @type players() = list() 43 | 44 | version_info()-> {?MODULE, 1}. 45 | 46 | %% @doc Users call this function. Initializes PAXOS FSM. 47 | %% subject_identifier - subject name. this names process, unless paxos_fsm can't find others. 48 | %% n() - paxos_fsm agent id. this must be unique in the paxos_fsm group. 49 | %% no more than length of players. 50 | %% any() - value to suggest (prepare, propose). 51 | %% other_players() - member list of paxos_fsm group, which consists of agents, except oneself. 52 | %% return_pids() - when consensus got decided, result is to be sent to return_pid(). 53 | %% 54 | %% @spec start( subject_identifier(), n(), any(), other_players(), return_pid() ) -> Result 55 | %% Result = {ok, Pid} | ignore | { error, Error } 56 | %% Error = {already_started, Pid } | term() 57 | %% Pid = pid() 58 | %% 59 | start(S, InitN, V, Others, ReturnPids) -> 60 | %% lists:map( fun(Other)-> net_adm:ping(Other) end, Others ), 61 | %% setting data; 62 | All = length(Others)+1, Quorum = All / 2 , 63 | InitStateData = #state{ subject=S, n=InitN, value=V, 64 | all=All, quorum=Quorum, others=Others, init_n=InitN, 65 | return_pids=ReturnPids }, 66 | gen_fsm:start_link( 67 | generate_global_address( node(), S ), %FsmName %%{global, ?MODULE}, %{local, {?MODULE, S} }, 68 | ?MODULE, %Module 69 | InitStateData, %Args 70 | [{timeout, ?DEFAULT_TIMEOUT}] %Options %%, {debug, debug_info} ] 71 | ). 72 | 73 | %% @doc Users can stop the FSM after or before PAXOS have make result. 74 | %% @spec stop( subject_identifier() ) -> ok 75 | stop(S) -> % io:format("~p ~p.~n", [?MODULE, stopped]), 76 | gen_fsm:send_all_state_event( generate_global_address( node(),S ), stop). 77 | 78 | %% @doc Users can get result as long as FSM remains. 79 | %% @spec get_result( subject_identifier() ) -> Reply 80 | %% Reply = {decided, V} | {OtherStateName, V} 81 | get_result(S)-> 82 | gen_fsm:sync_send_all_state_event( generate_global_address( node(),S ), result). 83 | 84 | 85 | %% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %% 86 | %% codes below are for gen_fsm. users don't need. %% 87 | %% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %% 88 | 89 | init(InitStateData)-> 90 | %% message; 91 | io:format("~p ~p: ~p~n", [?MODULE, started, InitStateData]), 92 | %% starting paxos_fsm... 93 | process_flag(trap_exit, true), 94 | {ok, 95 | nil, %% initial statename 96 | InitStateData, %%{{S, InitN, V},{All, Quorum, 0, Others, InitN}, Misc }, %% initial state data 97 | ?DEFAULT_TIMEOUT %% initial state timeout 98 | }. 99 | 100 | broadcast(Others, S, Message)-> 101 | PaxosOthers = [ generate_global_address( P, S ) || P <- Others ], 102 | lists:map( fun(Other)-> gen_fsm:send_event( Other, Message ) end , %Timeout * 1) end, 103 | PaxosOthers ). 104 | 105 | send(Node, S, Message)-> 106 | % io:format("sending: ~p to ~p~n", [Message, {global, {?MODULE, Node, S}}] ), 107 | gen_fsm:send_event( generate_global_address( Node, S ), Message ). 108 | 109 | get_next_n( N , All )-> (( N div All )+1) * All. 110 | 111 | generate_global_address( Node, Subject )-> {global, {?MODULE, Node, Subject}}. 112 | 113 | %% ========================================= 114 | %% states: 115 | %% - nil 116 | %% - preparing 117 | %% - proposing 118 | %% - acceptor 119 | %% - learner 120 | %% - decided 121 | %% events: n' < n < n''... 122 | %% - {prepare, {S, N, V}} 123 | %% - {prepare_result, {S, N, V}} 124 | %% - propose 125 | %% - propose_result 126 | %% - timeout 127 | %% - decide 128 | %% Data: 129 | %% - {{Sc, Nc, Vc}, {All, Quorum, Current, Others, InitN}} 130 | 131 | %% ========================================= 132 | %% - nil ( master lease time out ) 133 | nil( {prepare, {S, N, _V, From}}, StateData) when N > StateData#state.n -> 134 | % {{S, Nc, _Vc}, Nums} ) when N > Nc -> 135 | % gen_fsm:sync_send_event(From, {prepare_result, {0, nil}}), 136 | send(From, S, {prepare_result, {S, 0, nil, node()}}), 137 | NewStateData = StateData#state{n=N}, 138 | {next_state, acceptor, NewStateData, %{{S, N, V}, Nums}, 139 | ?DEFAULT_TIMEOUT}; 140 | nil( {prepare, {S, N, _V, From}}, StateData) when N < StateData#state.n -> %{{S, Nc, Vc}, Nums} ) when N < Nc -> 141 | % gen_fsm:sync_send_event(From, {prepare_result, {0, nil}}), 142 | send(From, S, {prepare_result, {S, StateData#state.n, StateData#state.value, node()}}), 143 | {next_state, nil, StateData, ?DEFAULT_TIMEOUT}; 144 | % {next_state, nil, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 145 | 146 | %% nil( {decide, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> % always ignore 147 | %% {next_state, nil, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 148 | nil( {decide, {S, N, V, _From}}, StateData ) -> % when N == Nc 149 | % {_OldNums , Members} = StateData, 150 | % {next_state, decided, {{S, N, V}, Members}, ?DEFAULT_TIMEOUT}; 151 | S=StateData#state.subject, 152 | decided_callback( StateData#state{n=N, value=V} ); 153 | % {next_state, decided, StateData#state{subject=S, n=N, value=V}, ?DEFAULT_TIMEOUT}; 154 | 155 | %nil( timeout, {{S, N, V}, {All, Quorum, _Current, Others, InitN}} )-> 156 | nil( timeout, StateData )-> %{{S, N, V}, {All, Quorum, _Current, Others, InitN}} )-> 157 | % send prepare ! to all members. 158 | NewN = get_next_n( StateData#state.n, StateData#state.all ) + StateData#state.init_n, 159 | io:format( "starting paxos ... ~p. ~n", [[NewN, StateData]]), 160 | % Result = broadcast( Others, S, {prepare, {S,NewN,V, node()}} ), 161 | S=StateData#state.subject, 162 | V=StateData#state.value, 163 | Result = broadcast( StateData#state.others, S, {prepare, {S, NewN, V, node()}} ), 164 | io:format( "broadcast: ~p. ~n", [Result]), 165 | % {next_state, preparing, {{S, NewN, V}, {All, Quorum, 1, Others, InitN}}, ?DEFAULT_TIMEOUT}; 166 | {next_state, preparing, StateData#state{n=NewN, current=1}, ?DEFAULT_TIMEOUT}; 167 | 168 | nil(UnknownEvent, StateData)-> % ignore 169 | io:format( "unknown event: ~p, ~p : all ignored.~n", [UnknownEvent, StateData] ), 170 | {next_state, nil, StateData, ?DEFAULT_TIMEOUT}. 171 | 172 | 173 | %% ========================================= 174 | %% - preparing 175 | %preparing( {prepare, {S, N, _V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 176 | preparing( {prepare, {S, N, _V, From}}, StateData ) when N < StateData#state.n -> 177 | % gen_fsm:sync_send_event( From, {prepare, {S, Nc, Vc} } ), 178 | send( From, S, {prepare, {S, StateData#state.n, StateData#state.value, node()} } ), 179 | {next_state, preparing, StateData, ?DEFAULT_TIMEOUT}; 180 | %% preparing( {prepare, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 181 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 182 | preparing( {prepare, {S, N, V, From}}, StateData ) when N > StateData#state.n -> 183 | send( From, S, {prepare_result, {S, StateData#state.n, StateData#state.value, node()}}), 184 | io:format("sending prepare_result and going acceptor...~n", []), 185 | {next_state, acceptor, StateData#state{subject=S, n=N, value=V}, ?DEFAULT_TIMEOUT}; 186 | 187 | %preparing( {prepare_result, {S, N, V, From}}, {{S, Nc, _Vc}, Nums} ) when N > Nc -> 188 | preparing( {prepare_result, {S, N, V, From}}, StateData ) when N > StateData#state.n -> 189 | send( From, S, {prepare_result, {S, N, V, node()}} ), 190 | {next_state, acceptor, StateData#state{subject=S, n=N, value=V}, ?DEFAULT_TIMEOUT}; 191 | 192 | preparing( {prepare_result, {S, _N, _V, _From}}, StateData ) when StateData#state.current > StateData#state.quorum -> 193 | % {{S, Nc, Vc}, {All, Quorum, Current, Others, InitN}} ) when Current > Quorum-> 194 | broadcast( StateData#state.others, S, {propose, {S,StateData#state.n,StateData#state.value, node()}} ), 195 | % {next_state, proposing, {{S, Nc, Vc}, {All, Quorum, 1, Others, InitN}}, ?DEFAULT_TIMEOUT}; 196 | {next_state, proposing, StateData#state{current=1}, ?DEFAULT_TIMEOUT}; 197 | 198 | %preparing( {prepare_result, {S, N, V, _From}}, {{S, N, V}, {All, Quorum, Current, Others, InitN}} )-> 199 | preparing( {prepare_result, {S, N, V, _From}}, StateData ) 200 | when S==StateData#state.subject , N==StateData#state.n , V==StateData#state.value -> 201 | Current = StateData#state.current, 202 | {next_state, proposing, StateData#state{current=Current+1}, ?DEFAULT_TIMEOUT}; 203 | 204 | %preparing( {prepare_result, {S, N, _V, _From}}, {{S, Nc, Vc}, {All, Quorum, Current, Others, InitN}} ) when N < Nc-> 205 | preparing( {prepare_result, {S, N, _V, _From}}, StateData ) when N < StateData#state.n -> 206 | %{{S, Nc, Vc}, {All, Quorum, Current, Others, InitN}} ) when N < Nc-> 207 | % io:format("recvd: ~p; (Current, Quorum)=(~p,~p)~n", [{{S,N,_V,_From}, {S, Nc, Vc }}, Current, Quorum]), 208 | case (StateData#state.current + 1 > StateData#state.quorum) of 209 | true -> 210 | io:format("got quorum at prepare!~n", []), 211 | broadcast( StateData#state.others, S, {propose, {S, StateData#state.n, StateData#state.value, node()}} ), 212 | % io:format("proposing ~p...~n", [{propose, {S,Nc,Vc,node()}}]), 213 | {next_state, proposing, StateData#state{current=1}, ?DEFAULT_TIMEOUT}; 214 | false -> 215 | Current = StateData#state.current, 216 | {next_state, preparing, StateData#state{current=Current+1}, ?DEFAULT_TIMEOUT} 217 | %{{S, Nc, Vc}, {All, Quorum, Current+1, Others, InitN}}, 218 | end; 219 | 220 | %% preparing( {propose, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 221 | %% {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 222 | %% preparing( {propose, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 223 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 224 | preparing( {propose, {S, N, V, From}}, StateData ) when N > StateData#state.n -> 225 | send( From, S, {propose_result, {S, N, V, node()}} ), 226 | {next_state, learner, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 227 | 228 | %% preparing( {propose_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 229 | %% {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 230 | %% preparing( {propose_result, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 231 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 232 | preparing( {propose_result, {S, N, V, From}}, StateData) when N > StateData#state.n -> 233 | send( From, S, {propose_result, {S, N, V, node()}} ), 234 | {next_state, learner, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 235 | 236 | preparing( {decide, {_S, N, V, _From}}, StateData)-> %{{S, _Nc, _Vc}, Nums} ) -> 237 | decided_callback( StateData#state{n=N, value=V} ); 238 | % {next_state, decided, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 239 | 240 | preparing( timeout, StateData)-> %{{S, N, V}, {All, Quorum, _Current, Others, InitN} } )-> 241 | {next_state, nil, StateData#state{current=0}, ?DEFAULT_TIMEOUT}. 242 | %{{S, N, V}, {All, Quorum, 0, Others, InitN}}, ?DEFAULT_TIMEOUT}. 243 | 244 | %% ========================================= 245 | %% - proposing 246 | %% proposing( {prepare, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 247 | %% {next_state, proposing, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 248 | %% proposing( {prepare, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 249 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 250 | proposing( {prepare, {S, N, V, From}}, StateData) when N > StateData#state.n -> %{{S, Nc, Vc}, Nums} ) when N > Nc -> 251 | send( From, S, {prepare_result, {S, StateData#state.n, StateData#state.value, node() }}), 252 | {next_state, acceptor, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 253 | 254 | %% proposing( {prepare_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 255 | %% {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 256 | %% proposing( {prepare_result, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 257 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 258 | proposing( {prepare_result, {S, N, V, From}}, StateData) when N > StateData#state.n -> %{{S, Nc, Vc}, Nums} ) when N > Nc -> 259 | send( From, S, {prepare_result, {S, StateData#state.n, StateData#state.value, node()}}), 260 | {next_state, acceptor, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 261 | 262 | %% proposing( {propose, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 263 | %% {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 264 | %% proposing( {propose, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 265 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 266 | proposing( {propose, {S, N, V, From}}, StateData) when N > StateData#state.n -> %{{S, Nc, Vc}, Nums} ) when N > Nc -> 267 | send( From, S, {propose_result, {S, StateData#state.n, StateData#state.value, node()}}), 268 | {next_state, learner, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 269 | 270 | %% proposing( {propose_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 271 | %% {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 272 | 273 | %% proposing( {propose_result, {S, N, V, _From}}, 274 | %% {{S, N, V}, {All, Quorum, Current, Others, InitN}} ) when (Quorum > Current+1) -> % when N == Nc 275 | proposing( {propose_result, {S, N, V, _From}}, StateData) 276 | when N==StateData#state.n, V==StateData#state.value, StateData#state.quorum > StateData#state.current+1 -> 277 | S=StateData#state.subject, 278 | Current = StateData#state.current, 279 | {next_state, proposing, StateData#state{current=Current+1}, ?DEFAULT_TIMEOUT }; 280 | 281 | %% Got quorum!!! 282 | %% proposing( {propose_result, {S, N, V, _From}}, 283 | %% {{S, N, V}, {All, Quorum, Current, Others, InitN}} )-> % when N == Nc 284 | proposing( {propose_result, {S, N, V, _From}}, StateData) when N==StateData#state.n, V==StateData#state.value-> 285 | io:format("got quorum at proposing!!~n", []), 286 | broadcast( StateData#state.others, S, {decide, {S, N, V, node()}} ), 287 | Current=StateData#state.current, 288 | decided_callback( StateData#state{current=Current+1} ); 289 | % {next_state, decided, StateData#state{current=Current+1}, ?DEFAULT_TIMEOUT }; 290 | 291 | proposing( {propose_result, {S, N, V, From}}, StateData) when N > StateData#state.n -> % {{S, Nc, _Vc}, Nums} ) when N > Nc -> 292 | send( From, S, {propose_result, {S, N, V, node()}}), 293 | {next_state, learner, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 294 | 295 | %% proposing( {decide, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> % always ignore 296 | %% {next_state, proposing, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 297 | %% proposing( {decide, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 298 | %% {next_state, decided, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 299 | 300 | proposing( {decide, {S, N, V, _From}}, StateData) when N >= StateData#state.n-> %{{S, Nc, _Vc}, Nums} ) when N >= Nc -> 301 | S=StateData#state.subject, 302 | decided_callback( StateData#state{n=N, value=V} ); 303 | % {next_state, decided, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 304 | 305 | proposing( timeout, StateData)-> %{{S, N, V}, {All, Quorum, _Current, Others, InitN}} )-> 306 | io:format("proposing timeout: ~p , StateData=~p~n" , [propose, StateData]), 307 | {next_state, nil, StateData#state{current=1}, ?DEFAULT_TIMEOUT}; 308 | 309 | proposing( _Event, StateData) -> 310 | {next_state, proposing, StateData}. 311 | 312 | 313 | %% ========================================= 314 | %% - acceptor 315 | acceptor( {prepare, {S, N, _V, From}}, StateData) when N < StateData#state.n-> %{{S, Nc, Vc}, Nums} ) when N < Nc -> 316 | send( From, S, { prepare_result, {S, StateData#state.n, StateData#state.value, node()}} ), 317 | {next_state, acceptor, StateData, ?DEFAULT_TIMEOUT}; 318 | 319 | %% acceptor( {prepare, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 320 | %% {next_state, acceptor, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 321 | acceptor( {prepare, {S, N, V, From}}, StateData ) when N >= StateData#state.n -> 322 | send( From, S, { prepare_result, {S, StateData#state.n, StateData#state.value, node()}} ), 323 | {next_state, acceptor, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 324 | 325 | %% acceptor( {prepare_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 326 | %% {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 327 | %% acceptor( {prepare_result, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 328 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 329 | %% acceptor( {prepare_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N > Nc -> 330 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 331 | 332 | acceptor( {propose, {S, N, _V, _From}}, StateData) when N < StateData#state.n -> %{{S, Nc, Vc}, Nums} ) when N < Nc -> 333 | io:format("bad state: ~p (N,Nc)=(~p)~n" , [{propose},{ N, StateData#state.n}]), 334 | S=StateData#state.subject, 335 | {next_state, propose, StateData, ?DEFAULT_TIMEOUT}; 336 | acceptor( {propose, {S, N, V, From}}, StateData ) when N > StateData#state.n -> 337 | send( From, S, {propose_result , {S, StateData#state.n, StateData#state.value, node() }} ), 338 | {next_state, learner, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 339 | acceptor( {propose, {S, N, V, From}}, StateData)-> % when N == Nc 340 | {N,V}={StateData#state.n, StateData#state.value}, 341 | send( From, S, {propose_result , {S, StateData#state.n, StateData#state.value, node() }} ), 342 | {next_state, learner, StateData, ?DEFAULT_TIMEOUT}; 343 | 344 | %% acceptor( {propose_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 345 | %% {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 346 | %% acceptor( {propose_result, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 347 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 348 | %% acceptor( {propose_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N > Nc -> 349 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 350 | 351 | %% acceptor( {decide, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> % always ignore 352 | %% {next_state, acceptor, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 353 | %% acceptor( {decide, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 354 | %% {next_state, decided, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 355 | acceptor( {decide, {S, N, V, _From}}, StateData) when N >= StateData#state.n -> %{{S, Nc, _Vc}, Nums} ) when N >= Nc -> 356 | S=StateData#state.subject, 357 | decided_callback( StateData#state{n=N, value=V} ); 358 | % {Next_state, decided, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 359 | 360 | acceptor( timeout, StateData)-> %{{S, N, V}, {All, Quorum, _Current, Others, InitN} })-> 361 | io:format("acceptor timeout: ~p (N,V)=(~p)~n" , [{propose},{StateData#state.n, StateData#state.value}]), 362 | {next_state, nil, StateData#state{current=1}, ?DEFAULT_TIMEOUT}; 363 | 364 | acceptor( _Event, StateData) -> 365 | io:format("acceptor unknown event: ~p ,~p~n" , [_Event , StateData]), 366 | {next_state, acceptor, StateData}. 367 | 368 | %% ========================================= 369 | %% - learner 370 | %% learner( {prepare, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 371 | %% {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 372 | %% learner( {prepare, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 373 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 374 | learner( {prepare, {S, N, V, From}}, StateData) when N > StateData#state.n -> % {{S, Nc, _Vc}, Nums} ) when N > Nc -> 375 | send( From, S, {prepare_result, {S, N, V, node()}} ), 376 | {next_state, acceptor, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 377 | 378 | learner( {prepare_result, {S, _N, _V, _From}}, StateData )-> % when N < Nc -> 379 | S=StateData#state.subject, 380 | {next_state, learner, StateData, ?DEFAULT_TIMEOUT }; 381 | % {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 382 | %% learner( {prepare_result, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 383 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 384 | %% learner( {prepare_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N > Nc -> 385 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 386 | 387 | learner( {propose, {S, N, _V, _From}}, StateData) when N < StateData#state.n -> %{{S, Nc, Vc}, Nums} ) when N < Nc -> 388 | S=StateData#state.subject, 389 | {next_state, learner, StateData, ?DEFAULT_TIMEOUT}; 390 | %% learner( {propose, {S, N, V, _From}}, {{S, N, V}, Nums} ) -> % when N == Nc 391 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 392 | learner( {propose, {S, N, V, From}}, StateData) when N > StateData#state.n -> %{{S, Nc, _Vc}, Nums} ) when N > Nc -> 393 | send( From, S, {propose_result, {S, N, V, node()}}), 394 | {next_state, learner, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 395 | 396 | %% learner( {propose_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 397 | %% {next_state, hoge, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 398 | %% learner( {propose_result, {S, N, V, From}}, {{S, N, V}, Nums} ) -> % when N == Nc 399 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 400 | %% learner( {propose_result, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N > Nc -> 401 | %% {next_state, hoge, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 402 | 403 | %% learner( {decide, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> % always ignore 404 | %% {next_state, learner, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 405 | %% learner( {decide, {S, N, V, _From}}, {{S, N, V}, Nums} ) -> % when N == Nc 406 | %% {next_state, decided, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 407 | learner( {decide, {S, N, V, _From}}, StateData) when N >= StateData#state.n -> %{{S, Nc, _Vc}, Nums} ) when N >= Nc -> 408 | S=StateData#state.subject, 409 | decided_callback( StateData#state{n=N, value=V} ); 410 | % {next_state, decided, StateData#state{n=N, value=V}, ?DEFAULT_TIMEOUT}; 411 | 412 | learner( timeout, StateData)-> %{{S, N, V}, {All, Quorum, _Current, Others, InitN}} )-> 413 | {next_state, nil, StateData#state{current=0}, ?DEFAULT_TIMEOUT}; 414 | 415 | learner( _Event, StateData )-> 416 | {next_state, learner, StateData }. 417 | 418 | %% ========================================= 419 | %% - decided ( within master lease time ) 420 | 421 | %% decided( {propose, {S, N, _V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> 422 | %% send( From, S, {decide, {S, Nc, Vc, node()}} ), 423 | %% {next_state, decided, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 424 | 425 | %% decided( {propose_result, {S, N, V, From}}, {Values, Nums} )-> 426 | %% send( From, S, {decide , {S, N, V, node()}} ), 427 | %% {next_state, decided, {Values, Nums}, ?DEFAULT_TIMEOUT}; 428 | 429 | %% %% decided( {decide, {S, N, V, From}}, {{S, Nc, Vc}, Nums} ) when N < Nc -> % always ignore 430 | %% %% {next_state, decided, {{S, Nc, Vc}, Nums}, ?DEFAULT_TIMEOUT}; 431 | %% decided( {decide, {S, N, V, _From}}, {{S, N, V}, Nums} ) -> % when N == Nc 432 | %% {next_state, decided, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 433 | %% decided( {decide, {S, N, V, _From}}, {{S, Nc, _Vc}, Nums} ) when N > Nc -> 434 | %% {next_state, decided, {{S, N, V}, Nums}, ?DEFAULT_TIMEOUT}; 435 | 436 | decided( {_Message, {S,_N,_V, From}}, StateData)-> 437 | send( From, S, {decide, {S,StateData#state.n, StateData#state.value,node()}} ), 438 | {next_state, decided, StateData, ?DEFAULT_TIMEOUT }; 439 | 440 | decided( timeout, StateData )-> 441 | io:format( "paxos mediation done. decided: ~p - (N=~p)~n", [StateData#state.value, StateData#state.n] ), 442 | % {next_state, decided, StateData}. %{{S, N, V}, Nums}}. 443 | {stop, normal, StateData }. 444 | 445 | %% @doc must be called back whenever the subject got consensus! 446 | decided_callback(StateData)-> 447 | callback(StateData#state.subject, StateData#state.value, StateData#state.return_pids ), 448 | {next_state, decided, StateData, ?DEFAULT_TIMEOUT}. 449 | 450 | %% @doc send back message to originator to share the consensus value. 451 | callback(S, V, ReturnPids)-> 452 | lists:map( fun(ReturnPid)-> ReturnPid ! {self(), result, {S, V}} end, ReturnPids ). 453 | 454 | code_change(_,_,_,_)-> 455 | ok. 456 | 457 | handle_event( stop, _StateName, StateData )-> 458 | {stop, normal, StateData}. 459 | 460 | handle_info(_,_,_)-> 461 | ok. 462 | 463 | handle_sync_event(result, _From, StateName, StateData)-> 464 | {reply, {StateName, StateName#state.value} , StateName, StateData}; 465 | 466 | handle_sync_event(stop, From, StateName, StateData)-> 467 | {stop, From, StateName, StateData}. 468 | 469 | terminate(Reason, StateName, StateData) -> 470 | io:format("~p terminated (~p).~n", [?MODULE, Reason]), 471 | io:format("StateName: ~p~nStateData: ~p~n", [StateName, StateData]), 472 | ok. 473 | --------------------------------------------------------------------------------