int main () { std::atomic <uint16_t> response_count (0); paxos::server::callback_type callback = [& response_count](int64_t, std::string const & workload) -> std::string { ++response_count; return "bar"; }; paxos::configuration configuration; configuration.set_strategy_factory (new test_strategy_factory (configuration.durable_storage ())); paxos::server server1 ("127.0.0.1", 1337, callback); paxos::server server2 ("127.0.0.1", 1338, callback); paxos::server server3 ("127.0.0.1", 1339, callback, configuration); paxos::client client; server1.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); server2.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); server3.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); client.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); /*! This would be retried and shouldn't cause any troubles. */ PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); PAXOS_ASSERT_EQ (response_count, 2); PAXOS_INFO ("test succeeded"); }
int main () { paxos::configuration configuration1; paxos::configuration configuration2; paxos::configuration configuration3; configuration1.durable_storage ().set_history_size (5); configuration2.durable_storage ().set_history_size (5); configuration3.durable_storage ().set_history_size (5); paxos::server::callback_type callback = []( int64_t promise_id, std::string const & workload) -> std::string { return "bar"; }; paxos::client client; client.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); paxos::server server1 ("127.0.0.1", 1337, callback, configuration1); paxos::server server2 ("127.0.0.1", 1338, callback, configuration2); server1.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); server2.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); { paxos::server server3 ("127.0.0.1", 1339, callback, configuration3); server3.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); /*! Issue 10 calls, so we can verify that the lowest proposal id is in fact 10. */ for (size_t i = 0; i < 15; ++i) { PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); } PAXOS_ASSERT_EQ (configuration1.durable_storage ().lowest_proposal_id (), 11); PAXOS_ASSERT_EQ (configuration2.durable_storage ().lowest_proposal_id (), 11); PAXOS_ASSERT_EQ (configuration3.durable_storage ().lowest_proposal_id (), 11); } /*! Now, since one host in our quorum is currently down, logs should *not* be cleaned up further than the proposal that has been previously accepted by that host. */ for (size_t i = 0; i < 30; ++i) { PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); } PAXOS_ASSERT_GE (configuration1.durable_storage ().lowest_proposal_id (), 15); PAXOS_ASSERT_GE (configuration2.durable_storage ().lowest_proposal_id (), 15); PAXOS_ASSERT_EQ (configuration3.durable_storage ().lowest_proposal_id (), 11); boost::this_thread::sleep ( boost::posix_time::milliseconds ( paxos::configuration ().timeout ())); { /*! And after our server is back online, the history can be cleared again. */ paxos::server server3 ("127.0.0.1", 1339, callback, configuration3); server3.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); for (size_t i = 0; i < 15; ++i) { PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); } PAXOS_ASSERT_EQ (configuration1.durable_storage ().lowest_proposal_id (), 56); PAXOS_ASSERT_EQ (configuration2.durable_storage ().lowest_proposal_id (), 56); PAXOS_ASSERT_EQ (configuration3.durable_storage ().lowest_proposal_id (), 56); } PAXOS_INFO ("test succeeded"); }
/*! static */ void initiate_request::step1 ( std::string const & byte_array, detail::quorum::client_view & quorum, callback_type callback, queue_guard_type guard) { boost::optional <boost::asio::ip::tcp::endpoint> leader = quorum.select_leader (); if (leader.is_initialized () == false) { PAXOS_DEBUG ("leader.is_initialized () == false"); callback (detail::error_no_leader, ""); quorum.advance_leader (); return; } PAXOS_DEBUG ("leader.is_initialized () == true"); quorum::server & server = quorum.lookup_server (*leader); if (server.has_connection () == false) { PAXOS_DEBUG ("server.has_connection () == false"); callback (detail::error_no_leader, ""); quorum.advance_leader (); return; } PAXOS_DEBUG ("server.has_connection () == true"); PAXOS_DEBUG ("sending request to host " << server.endpoint () << " with id = " << server.id ()); detail::tcp_connection_ptr connection = server.connection (); /*! Now that we have our leader's connection, let's send it our command to initiate the request. */ command command; command.set_type (command::type_request_initiate); command.set_workload (byte_array); connection->write_command (command); PAXOS_INFO ("client initiating request!"); connection->read_command ( [connection, & quorum, & server, callback, guard] ( boost::optional <enum detail::error_code> error, detail::command const & c) { if (error) { PAXOS_WARN ("client had problems communicating with leader: " << detail::to_string (*error)); quorum.connection_died (server.endpoint ()); quorum.advance_leader (); callback (*error, ""); } else { quorum.lookup_server (c.host_endpoint ()).set_id (c.host_id ()); quorum.lookup_server (c.host_endpoint ()).set_highest_proposal_id (c.highest_proposal_id ()); switch (c.type ()) { case command::type_request_accepted: PAXOS_DEBUG ("received command with workload = " << c.workload () << ", " "now calling callback!"); callback (boost::none, c.workload ()); break; case command::type_request_error: if (c.error_code () == detail::error_no_leader) { quorum.advance_leader (); } PAXOS_WARN ("request error occured"); callback (c.error_code (), c.workload ()); break; default: PAXOS_UNREACHABLE (); }; } }); }
int main () { std::map <int64_t, uint16_t> responses; /*! Synchronizes access to responses */ boost::mutex mutex; paxos::configuration configuration1; paxos::configuration configuration2; paxos::configuration configuration3; /*! Note that the configuration objects below outlive the server objects declared later in the test, thus providing semi-durable storage. */ configuration1.set_durable_storage ( new paxos::durable::heap ()); configuration2.set_durable_storage ( new paxos::durable::heap ()); configuration3.set_durable_storage ( new paxos::durable::heap ()); paxos::server::callback_type callback = [& responses, & mutex]( int64_t promise_id, std::string const & workload) -> std::string { boost::mutex::scoped_lock lock (mutex); if (responses.find (promise_id) == responses.end ()) { responses[promise_id] = 1; } else { responses[promise_id]++; } PAXOS_ASSERT (responses[promise_id] <= 3); return "bar"; }; paxos::client client; client.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); { paxos::server server1 ("127.0.0.1", 1337, callback, configuration1); server1.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); { paxos::server server2 ("127.0.0.1", 1338, callback, configuration2); server2.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); { paxos::server server3 ("127.0.0.1", 1339, callback, configuration3); server3.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); PAXOS_ASSERT_EQ (all_responses_equal (responses, 3), true); } PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); PAXOS_ASSERT_EQ (all_responses_equal (responses, 3), false); } PAXOS_ASSERT_THROW (client.send ("foo").get (), paxos::exception::no_majority); } /*! Note that we're re-adding servers in reverse order here; this is to ensure that server3 doesn't become our leader while it's lagging behind. */ paxos::server server3 ("127.0.0.1", 1339, callback, configuration3); server3.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); PAXOS_ASSERT_THROW (client.send ("foo").get (), paxos::exception::no_majority); paxos::server server2 ("127.0.0.1", 1338, callback, configuration2); server2.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); boost::this_thread::sleep ( boost::posix_time::milliseconds ( paxos::configuration ().timeout ())); PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); paxos::server server1 ("127.0.0.1", 1337, callback, configuration1); server1.add ({{"127.0.0.1", 1337}, {"127.0.0.1", 1338}, {"127.0.0.1", 1339}}); boost::this_thread::sleep ( boost::posix_time::milliseconds ( paxos::configuration ().timeout ())); do { PAXOS_ASSERT_EQ (client.send ("foo").get (), "bar"); } while (all_responses_equal (responses, 3) == false); PAXOS_INFO ("test succeeded"); }