├── Makefile ├── README.md ├── bench ├── .gitignore ├── Rules.mk ├── benchmark.cc ├── benchmark.h ├── client.cc └── replica.cc ├── common ├── Rules.mk ├── client.cc ├── client.h ├── log-impl.h ├── log.cc ├── log.h ├── quorumset.h ├── replica-inl.h ├── replica.cc ├── replica.h ├── request.proto └── tests │ └── Rules.mk ├── fastpaxos ├── Rules.mk ├── client.cc ├── client.h ├── fastpaxos-proto.proto ├── replica.cc ├── replica.h └── tests │ ├── Rules.mk │ └── fastpaxos-test.cc ├── lib ├── Rules.mk ├── assert.h ├── configuration.cc ├── configuration.h ├── hash.h ├── latency-format.proto ├── latency.cc ├── latency.h ├── lookup3.cc ├── memory.cc ├── memory.h ├── message.cc ├── message.h ├── simtransport.cc ├── simtransport.h ├── tests │ ├── Rules.mk │ ├── configuration-test-1.conf │ ├── configuration-test.cc │ ├── simtransport-test.cc │ └── simtransport-testmessage.proto ├── timeval.h ├── transport.cc ├── transport.h ├── transportcommon.h ├── udptransport.cc ├── udptransport.h └── viewstamp.h ├── nistore ├── .gitignore ├── Rules.mk ├── benchClient.cc ├── client.cc ├── client.h ├── kvstore.cc ├── kvstore.h ├── lockserver.cc ├── lockserver.h ├── lockstore.cc ├── lockstore.h ├── occstore.cc ├── occstore.h ├── request.proto ├── server.cc ├── server.h ├── shard.tss.config ├── shard0.config ├── txnstore.cc ├── txnstore.h ├── versionedKVStore.cc └── versionedKVStore.h ├── spec ├── Rules.mk ├── client.cc ├── client.h ├── replica.cc ├── replica.h ├── spec-proto.proto └── tests │ ├── Rules.mk │ ├── merge-test-case.proto │ ├── merge-test.cc │ ├── merge-tests │ ├── AllCommitted │ ├── Conflict │ ├── Divergence │ ├── EmptyLogs │ ├── OneEmpty │ ├── SpeculativeNoQuorum │ ├── SpeculativeQuorum │ └── Stress │ └── spec-test.cc ├── timeserver ├── .gitignore ├── Rules.mk ├── timeserver.cc └── timeserver.h ├── unreplicated ├── Rules.mk ├── client.cc ├── client.h ├── replica.cc ├── replica.h ├── tests │ ├── Rules.mk │ └── unreplicated-test.cc └── unreplicated-proto.proto └── vr ├── Rules.mk ├── client.cc ├── client.h ├── replica.cc ├── replica.h ├── tests ├── Rules.mk └── vr-test.cc └── vr-proto.proto /README.md: -------------------------------------------------------------------------------- 1 | # Speculative Paxos 2 | 3 | This is an implementation of the Speculative Paxos protocol, as 4 | described in the paper 5 | ["Designing Distributed Systems Using Approximate Synchrony in Datacenter Networks"](https://drkp.net/papers/specpaxos-nsdi15.pdf) 6 | from NSDI 2015. 7 | 8 | Speculative Paxos is a state machine replication protocol based on the 9 | idea of co-designing distributed systems with the datacenter 10 | network. It offers excellent performance in environments where 11 | replicas receive most requests in the same order. The NSDI paper 12 | describes how to implement a "Mostly-Ordered Multicast" (MOM) 13 | primitive that provides this property with high probability. 14 | 15 | In these environments, Speculative Paxos is able to complete 16 | operations without coordination. This allows it to achieve the 17 | theoretical minimum latency (one round trip from client to replicas) 18 | and high throughput, as replicas do not need to exchange messages in 19 | the normal case. 20 | 21 | ## Contents 22 | 23 | This repository contains implementations of 3 replication protocols: 24 | 25 | 1. Speculative Paxos, including normal operation, synchronization, and 26 | reconciliation protocols. 27 | 28 | 2. Viewstamped Replication (VR), aka Multi-Paxos, as described in the 29 | paper 30 | ["Viewstamped Replication Revisited"](http://pmg.csail.mit.edu/papers/vr-revisited.pdf), 31 | including an optional batching optimization 32 | 33 | 3. Fast Paxos, although only the normal case is implemented. 34 | 35 | 4. A simple unreplicated RPC protocol for comparison 36 | 37 | ...as well as three applications: 38 | 39 | 1. a "null RPC" benchmark that executes operations as quickly as 40 | possible 41 | 42 | 2. a simple timestamp server, as might be used in a distributed 43 | storage system 44 | 45 | 3. a transactional key-value store ("nistore") that supports 46 | multiversioning and concurrency control using either strict 47 | two-phase locking or optimistic concurrency control. (This is 48 | derived from the codebase used in the 49 | [TAPIR](http://syslab.cs.washington.edu/research/tapir/) 50 | project. 51 | 52 | ## Building and Running 53 | 54 | Speculative Paxos and the applications can be built using `make`. It 55 | has been tested on Ubuntu 14.04, 16.04 and Debian 8. Regression tests 56 | can be run with `make check` 57 | 58 | Dependencies include (Debian/Ubuntu packages): 59 | protobuf-compiler pkg-config libunwind-dev libssl-dev libprotobuf-dev libevent-dev libgtest-dev 60 | 61 | You will need to create a configuration file with the following 62 | syntax: 63 | 64 | ``` 65 | f 66 | replica : 67 | replica : 68 | ... 69 | ``` 70 | 71 | You can then start a replica with `./bench/replica -c -i -m `, where `mode` is either: 72 | - `spec` for Speculative Paxos 73 | - `vr` for Viewstamped Replication 74 | - `fastpaxos` for Fast Paxos 75 | - `unreplicated` for no replication (uses only the first replica) 76 | 77 | The `vr` mode also accepts a `-b` option to specify the maximum batch 78 | size. 79 | 80 | To run a single client, use `./bench/replica -c 81 | -m `. 82 | 83 | For performance measurements, you will likely want to add `-DNASSERT` 84 | and `-O2` to the `CFLAGS` in the Makefile, and run `make PARANOID=0`, 85 | which disables complexity-changing assertions. 86 | 87 | ## Contact 88 | 89 | Speculative Paxos is a product of the 90 | [UW Systems Lab](http://syslab.cs.washington.edu/). Please email Dan 91 | Ports at drkp@cs.washington.edu with any questions. 92 | -------------------------------------------------------------------------------- /bench/.gitignore: -------------------------------------------------------------------------------- 1 | client 2 | replica 3 | -------------------------------------------------------------------------------- /bench/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | SRCS += $(addprefix $(d), \ 4 | client.cc benchmark.cc replica.cc) 5 | 6 | OBJS-benchmark := $(o)benchmark.o \ 7 | $(LIB-message) $(LIB-latency) 8 | 9 | $(d)client: $(o)client.o $(OBJS-spec-client) $(OBJS-vr-client) $(OBJS-fastpaxos-client) $(OBJS-unreplicated-client) $(OBJS-benchmark) $(LIB-udptransport) 10 | 11 | $(d)replica: $(o)replica.o $(OBJS-spec-replica) $(OBJS-vr-replica) $(OBJS-fastpaxos-replica) $(OBJS-unreplicated-replica) $(LIB-udptransport) 12 | 13 | BINS += $(d)client $(d)replica 14 | -------------------------------------------------------------------------------- /bench/benchmark.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * benchmark.cpp: 5 | * simple replication benchmark client 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #include "bench/benchmark.h" 32 | #include "common/client.h" 33 | #include "lib/latency.h" 34 | #include "lib/message.h" 35 | #include "lib/transport.h" 36 | #include "lib/timeval.h" 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | namespace specpaxos { 44 | 45 | DEFINE_LATENCY(op); 46 | 47 | BenchmarkClient::BenchmarkClient(Client &client, Transport &transport, 48 | int numRequests, uint64_t delay, 49 | int warmupSec, 50 | string latencyFilename) 51 | : client(client), transport(transport), 52 | numRequests(numRequests), delay(delay), 53 | warmupSec(warmupSec), latencyFilename(latencyFilename) 54 | { 55 | if (delay != 0) { 56 | Notice("Delay between requests: %" PRIu64 " ms", delay); 57 | } 58 | started = false; 59 | done = false; 60 | cooldownDone = false; 61 | _Latency_Init(&latency, "op"); 62 | latencies.reserve(numRequests); 63 | } 64 | 65 | void 66 | BenchmarkClient::Start() 67 | { 68 | n = 0; 69 | transport.Timer(warmupSec * 1000, 70 | std::bind(&BenchmarkClient::WarmupDone, 71 | this)); 72 | SendNext(); 73 | } 74 | 75 | void 76 | BenchmarkClient::WarmupDone() 77 | { 78 | started = true; 79 | Notice("Completed warmup period of %d seconds with %d requests", 80 | warmupSec, n); 81 | gettimeofday(&startTime, NULL); 82 | n = 0; 83 | } 84 | 85 | void 86 | BenchmarkClient::CooldownDone() 87 | { 88 | 89 | char buf[1024]; 90 | cooldownDone = true; 91 | Notice("Finished cooldown period."); 92 | std::sort(latencies.begin(), latencies.end()); 93 | 94 | uint64_t ns = latencies[latencies.size()/2]; 95 | LatencyFmtNS(ns, buf); 96 | Notice("Median latency is %" PRIu64 " ns (%s)", ns, buf); 97 | 98 | ns = latencies[latencies.size()*90/100]; 99 | LatencyFmtNS(ns, buf); 100 | Notice("90th percentile latency is %" PRIu64 " ns (%s)", ns, buf); 101 | 102 | ns = latencies[latencies.size()*95/100]; 103 | LatencyFmtNS(ns, buf); 104 | Notice("95th percentile latency is %" PRIu64 " ns (%s)", ns, buf); 105 | 106 | ns = latencies[latencies.size()*99/100]; 107 | LatencyFmtNS(ns, buf); 108 | Notice("99th percentile latency is %" PRIu64 " ns (%s)", ns, buf); 109 | } 110 | 111 | void 112 | BenchmarkClient::SendNext() 113 | { 114 | std::ostringstream msg; 115 | msg << "request" << n; 116 | 117 | Latency_Start(&latency); 118 | client.Invoke(msg.str(), std::bind(&BenchmarkClient::OnReply, 119 | this, 120 | std::placeholders::_1, 121 | std::placeholders::_2)); 122 | } 123 | 124 | void 125 | BenchmarkClient::OnReply(const string &request, const string &reply) 126 | { 127 | if (cooldownDone) { 128 | return; 129 | } 130 | 131 | if ((started) && (!done) && (n != 0)) { 132 | uint64_t ns = Latency_End(&latency); 133 | latencies.push_back(ns); 134 | if (n > numRequests) { 135 | Finish(); 136 | } 137 | } 138 | 139 | n++; 140 | if (delay == 0) { 141 | SendNext(); 142 | } else { 143 | uint64_t rdelay = rand() % delay*2; 144 | transport.Timer(rdelay, 145 | std::bind(&BenchmarkClient::SendNext, this)); 146 | } 147 | } 148 | 149 | void 150 | BenchmarkClient::Finish() 151 | { 152 | gettimeofday(&endTime, NULL); 153 | 154 | struct timeval diff = timeval_sub(endTime, startTime); 155 | 156 | Notice("Completed %d requests in " FMT_TIMEVAL_DIFF " seconds", 157 | numRequests, VA_TIMEVAL_DIFF(diff)); 158 | done = true; 159 | 160 | transport.Timer(warmupSec * 1000, 161 | std::bind(&BenchmarkClient::CooldownDone, 162 | this)); 163 | 164 | 165 | if (latencyFilename.size() > 0) { 166 | Latency_FlushTo(latencyFilename.c_str()); 167 | } 168 | } 169 | 170 | 171 | } // namespace specpaxos 172 | -------------------------------------------------------------------------------- /bench/benchmark.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * benchmark.h: 5 | * simple replication benchmark client 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #include "common/client.h" 32 | #include "lib/latency.h" 33 | #include "lib/transport.h" 34 | 35 | namespace specpaxos { 36 | 37 | class BenchmarkClient 38 | { 39 | public: 40 | BenchmarkClient(Client &client, Transport &transport, 41 | int numRequests, uint64_t delay, 42 | int warmupSec, 43 | string latencyFilename = ""); 44 | void Start(); 45 | void OnReply(const string &request, const string &reply); 46 | struct Latency_t latency; 47 | bool started; 48 | bool done; 49 | bool cooldownDone; 50 | std::vector latencies; 51 | 52 | private: 53 | void SendNext(); 54 | void Finish(); 55 | void WarmupDone(); 56 | void CooldownDone(); 57 | Client &client; 58 | Transport &transport; 59 | int numRequests; 60 | uint64_t delay; 61 | int n; 62 | int warmupSec; 63 | struct timeval startTime; 64 | struct timeval endTime; 65 | string latencyFilename; 66 | }; 67 | 68 | } // namespace specpaxos 69 | -------------------------------------------------------------------------------- /common/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | SRCS += $(addprefix $(d), \ 4 | client.cc replica.cc log.cc) 5 | 6 | PROTOS += $(addprefix $(d), \ 7 | request.proto) 8 | 9 | LIB-request := $(o)request.o 10 | 11 | OBJS-client := $(o)client.o \ 12 | $(LIB-message) $(LIB-configuration) $(LIB-transport) \ 13 | $(LIB-request) 14 | 15 | OBJS-replica := $(o)replica.o $(o)log.o \ 16 | $(LIB-message) $(LIB-request) \ 17 | $(LIB-configuration) $(LIB-udptransport) 18 | 19 | include $(d)tests/Rules.mk 20 | -------------------------------------------------------------------------------- /common/client.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * client.cc: 5 | * interface to replication client stubs 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #include "common/client.h" 32 | #include "common/request.pb.h" 33 | #include "lib/message.h" 34 | #include "lib/transport.h" 35 | 36 | #include 37 | 38 | namespace specpaxos { 39 | 40 | Client::Client(const Configuration &config, Transport *transport, 41 | clientid_t clientid) 42 | : config(config), transport(transport) 43 | { 44 | this->clientid = clientid; 45 | 46 | // Randomly generate a client ID 47 | // This is surely not the fastest way to get a random 64-bit int, 48 | // but it should be fine for this purpose. 49 | while (this->clientid == 0) { 50 | std::random_device rd; 51 | std::mt19937_64 gen(rd()); 52 | std::uniform_int_distribution dis; 53 | this->clientid = dis(gen); 54 | } 55 | 56 | transport->Register(this, config, -1); 57 | } 58 | 59 | Client::~Client() 60 | { 61 | 62 | } 63 | 64 | void 65 | Client::ReceiveMessage(const TransportAddress &remote, 66 | const string &type, const string &data) 67 | { 68 | Panic("Received unexpected message type: %s", 69 | type.c_str()); 70 | } 71 | 72 | } // namespace specpaxos 73 | -------------------------------------------------------------------------------- /common/client.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * client.h: 5 | * interface to replication client stubs 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _COMMON_CLIENT_H_ 32 | #define _COMMON_CLIENT_H_ 33 | 34 | #include "lib/configuration.h" 35 | #include "common/request.pb.h" 36 | #include "lib/transport.h" 37 | 38 | 39 | #include 40 | #include 41 | 42 | namespace specpaxos { 43 | 44 | typedef uint64_t clientid_t; 45 | #define FMT_CLIENTID "%" PRIx64 46 | 47 | class Client : public TransportReceiver 48 | { 49 | public: 50 | typedef std::function continuation_t; 51 | typedef std::function timeout_continuation_t; 52 | 53 | static const uint32_t DEFAULT_UNLOGGED_OP_TIMEOUT = 1000; // milliseconds 54 | 55 | Client(const Configuration &config, Transport *transport, 56 | clientid_t clientid = 0); 57 | virtual ~Client(); 58 | virtual void Invoke(const string &request, 59 | continuation_t continuation) = 0; 60 | virtual void InvokeUnlogged(int replicaIdx, 61 | const string &request, 62 | continuation_t continuation, 63 | timeout_continuation_t timeoutContinuation = nullptr, 64 | uint32_t timeout = DEFAULT_UNLOGGED_OP_TIMEOUT) = 0; 65 | virtual void ReceiveMessage(const TransportAddress &remote, 66 | const string &type, 67 | const string &data); 68 | 69 | protected: 70 | Configuration config; 71 | Transport *transport; 72 | 73 | clientid_t clientid; 74 | }; 75 | 76 | } // namespace specpaxos 77 | 78 | #endif /* _COMMON_CLIENT_H_ */ 79 | -------------------------------------------------------------------------------- /common/log-impl.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * log.h: 5 | * a replica's log of pending and committed operations 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _COMMON_LOG_IMPL_H_ 32 | #define _COMMON_LOG_IMPL_H_ 33 | 34 | template void 35 | Log::Dump(opnum_t from, T out) 36 | { 37 | for (opnum_t i = std::max(from, start); 38 | i <= LastOpnum(); i++) { 39 | 40 | const LogEntry *entry = Find(i); 41 | ASSERT(entry != NULL); 42 | 43 | auto elem = out->Add(); 44 | elem->set_view(entry->viewstamp.view); 45 | elem->set_opnum(entry->viewstamp.opnum); 46 | elem->set_state(entry->state); 47 | elem->set_hash(entry->hash); 48 | *(elem->mutable_request()) = entry->request; 49 | } 50 | } 51 | 52 | template void 53 | Log::Install(iter start, iter end) 54 | { 55 | // Find the first divergence in the log 56 | iter it = start; 57 | for (it = start; it != end; it++) { 58 | const LogEntry *oldEntry = Find(it->opnum()); 59 | if (oldEntry == NULL) { 60 | break; 61 | } 62 | if (it->view() != oldEntry->viewstamp.view) { 63 | RemoveAfter(it->opnum()); 64 | break; 65 | } 66 | } 67 | 68 | if (it == end) { 69 | // We didn't find a divergence. This means that the logs 70 | // should be identical. If the existing log is longer, 71 | // something is wrong. 72 | // it--; 73 | // ASSERT(it->opnum() == lastViewstamp.opnum); 74 | // ASSERT(it->view() == lastViewstamp.view); 75 | // ASSERT(Find(it->opnum()+1) == NULL); 76 | } 77 | 78 | // Install the new log entries 79 | for (; it != end; it++) { 80 | viewstamp_t vs = { it->view(), it->opnum() }; 81 | Append(vs, it->request(), LOG_STATE_PREPARED); 82 | } 83 | } 84 | 85 | #endif /* _COMMON_LOG_IMPL_H_ */ 86 | -------------------------------------------------------------------------------- /common/log.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * log.h: 5 | * a replica's log of pending and committed operations 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #include "common/log.h" 32 | #include "common/request.pb.h" 33 | #include "lib/assert.h" 34 | 35 | #include 36 | 37 | namespace specpaxos { 38 | 39 | const string Log::EMPTY_HASH = string(SHA_DIGEST_LENGTH, '\0'); 40 | 41 | Log::Log(bool useHash, opnum_t start, string initialHash) 42 | : useHash(useHash) 43 | { 44 | this->initialHash = initialHash; 45 | this->start = start; 46 | if (start == 1) { 47 | ASSERT(initialHash == EMPTY_HASH); 48 | } 49 | } 50 | 51 | 52 | LogEntry & 53 | Log::Append(viewstamp_t vs, const Request &req, LogEntryState state) 54 | { 55 | if (entries.empty()) { 56 | ASSERT(vs.opnum == start); 57 | } else { 58 | ASSERT(vs.opnum == LastOpnum()+1); 59 | } 60 | 61 | string prevHash = LastHash(); 62 | entries.push_back(LogEntry(vs, state, req)); 63 | if (useHash) { 64 | entries.back().hash = ComputeHash(prevHash, entries.back()); 65 | } 66 | 67 | return entries.back(); 68 | } 69 | 70 | // This really ought to be const 71 | LogEntry * 72 | Log::Find(opnum_t opnum) 73 | { 74 | if (entries.empty()) { 75 | return NULL; 76 | } 77 | 78 | if (opnum < start) { 79 | return NULL; 80 | } 81 | 82 | if (opnum-start > entries.size()-1) { 83 | return NULL; 84 | } 85 | 86 | LogEntry *entry = &entries[opnum-start]; 87 | ASSERT(entry->viewstamp.opnum == opnum); 88 | return entry; 89 | } 90 | 91 | 92 | bool 93 | Log::SetStatus(opnum_t op, LogEntryState state) 94 | { 95 | LogEntry *entry = Find(op); 96 | if (entry == NULL) { 97 | return false; 98 | } 99 | 100 | entry->state = state; 101 | return true; 102 | } 103 | 104 | bool 105 | Log::SetRequest(opnum_t op, const Request &req) 106 | { 107 | if (useHash) { 108 | Panic("Log::SetRequest on hashed log not supported."); 109 | } 110 | 111 | LogEntry *entry = Find(op); 112 | if (entry == NULL) { 113 | return false; 114 | } 115 | 116 | entry->request = req; 117 | return true; 118 | } 119 | 120 | void 121 | Log::RemoveAfter(opnum_t op) 122 | { 123 | #if PARANOID 124 | // We'd better not be removing any committed entries. 125 | for (opnum_t i = op; i <= LastOpnum(); i++) { 126 | ASSERT(Find(i)->state != LOG_STATE_COMMITTED); 127 | } 128 | #endif 129 | 130 | if (op > LastOpnum()) { 131 | return; 132 | } 133 | 134 | Debug("Removing log entries after " FMT_OPNUM, op); 135 | 136 | ASSERT(op-start < entries.size()); 137 | entries.resize(op-start); 138 | 139 | ASSERT(LastOpnum() == op-1); 140 | } 141 | 142 | LogEntry * 143 | Log::Last() 144 | { 145 | if (entries.empty()) { 146 | return NULL; 147 | } 148 | 149 | return &entries.back(); 150 | } 151 | 152 | viewstamp_t 153 | Log::LastViewstamp() const 154 | { 155 | if (entries.empty()) { 156 | return viewstamp_t(0, start-1); 157 | } else { 158 | return entries.back().viewstamp; 159 | } 160 | } 161 | 162 | opnum_t 163 | Log::LastOpnum() const 164 | { 165 | if (entries.empty()) { 166 | return start-1; 167 | } else { 168 | return entries.back().viewstamp.opnum; 169 | } 170 | } 171 | 172 | opnum_t 173 | Log::FirstOpnum() const 174 | { 175 | // XXX Not really sure what's appropriate to return here if the 176 | // log is empty 177 | return start; 178 | } 179 | 180 | bool 181 | Log::Empty() const 182 | { 183 | return entries.empty(); 184 | } 185 | 186 | const string & 187 | Log::LastHash() const 188 | { 189 | if (entries.empty()) { 190 | return initialHash; 191 | } else { 192 | return entries.back().hash; 193 | } 194 | } 195 | 196 | string 197 | Log::ComputeHash(string lastHash, const LogEntry &entry) 198 | { 199 | SHA_CTX ctx; 200 | unsigned char out[SHA_DIGEST_LENGTH]; 201 | 202 | SHA1_Init(&ctx); 203 | 204 | SHA1_Update(&ctx, lastHash.c_str(), lastHash.size()); 205 | //SHA1_Update(&ctx, &entry.viewstamp, sizeof(entry.viewstamp)); 206 | uint64_t x[2]; 207 | x[0] = entry.request.clientid(); 208 | x[1] = entry.request.clientreqid(); 209 | SHA1_Update(&ctx, x, sizeof(uint64_t)*2); 210 | // SHA1_Update(&ctx, entry.request.op().c_str(), 211 | // entry.request.op().size()); 212 | 213 | SHA1_Final(out, &ctx); 214 | 215 | return string((char *)out, SHA_DIGEST_LENGTH); 216 | } 217 | 218 | } // namespace specpaxos 219 | -------------------------------------------------------------------------------- /common/log.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * log.h: 5 | * a replica's log of pending and committed operations 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _COMMON_LOG_H_ 32 | #define _COMMON_LOG_H_ 33 | 34 | #include "common/request.pb.h" 35 | #include "lib/assert.h" 36 | #include "lib/message.h" 37 | #include "lib/transport.h" 38 | #include "lib/viewstamp.h" 39 | 40 | #include 41 | #include 42 | 43 | namespace specpaxos { 44 | 45 | enum LogEntryState { 46 | LOG_STATE_COMMITTED, 47 | LOG_STATE_PREPARED, 48 | LOG_STATE_SPECULATIVE, // specpaxos only 49 | LOG_STATE_FASTPREPARED // fastpaxos only 50 | }; 51 | 52 | 53 | class Log 54 | { 55 | 56 | public: 57 | struct LogEntry 58 | { 59 | viewstamp_t viewstamp; 60 | LogEntryState state; 61 | Request request; 62 | string hash; 63 | // Speculative client table stuff 64 | opnum_t prevClientReqOpnum; 65 | ::google::protobuf::Message *replyMessage; 66 | 67 | LogEntry() { replyMessage = NULL; } 68 | LogEntry(const LogEntry &x) 69 | : viewstamp(x.viewstamp), state(x.state), request(x.request), 70 | hash(x.hash), prevClientReqOpnum(x.prevClientReqOpnum) 71 | { 72 | if (x.replyMessage) { 73 | replyMessage = x.replyMessage->New(); 74 | replyMessage->CopyFrom(*x.replyMessage); 75 | } else { 76 | replyMessage = NULL; 77 | } 78 | } 79 | LogEntry(viewstamp_t viewstamp, LogEntryState state, 80 | const Request &request, const string &hash=Log::EMPTY_HASH) 81 | : viewstamp(viewstamp), state(state), request(request), 82 | hash(hash), replyMessage(NULL) { } 83 | virtual ~LogEntry() 84 | { 85 | if (replyMessage) { 86 | delete replyMessage; 87 | } 88 | } 89 | }; 90 | 91 | Log(bool useHash, opnum_t start = 1, string initialHash = EMPTY_HASH); 92 | LogEntry & Append(viewstamp_t vs, const Request &req, LogEntryState state); 93 | LogEntry * Find(opnum_t opnum); 94 | bool SetStatus(opnum_t opnum, LogEntryState state); 95 | bool SetRequest(opnum_t op, const Request &req); 96 | void RemoveAfter(opnum_t opnum); 97 | LogEntry * Last(); 98 | viewstamp_t LastViewstamp() const; // deprecated 99 | opnum_t LastOpnum() const; 100 | opnum_t FirstOpnum() const; 101 | bool Empty() const; 102 | template void Dump(opnum_t from, T out); 103 | template void Install(iter start, iter end); 104 | const string &LastHash() const; 105 | 106 | static string ComputeHash(string lastHash, const LogEntry &entry); 107 | static const string EMPTY_HASH; 108 | 109 | 110 | private: 111 | std::vector entries; 112 | string initialHash; 113 | opnum_t start; 114 | bool useHash; 115 | }; 116 | 117 | typedef Log::LogEntry LogEntry; 118 | 119 | #include "common/log-impl.h" 120 | 121 | } // namespace specpaxos 122 | 123 | #endif /* _COMMON_LOG_H_ */ 124 | -------------------------------------------------------------------------------- /common/quorumset.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * quorumset.h: 5 | * utility type for tracking sets of messages received from other 6 | * replicas and determining whether a quorum of responses has been met 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _COMMON_QUORUMSET_H_ 33 | #define _COMMON_QUORUMSET_H_ 34 | 35 | namespace specpaxos { 36 | 37 | template 38 | class QuorumSet 39 | { 40 | public: 41 | QuorumSet(int numRequired) 42 | : numRequired(numRequired) 43 | { 44 | 45 | } 46 | 47 | void 48 | Clear() 49 | { 50 | messages.clear(); 51 | } 52 | 53 | void 54 | Clear(IDTYPE vs) 55 | { 56 | std::map &vsmessages = messages[vs]; 57 | vsmessages.clear(); 58 | } 59 | 60 | int 61 | NumRequired() const 62 | { 63 | return numRequired; 64 | } 65 | 66 | const std::map & 67 | GetMessages(IDTYPE vs) 68 | { 69 | return messages[vs]; 70 | } 71 | 72 | const std::map * 73 | CheckForQuorum(IDTYPE vs) 74 | { 75 | std::map &vsmessages = messages[vs]; 76 | int count = vsmessages.size(); 77 | if (count >= numRequired) { 78 | return &vsmessages; 79 | } else { 80 | return NULL; 81 | } 82 | } 83 | 84 | const std::map * 85 | AddAndCheckForQuorum(IDTYPE vs, int replicaIdx, const MSGTYPE &msg) 86 | { 87 | std::map &vsmessages = messages[vs]; 88 | if (vsmessages.find(replicaIdx) != vsmessages.end()) { 89 | // This is a duplicate message 90 | 91 | // But we'll ignore that, replace the old message from 92 | // this replica, and proceed. 93 | // 94 | // XXX Is this the right thing to do? It is for 95 | // speculative replies in SpecPaxos... 96 | } 97 | 98 | vsmessages[replicaIdx] = msg; 99 | 100 | return CheckForQuorum(vs); 101 | } 102 | 103 | void 104 | Add(IDTYPE vs, int replicaIdx, const MSGTYPE &msg) 105 | { 106 | AddAndCheckForQuorum(vs, replicaIdx, msg); 107 | } 108 | 109 | public: 110 | int numRequired; 111 | private: 112 | std::map > messages; 113 | }; 114 | 115 | } // namespace specpaxos 116 | 117 | #endif // _COMMON_QUORUMSET_H_ 118 | -------------------------------------------------------------------------------- /common/replica-inl.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * replica-inl.h: 5 | * inline/template functions for common replica interface 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _COMMON_REPLICA_INL_H_ 32 | #define _COMMON_REPLICA_INL_H_ 33 | 34 | template 35 | void 36 | Replica::Execute(opnum_t opnum, 37 | const Request &msg, 38 | MSG &reply) 39 | { 40 | string res; 41 | ReplicaUpcall(opnum, msg.op(), res); 42 | 43 | reply.set_reply(res); 44 | } 45 | 46 | template 47 | void 48 | Replica::ExecuteUnlogged(const UnloggedRequest &msg, 49 | MSG &reply) 50 | { 51 | string res; 52 | UnloggedUpcall(msg.op(), res); 53 | 54 | reply.set_reply(res); 55 | } 56 | 57 | #endif // _COMMON_REPLICA_INL_H_ 58 | -------------------------------------------------------------------------------- /common/replica.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * replica.cc: 5 | * common functions for replica implementation regardless of 6 | * replication protocol 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #include "common/log.h" 33 | #include "common/replica.h" 34 | 35 | #include "lib/message.h" 36 | 37 | #include 38 | 39 | namespace specpaxos { 40 | 41 | Replica::Replica(const Configuration &configuration, int myIdx, 42 | bool initialize, 43 | Transport *transport, AppReplica *app) 44 | : configuration(configuration), myIdx(myIdx), 45 | transport(transport), app(app) 46 | { 47 | transport->Register(this, configuration, myIdx); 48 | } 49 | 50 | Replica::~Replica() 51 | { 52 | 53 | } 54 | 55 | void 56 | Replica::LeaderUpcall(opnum_t opnum, const string &op, bool &replicate, string &res) 57 | { 58 | app->LeaderUpcall(opnum, op, replicate, res); 59 | } 60 | 61 | void 62 | Replica::ReplicaUpcall(opnum_t opnum, const string &op, string &res) 63 | { 64 | Debug("Making upcall for operation %s", op.c_str()); 65 | app->ReplicaUpcall(opnum, op, res); 66 | 67 | Debug("Upcall result: %s", res.c_str()); 68 | } 69 | 70 | void 71 | Replica::Rollback(opnum_t current, opnum_t to, Log &log) 72 | { 73 | Debug("Making rollback-upcall from " FMT_OPNUM " to " FMT_OPNUM, 74 | current, to); 75 | 76 | std::map reqs; 77 | for (opnum_t x = current; x > to; x--) { 78 | reqs.insert(std::pair(x, 79 | log.Find(x)->request.op())); 80 | } 81 | 82 | app->RollbackUpcall(current, to, reqs); 83 | } 84 | 85 | void 86 | Replica::Commit(opnum_t op) 87 | { 88 | app->CommitUpcall(op); 89 | } 90 | 91 | void 92 | Replica::UnloggedUpcall(const string &op, string &res) 93 | { 94 | app->UnloggedUpcall(op, res); 95 | } 96 | 97 | } // namespace specpaxos 98 | -------------------------------------------------------------------------------- /common/replica.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * replica.h: 5 | * common interface to different replication protocols 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _COMMON_REPLICA_H_ 32 | #define _COMMON_REPLICA_H_ 33 | 34 | 35 | #include "lib/configuration.h" 36 | #include "common/client.h" 37 | #include "common/log.h" 38 | #include "common/request.pb.h" 39 | #include "lib/transport.h" 40 | #include "lib/viewstamp.h" 41 | 42 | namespace specpaxos { 43 | 44 | class Replica; 45 | 46 | enum ReplicaStatus { 47 | STATUS_NORMAL, 48 | STATUS_VIEW_CHANGE, 49 | STATUS_RECOVERING 50 | }; 51 | 52 | class AppReplica 53 | { 54 | public: 55 | AppReplica() { }; 56 | virtual ~AppReplica() { }; 57 | // Invoke callback on the leader, with the option to replicate on success 58 | virtual void LeaderUpcall(opnum_t opnum, const string &str1, bool &replicate, string &str2) { replicate = true; str2 = str1; }; 59 | // Invoke callback on all replicas 60 | virtual void ReplicaUpcall(opnum_t opnum, const string &str1, string &str2) { }; 61 | // Rollback callback on failed speculative operations 62 | virtual void RollbackUpcall(opnum_t current, opnum_t to, const std::map &opMap) { }; 63 | // Commit callback to commit speculative operations 64 | virtual void CommitUpcall(opnum_t) { }; 65 | // Invoke call back for unreplicated operations run on only one replica 66 | virtual void UnloggedUpcall(const string &str1, string &str2) { }; 67 | }; 68 | 69 | class Replica : public TransportReceiver 70 | { 71 | public: 72 | Replica(const Configuration &config, int myIdx, bool initialize, 73 | Transport *transport, AppReplica *app); 74 | virtual ~Replica(); 75 | 76 | protected: 77 | void LeaderUpcall(opnum_t opnum, const string &op, bool &replicate, string &res); 78 | void ReplicaUpcall(opnum_t opnum, const string &op, string &res); 79 | template void Execute(opnum_t opnum, 80 | const Request & msg, 81 | MSG &reply); 82 | void Rollback(opnum_t current, opnum_t to, Log &log); 83 | void Commit(opnum_t op); 84 | void UnloggedUpcall(const string &op, string &res); 85 | template void ExecuteUnlogged(const UnloggedRequest & msg, 86 | MSG &reply); 87 | 88 | protected: 89 | Configuration configuration; 90 | int myIdx; 91 | Transport *transport; 92 | AppReplica *app; 93 | ReplicaStatus status; 94 | }; 95 | 96 | #include "replica-inl.h" 97 | 98 | } // namespace specpaxos 99 | 100 | #endif /* _COMMON_REPLICA_H */ 101 | -------------------------------------------------------------------------------- /common/request.proto: -------------------------------------------------------------------------------- 1 | package specpaxos; 2 | 3 | message Request { 4 | required bytes op = 1; 5 | required uint64 clientid = 2; 6 | required uint64 clientreqid = 3; 7 | } 8 | 9 | message UnloggedRequest { 10 | required bytes op = 1; 11 | required uint64 clientid = 2; 12 | required uint64 clientreqid = 3; 13 | } 14 | -------------------------------------------------------------------------------- /common/tests/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | -------------------------------------------------------------------------------- /fastpaxos/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | SRCS += $(addprefix $(d), \ 4 | replica.cc client.cc) 5 | 6 | PROTOS += $(addprefix $(d), \ 7 | fastpaxos-proto.proto) 8 | 9 | OBJS-fastpaxos-client := $(o)client.o $(o)fastpaxos-proto.o \ 10 | $(OBJS-client) $(LIB-message) \ 11 | $(LIB-configuration) 12 | 13 | OBJS-fastpaxos-replica := $(o)replica.o $(o)fastpaxos-proto.o \ 14 | $(OBJS-replica) $(LIB-message) \ 15 | $(LIB-configuration) 16 | 17 | include $(d)tests/Rules.mk 18 | -------------------------------------------------------------------------------- /fastpaxos/client.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * fastpaxos/client.cc: 5 | * Fast Paxos client 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #include "common/client.h" 32 | #include "common/request.pb.h" 33 | #include "lib/assert.h" 34 | #include "lib/message.h" 35 | #include "lib/transport.h" 36 | #include "fastpaxos/client.h" 37 | #include "fastpaxos/fastpaxos-proto.pb.h" 38 | 39 | namespace specpaxos { 40 | namespace fastpaxos { 41 | 42 | FastPaxosClient::FastPaxosClient(const Configuration &config, 43 | Transport *transport, 44 | uint64_t clientid) 45 | : Client(config, transport, clientid) 46 | { 47 | pendingRequest = NULL; 48 | pendingUnloggedRequest = NULL; 49 | lastReqId = 0; 50 | 51 | requestTimeout = new Timeout(transport, 7000, [this]() { 52 | ResendRequest(); 53 | }); 54 | unloggedRequestTimeout = new Timeout(transport, 1000, [this]() { 55 | UnloggedRequestTimeoutCallback(); 56 | }); 57 | } 58 | 59 | FastPaxosClient::~FastPaxosClient() 60 | { 61 | if (pendingRequest) { 62 | delete pendingRequest; 63 | } 64 | if (pendingUnloggedRequest) { 65 | delete pendingUnloggedRequest; 66 | } 67 | delete requestTimeout; 68 | delete unloggedRequestTimeout; 69 | } 70 | 71 | void 72 | FastPaxosClient::Invoke(const string &request, 73 | continuation_t continuation) 74 | { 75 | // XXX Can only handle one pending request for now 76 | if (pendingRequest != NULL) { 77 | Panic("Client only supports one pending request"); 78 | } 79 | 80 | ++lastReqId; 81 | uint64_t reqId = lastReqId; 82 | pendingRequest = new PendingRequest(request, reqId, continuation); 83 | 84 | SendRequest(); 85 | } 86 | 87 | void 88 | FastPaxosClient::InvokeUnlogged(int replicaIdx, 89 | const string &request, 90 | continuation_t continuation, 91 | timeout_continuation_t timeoutContinuation, 92 | uint32_t timeout) 93 | { 94 | // XXX Can only handle one pending request for now 95 | if (pendingUnloggedRequest != NULL) { 96 | Panic("Client only supports one pending request"); 97 | } 98 | 99 | ++lastReqId; 100 | uint64_t reqId = lastReqId; 101 | 102 | pendingUnloggedRequest = new PendingRequest(request, reqId, continuation); 103 | pendingUnloggedRequest->timeoutContinuation = timeoutContinuation; 104 | 105 | proto::UnloggedRequestMessage reqMsg; 106 | reqMsg.mutable_req()->set_op(pendingUnloggedRequest->request); 107 | reqMsg.mutable_req()->set_clientid(clientid); 108 | reqMsg.mutable_req()->set_clientreqid(pendingUnloggedRequest->clientReqId); 109 | 110 | ASSERT(!unloggedRequestTimeout->Active()); 111 | unloggedRequestTimeout->SetTimeout(timeout); 112 | unloggedRequestTimeout->Start(); 113 | 114 | transport->SendMessageToReplica(this, replicaIdx, reqMsg); 115 | } 116 | 117 | void 118 | FastPaxosClient::SendRequest() 119 | { 120 | proto::RequestMessage reqMsg; 121 | reqMsg.mutable_req()->set_op(pendingRequest->request); 122 | reqMsg.mutable_req()->set_clientid(clientid); 123 | reqMsg.mutable_req()->set_clientreqid(pendingRequest->clientReqId); 124 | 125 | // XXX Try sending only to (what we think is) the leader first 126 | transport->SendMessageToAll(this, reqMsg); 127 | 128 | requestTimeout->Reset(); 129 | } 130 | 131 | void 132 | FastPaxosClient::ResendRequest() 133 | { 134 | Warning("Client timeout; resending request"); 135 | SendRequest(); 136 | } 137 | 138 | 139 | void 140 | FastPaxosClient::ReceiveMessage(const TransportAddress &remote, 141 | const string &type, 142 | const string &data) 143 | { 144 | static proto::ReplyMessage reply; 145 | static proto::UnloggedReplyMessage unloggedReply; 146 | 147 | if (type == reply.GetTypeName()) { 148 | reply.ParseFromString(data); 149 | HandleReply(remote, reply); 150 | } else if (type == unloggedReply.GetTypeName()) { 151 | unloggedReply.ParseFromString(data); 152 | HandleUnloggedReply(remote, unloggedReply); 153 | } else { 154 | Client::ReceiveMessage(remote, type, data); 155 | } 156 | } 157 | 158 | void 159 | FastPaxosClient::HandleReply(const TransportAddress &remote, 160 | const proto::ReplyMessage &msg) 161 | { 162 | Debug("Client received reply for " FMT_VIEWSTAMP, msg.view(), msg.opnum()); 163 | if (pendingRequest == NULL) { 164 | Debug("Received reply when no request was pending"); 165 | return; 166 | } 167 | if (msg.clientreqid() != pendingRequest->clientReqId) { 168 | Debug("Received reply for a different request"); 169 | return; 170 | } 171 | 172 | requestTimeout->Stop(); 173 | 174 | PendingRequest *req = pendingRequest; 175 | pendingRequest = NULL; 176 | 177 | req->continuation(req->request, msg.reply()); 178 | delete req; 179 | } 180 | 181 | void 182 | FastPaxosClient::HandleUnloggedReply(const TransportAddress &remote, 183 | const proto::UnloggedReplyMessage &msg) 184 | { 185 | if (pendingUnloggedRequest == NULL) { 186 | Warning("Received unloggedReply when no request was pending"); 187 | return; 188 | } 189 | 190 | Debug("Client received unloggedReply"); 191 | 192 | unloggedRequestTimeout->Stop(); 193 | 194 | PendingRequest *req = pendingUnloggedRequest; 195 | pendingUnloggedRequest = NULL; 196 | 197 | req->continuation(req->request, msg.reply()); 198 | delete req; 199 | } 200 | 201 | void 202 | FastPaxosClient::UnloggedRequestTimeoutCallback() 203 | { 204 | PendingRequest *req = pendingUnloggedRequest; 205 | pendingUnloggedRequest = NULL; 206 | 207 | Warning("Unlogged request timed out"); 208 | 209 | unloggedRequestTimeout->Stop(); 210 | 211 | req->timeoutContinuation(req->request); 212 | } 213 | 214 | } // namespace vr 215 | } // namespace specpaxos 216 | -------------------------------------------------------------------------------- /fastpaxos/client.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * fastpaxos/client.h: 5 | * Fast Paxos client 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _FASTPAXOS_CLIENT_H_ 32 | #define _FASTPAXOS_CLIENT_H_ 33 | 34 | #include "common/client.h" 35 | #include "lib/configuration.h" 36 | #include "fastpaxos/fastpaxos-proto.pb.h" 37 | 38 | namespace specpaxos { 39 | namespace fastpaxos { 40 | 41 | class FastPaxosClient : public Client 42 | { 43 | public: 44 | FastPaxosClient(const Configuration &config, 45 | Transport *transport, 46 | uint64_t clientid = 0); 47 | virtual ~FastPaxosClient(); 48 | virtual void Invoke(const string &request, 49 | continuation_t continuation); 50 | virtual void InvokeUnlogged(int replicaIdx, 51 | const string &request, 52 | continuation_t continuation, 53 | timeout_continuation_t timeoutContinuation = nullptr, 54 | uint32_t timeout = DEFAULT_UNLOGGED_OP_TIMEOUT); 55 | virtual void ReceiveMessage(const TransportAddress &remote, 56 | const string &type, const string &data); 57 | 58 | protected: 59 | int view; 60 | int opnumber; 61 | uint64_t lastReqId; 62 | 63 | struct PendingRequest 64 | { 65 | string request; 66 | uint64_t clientReqId; 67 | continuation_t continuation; 68 | timeout_continuation_t timeoutContinuation; 69 | inline PendingRequest(string request, uint64_t clientReqId, 70 | continuation_t continuation) 71 | : request(request), clientReqId(clientReqId), 72 | continuation(continuation) { } 73 | }; 74 | PendingRequest *pendingRequest; 75 | PendingRequest *pendingUnloggedRequest; 76 | Timeout *requestTimeout; 77 | Timeout *unloggedRequestTimeout; 78 | 79 | void SendRequest(); 80 | void ResendRequest(); 81 | void HandleReply(const TransportAddress &remote, 82 | const proto::ReplyMessage &msg); 83 | void HandleUnloggedReply(const TransportAddress &remote, 84 | const proto::UnloggedReplyMessage &msg); 85 | void UnloggedRequestTimeoutCallback(); 86 | }; 87 | 88 | } // namespace specpaxos::fastpaxos 89 | } // namespace specpaxos 90 | 91 | #endif /* _FASTPAXOS_CLIENT_H_ */ 92 | -------------------------------------------------------------------------------- /fastpaxos/fastpaxos-proto.proto: -------------------------------------------------------------------------------- 1 | import "common/request.proto"; 2 | 3 | package specpaxos.fastpaxos.proto; 4 | 5 | message RequestMessage { 6 | required specpaxos.Request req = 1; 7 | } 8 | 9 | message ReplyMessage { 10 | required uint64 view = 1; 11 | required uint64 opnum = 2; 12 | required bytes reply = 3; 13 | required uint64 clientreqid = 4; 14 | } 15 | 16 | message UnloggedRequestMessage { 17 | required specpaxos.UnloggedRequest req = 1; 18 | } 19 | 20 | message UnloggedReplyMessage { 21 | required bytes reply = 1; 22 | } 23 | 24 | message PrepareOKMessage { 25 | required uint64 view = 1; 26 | required uint64 opnum = 2; 27 | required uint32 replicaIdx = 3; 28 | required specpaxos.Request req = 4; 29 | required uint32 slowpath = 5; 30 | } 31 | 32 | message PrepareMessage { 33 | required uint64 view = 1; 34 | required uint64 opnum = 2; 35 | required specpaxos.Request req = 3; 36 | } 37 | 38 | message CommitMessage { 39 | required uint64 view = 1; 40 | required uint64 opnum = 2; 41 | required specpaxos.Request req = 3; 42 | } 43 | 44 | message RequestStateTransferMessage { 45 | required uint64 view = 1; 46 | required uint64 opnum = 2; 47 | } 48 | 49 | message StateTransferMessage { 50 | message LogEntry { 51 | required uint64 view = 1; 52 | required uint64 opnum = 2; 53 | required specpaxos.Request request = 3; 54 | optional uint32 state = 4; 55 | optional bytes hash = 5; 56 | } 57 | required uint64 view = 1; 58 | required uint64 opnum = 2; 59 | repeated LogEntry entries = 3; 60 | required uint64 lastop = 4; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /fastpaxos/replica.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * fastpaxos/replica.h: 5 | * Fast Paxos protocol 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _FASTPAXOS_REPLICA_H_ 32 | #define _FASTPAXOS_REPLICA_H_ 33 | 34 | #include "lib/configuration.h" 35 | #include "common/log.h" 36 | #include "common/replica.h" 37 | #include "common/quorumset.h" 38 | #include "fastpaxos/fastpaxos-proto.pb.h" 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | namespace specpaxos { 45 | namespace fastpaxos { 46 | 47 | class FastPaxosReplica : public Replica 48 | { 49 | public: 50 | FastPaxosReplica(Configuration config, int myIdx, bool initialize, 51 | Transport *transport, AppReplica *app); 52 | ~FastPaxosReplica(); 53 | 54 | void ReceiveMessage(const TransportAddress &remote, 55 | const string &type, const string &data); 56 | 57 | private: 58 | view_t view; 59 | opnum_t lastCommitted; 60 | opnum_t lastFastPath; 61 | opnum_t lastSlowPath; 62 | view_t lastRequestStateTransferView; 63 | opnum_t lastRequestStateTransferOpnum; 64 | std::list > pendingPrepares; 66 | std::list > pendingPrepareOKs; 68 | proto::PrepareMessage lastPrepare; 69 | 70 | Log log; 71 | std::map > clientAddresses; 72 | struct ClientTableEntry 73 | { 74 | uint64_t lastReqId; 75 | bool replied; 76 | proto::ReplyMessage reply; 77 | }; 78 | std::map clientTable; 79 | 80 | QuorumSet slowPrepareOKQuorum; 81 | QuorumSet fastPrepareOKQuorum; 82 | 83 | Timeout *stateTransferTimeout; 84 | Timeout *resendPrepareTimeout; 85 | 86 | bool AmLeader() const; 87 | void CommitUpTo(opnum_t upto); 88 | void SendPrepareOKs(opnum_t oldLastOp); 89 | void RequestStateTransfer(); 90 | void EnterView(view_t newview); 91 | void UpdateClientTable(const Request &req); 92 | void ResendPrepare(); 93 | 94 | void HandleRequest(const TransportAddress &remote, 95 | const proto::RequestMessage &msg); 96 | void HandleUnloggedRequest(const TransportAddress &remote, 97 | const proto::UnloggedRequestMessage &msg); 98 | 99 | void HandlePrepare(const TransportAddress &remote, 100 | const proto::PrepareMessage &msg); 101 | void HandlePrepareOK(const TransportAddress &remote, 102 | const proto::PrepareOKMessage &msg); 103 | void HandleCommit(const TransportAddress &remote, 104 | const proto::CommitMessage &msg); 105 | void HandleRequestStateTransfer(const TransportAddress &remote, 106 | const proto::RequestStateTransferMessage &msg); 107 | void HandleStateTransfer(const TransportAddress &remote, 108 | const proto::StateTransferMessage &msg); 109 | }; 110 | 111 | } // namespace specpaxos::vr 112 | } // namespace specpaxos 113 | 114 | #endif /* _FASTPAXOS_REPLICA_H_ */ 115 | -------------------------------------------------------------------------------- /fastpaxos/tests/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | GTEST_SRCS += $(d)fastpaxos-test.cc 4 | 5 | $(d)fastpaxos-test: $(o)fastpaxos-test.o \ 6 | $(OBJS-fastpaxos-replica) $(OBJS-fastpaxos-client) \ 7 | $(LIB-simtransport) \ 8 | $(GTEST_MAIN) 9 | 10 | TEST_BINS += $(d)fastpaxos-test 11 | -------------------------------------------------------------------------------- /lib/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | SRCS += $(addprefix $(d), \ 4 | lookup3.cc message.cc memory.cc \ 5 | latency.cc configuration.cc transport.cc udptransport.cc simtransport.cc) 6 | 7 | PROTOS += $(addprefix $(d), \ 8 | latency-format.proto) 9 | 10 | LIB-hash := $(o)lookup3.o 11 | 12 | LIB-message := $(o)message.o $(LIB-hash) 13 | 14 | LIB-hashtable := $(LIB-hash) $(LIB-message) 15 | 16 | LIB-memory := $(o)memory.o 17 | 18 | LIB-latency := $(o)latency.o $(o)latency-format.o $(LIB-message) 19 | 20 | LIB-configuration := $(o)configuration.o $(LIB-message) 21 | 22 | LIB-transport := $(o)transport.o $(LIB-message) $(LIB-configuration) 23 | 24 | LIB-simtransport := $(o)simtransport.o $(LIB-transport) 25 | 26 | LIB-udptransport := $(o)udptransport.o $(LIB-transport) 27 | 28 | 29 | include $(d)tests/Rules.mk 30 | -------------------------------------------------------------------------------- /lib/assert.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * assert.h: 5 | * assertion macros that integrate with the logging framework 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _LIB_ASSERT_H_ 32 | #define _LIB_ASSERT_H_ 33 | 34 | /* 35 | * Assertion macros. 36 | * 37 | * Currently these mostly just wrap the standard C assert but 38 | * eventually they should tie in better with the logging framework. 39 | */ 40 | #include 41 | #include 42 | #include 43 | #include "lib/message.h" 44 | 45 | #define ASSERT(x) Assert(x) 46 | 47 | // XXX These should output the expected and actual values in addition 48 | // to failing. 49 | #define ASSERT_EQ(x, y) Assert(x == y) 50 | #define ASSERT_LT(x, y) Assert(x < y) 51 | #define ASSERT_GT(x, y) Assert(x > y) 52 | #define ASSERT_LE(x, y) Assert(x <= y) 53 | #define ASSERT_GE(x, y) Assert(x >= y) 54 | 55 | #define NOT_REACHABLE() do { \ 56 | fprintf(stderr, "NOT_REACHABLE point reached: %s, line %d\n", \ 57 | __FILE__, __LINE__); \ 58 | abort(); \ 59 | } while (0) 60 | 61 | #define NOT_IMPLEMENTED() do { \ 62 | fprintf(stderr, "NOT_IMPLEMENTED point reached: %s, line %d\n", \ 63 | __FILE__, __LINE__); \ 64 | abort(); \ 65 | } while (0) 66 | 67 | 68 | #endif /* _LIB_ASSERT_H */ 69 | -------------------------------------------------------------------------------- /lib/configuration.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * configuration.cc: 5 | * Representation of a replica group configuration, i.e. the number 6 | * and list of replicas in the group 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #include "lib/assert.h" 33 | #include "lib/configuration.h" 34 | #include "lib/message.h" 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | namespace specpaxos { 42 | 43 | ReplicaAddress::ReplicaAddress(const string &host, const string &port) 44 | : host(host), port(port) 45 | { 46 | 47 | } 48 | 49 | bool 50 | ReplicaAddress::operator==(const ReplicaAddress &other) const { 51 | return ((host == other.host) && 52 | (port == other.port)); 53 | } 54 | 55 | 56 | Configuration::Configuration(const Configuration &c) 57 | : n(c.n), f(c.f), replicas(c.replicas), hasMulticast(c.hasMulticast) 58 | { 59 | multicastAddress = NULL; 60 | if (hasMulticast) { 61 | multicastAddress = new ReplicaAddress(*c.multicastAddress); 62 | } 63 | } 64 | 65 | Configuration::Configuration(int n, int f, 66 | std::vector replicas, 67 | ReplicaAddress *multicastAddress) 68 | : n(n), f(f), replicas(replicas) 69 | { 70 | if (multicastAddress) { 71 | hasMulticast = true; 72 | this->multicastAddress = 73 | new ReplicaAddress(*multicastAddress); 74 | } else { 75 | hasMulticast = false; 76 | multicastAddress = NULL; 77 | } 78 | } 79 | 80 | Configuration::Configuration(std::ifstream &file) 81 | { 82 | f = -1; 83 | hasMulticast = false; 84 | multicastAddress = NULL; 85 | 86 | while (!file.eof()) { 87 | // Read a line 88 | string line; 89 | getline(file, line);; 90 | 91 | // Ignore comments 92 | if ((line.size() == 0) || (line[0] == '#')) { 93 | continue; 94 | } 95 | 96 | // Get the command 97 | // This is pretty horrible, but C++ does promise that &line[0] 98 | // is going to be a mutable contiguous buffer... 99 | char *cmd = strtok(&line[0], " \t"); 100 | 101 | if (strcasecmp(cmd, "f") == 0) { 102 | char *arg = strtok(NULL, " \t"); 103 | if (!arg) { 104 | Panic ("'f' configuration line requires an argument"); 105 | } 106 | char *strtolPtr; 107 | f = strtoul(arg, &strtolPtr, 0); 108 | if ((*arg == '\0') || (*strtolPtr != '\0')) { 109 | Panic("Invalid argument to 'f' configuration line"); 110 | } 111 | } else if (strcasecmp(cmd, "replica") == 0) { 112 | char *arg = strtok(NULL, " \t"); 113 | if (!arg) { 114 | Panic ("'replica' configuration line requires an argument"); 115 | } 116 | 117 | char *host = strtok(arg, ":"); 118 | char *port = strtok(NULL, ""); 119 | 120 | if (!host || !port) { 121 | Panic("Configuration line format: 'replica host:port'"); 122 | } 123 | 124 | replicas.push_back(ReplicaAddress(string(host), string(port))); 125 | } else if (strcasecmp(cmd, "multicast") == 0) { 126 | char *arg = strtok(NULL, " \t"); 127 | if (!arg) { 128 | Panic ("'multicast' configuration line requires an argument"); 129 | } 130 | 131 | char *host = strtok(arg, ":"); 132 | char *port = strtok(NULL, ""); 133 | 134 | if (!host || !port) { 135 | Panic("Configuration line format: 'multicast host:port'"); 136 | } 137 | 138 | multicastAddress = new ReplicaAddress(string(host), 139 | string(port)); 140 | hasMulticast = true; 141 | } else { 142 | Panic("Unknown configuration directive: %s", cmd); 143 | } 144 | } 145 | 146 | n = replicas.size(); 147 | if (n == 0) { 148 | Panic("Configuration did not specify any replicas"); 149 | } 150 | 151 | if (f == -1) { 152 | Panic("Configuration did not specify a 'f' parameter"); 153 | } 154 | } 155 | 156 | Configuration::~Configuration() 157 | { 158 | if (hasMulticast) { 159 | delete multicastAddress; 160 | } 161 | } 162 | 163 | ReplicaAddress 164 | Configuration::replica(int idx) const 165 | { 166 | return replicas[idx]; 167 | } 168 | 169 | const ReplicaAddress * 170 | Configuration::multicast() const 171 | { 172 | if (hasMulticast) { 173 | return multicastAddress; 174 | } else { 175 | return nullptr; 176 | } 177 | } 178 | 179 | int 180 | Configuration::QuorumSize() const 181 | { 182 | return f+1; 183 | } 184 | 185 | int 186 | Configuration::FastQuorumSize() const 187 | { 188 | return f + (f+1)/2 + 1; 189 | } 190 | 191 | bool 192 | Configuration::operator==(const Configuration &other) const 193 | { 194 | if ((n != other.n) || 195 | (f != other.f) || 196 | (replicas != other.replicas) || 197 | (hasMulticast != other.hasMulticast)) { 198 | return false; 199 | } 200 | 201 | if (hasMulticast) { 202 | if (*multicastAddress != *other.multicastAddress) { 203 | return false; 204 | } 205 | } 206 | 207 | return true; 208 | } 209 | 210 | } // namespace specpaxos 211 | -------------------------------------------------------------------------------- /lib/configuration.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * configuration.h: 5 | * Representation of a replica group configuration, i.e. the number 6 | * and list of replicas in the group 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _LIB_CONFIGURATION_H_ 33 | #define _LIB_CONFIGURATION_H_ 34 | 35 | #include "lib/viewstamp.h" 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | using std::string; 43 | 44 | namespace specpaxos { 45 | 46 | struct ReplicaAddress 47 | { 48 | string host; 49 | string port; 50 | ReplicaAddress(const string &host, const string &port); 51 | bool operator==(const ReplicaAddress &other) const; 52 | inline bool operator!=(const ReplicaAddress &other) const { 53 | return !(*this == other); 54 | } 55 | }; 56 | 57 | 58 | class Configuration 59 | { 60 | public: 61 | Configuration(const Configuration &c); 62 | Configuration(int n, int f, std::vector replicas, 63 | ReplicaAddress *multicastAddress = nullptr); 64 | Configuration(std::ifstream &file); 65 | virtual ~Configuration(); 66 | ReplicaAddress replica(int idx) const; 67 | const ReplicaAddress *multicast() const; 68 | inline int GetLeaderIndex(view_t view) const { 69 | return (view % n); 70 | }; 71 | int QuorumSize() const; 72 | int FastQuorumSize() const; 73 | bool operator==(const Configuration &other) const; 74 | inline bool operator!= (const Configuration &other) const { 75 | return !(*this == other); 76 | } 77 | 78 | public: 79 | int n; // number of replicas 80 | int f; // number of failures tolerated 81 | private: 82 | std::vector replicas; 83 | ReplicaAddress *multicastAddress; 84 | bool hasMulticast; 85 | }; 86 | 87 | } // namespace specpaxos 88 | 89 | namespace std { 90 | template <> struct hash 91 | { 92 | size_t operator()(const specpaxos::ReplicaAddress & x) const 93 | { 94 | return hash()(x.host) * 37 + hash()(x.port); 95 | } 96 | }; 97 | } 98 | 99 | namespace std { 100 | template <> struct hash 101 | { 102 | size_t operator()(const specpaxos::Configuration & x) const 103 | { 104 | size_t out = 0; 105 | out = x.n * 37 + x.f; 106 | for (int i = 0; i < x.n; i++) { 107 | out *= 37; 108 | out += hash()(x.replica(i)); 109 | } 110 | return out; 111 | } 112 | }; 113 | } 114 | 115 | 116 | #endif /* _LIB_CONFIGURATION_H_ */ 117 | -------------------------------------------------------------------------------- /lib/hash.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * hash.h: 5 | * header defining hash functions 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * Copyright 2009-2012 Massachusetts Institute of Technology 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _LIB_HASH_H_ 33 | #define _LIB_HASH_H_ 34 | 35 | #include /* defines uint32_t etc */ 36 | #include /* size_t */ 37 | 38 | #ifdef __APPLE__ 39 | /* OS X: defines __LITTLE_ENDIAN__ or __BIG_ENDIAN__ */ 40 | #include 41 | #else 42 | #include /* attempt to define endianness */ 43 | #endif 44 | 45 | 46 | #define hashsize(n) ((uint32_t)1<<(n)) 47 | #define hashmask(n) (hashsize(n)-1) 48 | 49 | /* 50 | * My best guess at if you are big-endian or little-endian. This may 51 | * need adjustment. 52 | */ 53 | #if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ 54 | __BYTE_ORDER == __LITTLE_ENDIAN) || \ 55 | (defined(i386) || defined(__i386__) || defined(__i486__) || \ 56 | defined(__i586__) || defined(__i686__) || defined(vax) || \ 57 | defined(MIPSEL) || defined(__LITTLE_ENDIAN__)) 58 | # define HASH_LITTLE_ENDIAN 1 59 | # define HASH_BIG_ENDIAN 0 60 | #elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ 61 | __BYTE_ORDER == __BIG_ENDIAN) || \ 62 | (defined(sparc) || defined(POWERPC) || defined(mc68000) || \ 63 | defined(sel) || defined(__LITTLE_ENDIAN__)) 64 | # define HASH_LITTLE_ENDIAN 0 65 | # define HASH_BIG_ENDIAN 1 66 | #else 67 | # define HASH_LITTLE_ENDIAN 0 68 | # define HASH_BIG_ENDIAN 0 69 | #endif 70 | 71 | uint32_t hash(const void *key, size_t length, uint32_t initval); 72 | 73 | #endif // _LIB_HASH_H_ 74 | 75 | -------------------------------------------------------------------------------- /lib/latency-format.proto: -------------------------------------------------------------------------------- 1 | package specpaxos.latency.format; 2 | 3 | message LatencyDist 4 | { 5 | required uint32 type = 1; 6 | required uint64 min = 2; 7 | required uint64 max = 3; 8 | required uint64 total = 4; 9 | required uint64 count = 5; 10 | repeated uint32 buckets = 6; 11 | } 12 | 13 | message Latency 14 | { 15 | required string name = 1; 16 | repeated LatencyDist dists = 2; 17 | } 18 | 19 | message LatencyFile 20 | { 21 | repeated Latency latencies = 1; 22 | } 23 | -------------------------------------------------------------------------------- /lib/latency.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * latency.h: 5 | * latency profiling functions 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * Copyright 2009-2012 Massachusetts Institute of Technology 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _LIB_LATENCY_H_ 33 | #define _LIB_LATENCY_H_ 34 | 35 | #include "lib/latency-format.pb.h" 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | // The number of the maximum distribution type. Since we use 44 | // characters as distribution types, this is 127. We could probably 45 | // shift things down by 32 to save some space, but that's probably not 46 | // worth anything. 47 | #define LATENCY_MAX_DIST 127 48 | 49 | // The maximum number of unique distribution types in a single latency 50 | // distribution. 51 | #define LATENCY_DIST_POOL_SIZE 5 52 | 53 | // The width of a printed histogram in characters. 54 | #define LATENCY_HISTOGRAM_WIDTH 50 55 | 56 | // The number of histogram buckets. 57 | #define LATENCY_NUM_BUCKETS 65 58 | 59 | typedef struct Latency_Frame_t 60 | { 61 | struct timespec start; 62 | uint64_t accum; 63 | struct Latency_Frame_t *parent; 64 | } Latency_Frame_t; 65 | 66 | typedef struct Latency_Dist_t 67 | { 68 | uint64_t min, max, total, count; 69 | uint32_t buckets[LATENCY_NUM_BUCKETS]; 70 | char type; 71 | } Latency_Dist_t; 72 | 73 | typedef struct Latency_t 74 | { 75 | const char *name; 76 | 77 | Latency_Dist_t *dists[LATENCY_MAX_DIST]; 78 | Latency_Dist_t distPool[LATENCY_DIST_POOL_SIZE]; 79 | int distPoolNext; 80 | 81 | Latency_Frame_t *bottom; 82 | Latency_Frame_t defaultFrame; 83 | 84 | struct Latency_t *next; 85 | } Latency_t; 86 | 87 | #define DEFINE_LATENCY(name) \ 88 | static Latency_t name; \ 89 | static __attribute__((constructor)) void _##name##_init(void) \ 90 | { \ 91 | _Latency_Init(&name, #name); \ 92 | } 93 | 94 | void _Latency_Init(Latency_t *l, const char *name); 95 | 96 | void Latency_StartRec(Latency_t *l, Latency_Frame_t *fr); 97 | uint64_t Latency_EndRecType(Latency_t *l, Latency_Frame_t *fr, char type); 98 | void Latency_Pause(Latency_t *l); 99 | void Latency_Resume(Latency_t *l); 100 | 101 | void Latency_Sum(Latency_t *dest, Latency_t *summand); 102 | 103 | void Latency_Dump(Latency_t *l); 104 | void Latency_DumpAll(void); 105 | void Latency_FlushTo(const char *fname); 106 | void Latency_Flush(void); 107 | 108 | void Latency_Put(Latency_t *l, 109 | ::specpaxos::latency::format::Latency &out); 110 | bool Latency_TryGet(const ::specpaxos::latency::format::Latency &in, 111 | Latency_t *l); 112 | 113 | static inline void 114 | Latency_Start(Latency_t *l) 115 | { 116 | Latency_StartRec(l, &l->defaultFrame); 117 | } 118 | 119 | static inline uint64_t 120 | Latency_EndRec(Latency_t *l, Latency_Frame_t *fr) 121 | { 122 | return Latency_EndRecType(l, fr, '='); 123 | } 124 | 125 | static inline uint64_t 126 | Latency_EndType(Latency_t *l, char type) 127 | { 128 | return Latency_EndRecType(l, &l->defaultFrame, type); 129 | } 130 | 131 | static inline uint64_t 132 | Latency_End(Latency_t *l) 133 | { 134 | return Latency_EndRec(l, &l->defaultFrame); 135 | } 136 | 137 | char *LatencyFmtNS(uint64_t ns, char *buf); 138 | 139 | 140 | #endif // _LIB_LATENCY_H_ 141 | -------------------------------------------------------------------------------- /lib/memory.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * memory.cc: 5 | * parsing and pretty-printing of memory sizes 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * Copyright 2009-2012 Massachusetts Institute of Technology 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #include "memory.h" 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | char * 39 | Memory_FmtSize(char *buf, size_t n) 40 | { 41 | char suffix = 0; 42 | if ((n & 0x3ff) == 0) { 43 | n >>= 10; 44 | suffix = 'K'; 45 | } 46 | if ((n & 0x3ff) == 0) { 47 | n >>= 10; 48 | suffix = 'M'; 49 | } 50 | if ((n & 0x3ff) == 0) { 51 | n >>= 10; 52 | suffix = 'G'; 53 | } 54 | if (suffix) { 55 | sprintf(buf, "%llu%c", (unsigned long long)n, suffix); 56 | } else { 57 | sprintf(buf, "%llu", (unsigned long long)n); 58 | } 59 | return buf; 60 | } 61 | 62 | static unsigned long long 63 | Memory_ReadSize1(const char *buf, const char **endPtr) 64 | { 65 | unsigned long long res = strtoull(buf, (char **)endPtr, 0); 66 | switch (**endPtr) { 67 | case 'G': 68 | case 'g': 69 | res <<= 10; 70 | case 'M': 71 | case 'm': 72 | res <<= 10; 73 | case 'K': 74 | case 'k': 75 | res <<= 10; 76 | ++(*endPtr); 77 | } 78 | return res; 79 | } 80 | 81 | size_t 82 | Memory_ReadSize(const char *buf, const char **endPtr) 83 | { 84 | unsigned long long ret = 0; 85 | bool more; 86 | 87 | do { 88 | ret += Memory_ReadSize1(buf, &buf); 89 | if (*buf == '+' && *(buf+1)) { 90 | more = true; 91 | ++buf; 92 | } else { 93 | more = false; 94 | } 95 | } while (more); 96 | 97 | if (endPtr) 98 | *endPtr = buf; 99 | return (size_t)ret; 100 | } 101 | -------------------------------------------------------------------------------- /lib/memory.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * memory.h: 5 | * parsing and pretty-printing of memory sizes 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * Copyright 2009-2012 Massachusetts Institute of Technology 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _LIB_MEMORY_H_ 33 | #define _LIB_MEMORY_H_ 34 | 35 | #include 36 | 37 | // Experimentally determined malloc size (for smallish objects, at 38 | // least). This is (v+8) rounded up to the nearest multiple of 16 39 | // (though anything less than 24 takes 32 bytes). Obviously this 40 | // doesn't account for fragmentation. 41 | #define MALLOC_SIZE(size) ((size) <= 24 ? 32 : (((size) + 7) | 15) + 1) 42 | 43 | #define MEMORY_FMTSIZE_BUF 22 44 | 45 | char *Memory_FmtSize(char *buf, size_t n); 46 | size_t Memory_ReadSize(const char *buf, const char **endPtr); 47 | 48 | #endif // _LIB_MEMORY_H_ 49 | -------------------------------------------------------------------------------- /lib/message.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * message.h: 5 | * logging functions 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * Copyright 2009-2012 Massachusetts Institute of Technology 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _LIB_MESSAGE_H_ 33 | #define _LIB_MESSAGE_H_ 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | enum Message_Type { 40 | MSG_PANIC = 0, 41 | MSG_WARNING, 42 | MSG_NOTICE, 43 | MSG_DEBUG, 44 | MSG_NUM_TYPES, 45 | MSG_PERROR = 1 << 16, 46 | }; 47 | 48 | #define PanicFlags(flags, msg...) \ 49 | do { \ 50 | _Message((Message_Type)(MSG_PANIC|flags), __FILE__, __LINE__, __func__, msg); \ 51 | _Panic(); \ 52 | } while (0) 53 | #define MessageFlags(flags, msg...) \ 54 | _Message(flags, __FILE__, __LINE__, __func__, msg) 55 | 56 | #define Panic(msg...) PanicFlags(0, msg) 57 | #define Warning(msg...) MessageFlags(MSG_WARNING, msg) 58 | #define Notice(msg...) MessageFlags(MSG_NOTICE, msg) 59 | #define QNotice(msg...) _Message(MSG_NOTICE, NULL, 0, NULL, msg) 60 | 61 | #define PPanic(msg...) PanicFlags(MSG_PERROR, msg) 62 | #define PWarning(msg...) MessageFlags((Message_Type)(MSG_WARNING|MSG_PERROR), msg) 63 | #define PNotice(msg...) MessageFlags((Message_Type)(MSG_NOTICE|MSG_PERROR), msg) 64 | 65 | void _Message(enum Message_Type type, 66 | const char *fname, int line, const char *func, 67 | const char *fmt, ...) 68 | __attribute__((format(printf,5,6))); 69 | void _Panic(void) __attribute__((noreturn)); 70 | bool _Message_DebugEnabled(const char *fname); 71 | 72 | void Message_VA(enum Message_Type type, 73 | const char *fname, int line, const char *func, 74 | const char *fmt, va_list args); 75 | 76 | void _Message_VA(enum Message_Type type, FILE *fp, 77 | const char *fname, int line, const char *func, 78 | const char *fmt, va_list args); 79 | 80 | const char *Message_DFree(char *buf); 81 | void Message_DoFrees(void); 82 | 83 | void _Message_Hexdump(const void *data, int len); 84 | char *Message_FmtBlob(const void *data, int len); 85 | void PanicOnSignal(int signo); 86 | 87 | // This is not a mistake. We actually want exactly one of these flags 88 | // per file that uses the Debug macro. 89 | static __attribute__((unused)) signed char _Message_FileDebugFlag = -1; 90 | 91 | #define Debug(msg...) \ 92 | do { \ 93 | if (Message_DebugEnabled(__FILE__)) \ 94 | MessageFlags(MSG_DEBUG, msg); \ 95 | } while (0) 96 | #define Message_Hexdump(data, len) \ 97 | do { \ 98 | if (Message_DebugEnabled(__FILE__)) \ 99 | _Message_Hexdump(data, len); \ 100 | } while (0) 101 | #ifndef NASSERT 102 | #define Assert(pred) \ 103 | do { \ 104 | if (!(pred)) \ 105 | Panic("Assertion `%s' failed", #pred); \ 106 | } while (0) 107 | #else 108 | #define Assert(pred) 109 | #endif 110 | 111 | static inline bool 112 | Message_DebugEnabled(const char *fname) 113 | { 114 | if (_Message_FileDebugFlag >= 0) 115 | return _Message_FileDebugFlag; 116 | _Message_FileDebugFlag = _Message_DebugEnabled(fname); 117 | return _Message_FileDebugFlag; 118 | } 119 | 120 | #include "hash.h" 121 | 122 | #define FMT_BLOB "<%ld %08x>" 123 | #define VA_BLOB(d, l) (long)l, hash(d, l, 0) 124 | #define VA_BLOB_STRING(d) (long)d.size(), \ 125 | hash(d.c_str(), d.size(), 0) 126 | 127 | #define FMT_VBLOB "%s" 128 | #define XVA_VBLOB(d, l) Message_DFree(Message_FmtBlob(d, l)) 129 | #define XVA_VBLOB_STRING(d) Message_DFree(Message_FmtBlob(d.c_str(), \ 130 | d.size())) 131 | 132 | #endif // _LIB_MESSAGE_H_ 133 | -------------------------------------------------------------------------------- /lib/simtransport.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * simtransport.h: 5 | * simulated message-passing interface for testing use 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _LIB_SIMTRANSPORT_H_ 32 | #define _LIB_SIMTRANSPORT_H_ 33 | 34 | #include "lib/transport.h" 35 | #include "lib/transportcommon.h" 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | class SimulatedTransportAddress : public TransportAddress 42 | { 43 | public: 44 | SimulatedTransportAddress * clone() const; 45 | int GetAddr() const; 46 | bool operator==(const SimulatedTransportAddress &other) const; 47 | inline bool operator!=(const SimulatedTransportAddress &other) const 48 | { 49 | return !(*this == other); 50 | } 51 | private: 52 | SimulatedTransportAddress(int addr); 53 | 54 | int addr; 55 | friend class SimulatedTransport; 56 | }; 57 | 58 | class SimulatedTransport : 59 | public TransportCommon 60 | { 61 | typedef std::function filter_t; 64 | public: 65 | SimulatedTransport(); 66 | ~SimulatedTransport(); 67 | void Register(TransportReceiver *receiver, 68 | const specpaxos::Configuration &config, 69 | int replicaIdx); 70 | void Run(); 71 | void AddFilter(int id, filter_t filter); 72 | void RemoveFilter(int id); 73 | int Timer(uint64_t ms, timer_callback_t cb); 74 | bool CancelTimer(int id); 75 | void CancelAllTimers(); 76 | 77 | protected: 78 | bool SendMessageInternal(TransportReceiver *src, 79 | const SimulatedTransportAddress &dstAddr, 80 | const Message &m, 81 | bool multicast); 82 | 83 | SimulatedTransportAddress 84 | LookupAddress(const specpaxos::Configuration &cfg, int idx); 85 | const SimulatedTransportAddress * 86 | LookupMulticastAddress(const specpaxos::Configuration *cfg); 87 | 88 | private: 89 | struct QueuedMessage { 90 | int dst; 91 | int src; 92 | string type; 93 | string msg; 94 | inline QueuedMessage(int dst, int src, 95 | const string &type, const string &msg) : 96 | dst(dst), src(src), type(type), msg(msg) { } 97 | }; 98 | struct PendingTimer { 99 | uint64_t when; 100 | int id; 101 | timer_callback_t cb; 102 | }; 103 | 104 | std::deque queue; 105 | std::map endpoints; 106 | int lastAddr; 107 | // std::map replicas; 108 | std::map replicaIdxs; 109 | std::multimap filters; 110 | std::multimap timers; 111 | int lastTimerId; 112 | uint64_t vtime; 113 | bool processTimers; 114 | }; 115 | 116 | #endif // _LIB_SIMTRANSPORT_H_ 117 | -------------------------------------------------------------------------------- /lib/tests/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | # 4 | # gtest-based tests 5 | # 6 | GTEST_SRCS += $(addprefix $(d), \ 7 | configuration-test.cc \ 8 | simtransport-test.cc) 9 | 10 | PROTOS += $(d)simtransport-testmessage.proto 11 | 12 | $(d)configuration-test: $(o)configuration-test.o $(LIB-configuration) $(GTEST_MAIN) 13 | 14 | TEST_BINS += $(d)configuration-test 15 | 16 | $(d)simtransport-test: $(o)simtransport-test.o $(LIB-simtransport) $(o)simtransport-testmessage.o $(GTEST_MAIN) 17 | 18 | TEST_BINS += $(d)simtransport-test 19 | -------------------------------------------------------------------------------- /lib/tests/configuration-test-1.conf: -------------------------------------------------------------------------------- 1 | # This is a test configuration file 2 | f 1 3 | replica localhost:12345 4 | replica localhost:12346 5 | replica localhost:12347 6 | multicast localhost:12348 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/tests/simtransport-testmessage.proto: -------------------------------------------------------------------------------- 1 | package specpaxos.test; 2 | 3 | message TestMessage { 4 | required string test = 1; 5 | } 6 | -------------------------------------------------------------------------------- /lib/timeval.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * timeval.h: 5 | * utility functions for manipulating timevals 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * Copyright 2009-2012 Massachusetts Institute of Technology 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _LIB_TIMEVAL_H_ 33 | #define _LIB_TIMEVAL_H_ 34 | 35 | #include 36 | #include /* strftime */ 37 | #include /* malloc */ 38 | #include /* sprintf */ 39 | #include 40 | 41 | static inline struct timeval 42 | timeval_sub(struct timeval a, struct timeval b) { 43 | struct timeval result; 44 | 45 | if (a.tv_usec < b.tv_usec) { 46 | result.tv_sec = a.tv_sec - b.tv_sec - 1; 47 | result.tv_usec = a.tv_usec + 1000000 - b.tv_usec; 48 | } else { 49 | result.tv_sec = a.tv_sec - b.tv_sec; 50 | result.tv_usec = a.tv_usec - b.tv_usec; 51 | } 52 | return result; 53 | }; 54 | 55 | static inline bool 56 | timeval_lessthan(struct timeval a, struct timeval b) { 57 | return ((a.tv_sec < b.tv_sec) || 58 | ((a.tv_sec == b.tv_sec) && 59 | (a.tv_usec < b.tv_usec))); 60 | }; 61 | 62 | static inline struct timeval 63 | Timeval_FromSecs(double secs) 64 | { 65 | struct timeval res; 66 | res.tv_sec = (time_t)secs; 67 | res.tv_usec = (time_t) (secs - (long long)secs) * 1000000; 68 | return res; 69 | } 70 | 71 | #define FMT_TIMEVAL_ABS "%s" 72 | #define XVA_TIMEVAL_ABS(t) Message_DFree(Timeval_FmtAbs(t)) 73 | 74 | static inline char * 75 | Timeval_FmtAbs(struct timeval tv) 76 | { 77 | static const int LEN = 32; 78 | char *buf = (char *)malloc(LEN); 79 | if (!buf) 80 | return NULL; 81 | strftime(buf, LEN, "%H:%M:%S", localtime(&tv.tv_sec)); 82 | sprintf(buf + strlen(buf), ":%06ld", (long) tv.tv_usec); 83 | return buf; 84 | } 85 | 86 | #define FMT_TIMEVAL_DIFF "%lld.%06ld" 87 | #define VA_TIMEVAL_DIFF(t) (long long)t.tv_sec, (long) t.tv_usec 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /lib/transport.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * transport.cc: 5 | * message-passing network interface; common definitions 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #include "lib/assert.h" 32 | #include "lib/transport.h" 33 | 34 | TransportReceiver::~TransportReceiver() 35 | { 36 | delete this->myAddress; 37 | } 38 | 39 | void 40 | TransportReceiver::SetAddress(const TransportAddress *addr) 41 | { 42 | this->myAddress = addr; 43 | } 44 | 45 | const TransportAddress & 46 | TransportReceiver::GetAddress() 47 | { 48 | return *(this->myAddress); 49 | } 50 | 51 | Timeout::Timeout(Transport *transport, uint64_t ms, timer_callback_t cb) 52 | : transport(transport), ms(ms), cb(cb) 53 | { 54 | timerId = 0; 55 | } 56 | 57 | Timeout::~Timeout() 58 | { 59 | Stop(); 60 | } 61 | 62 | void 63 | Timeout::SetTimeout(uint64_t ms) 64 | { 65 | ASSERT(!Active()); 66 | this->ms = ms; 67 | } 68 | 69 | uint64_t 70 | Timeout::Start() 71 | { 72 | return this->Reset(); 73 | } 74 | 75 | 76 | uint64_t 77 | Timeout::Reset() 78 | { 79 | Stop(); 80 | 81 | timerId = transport->Timer(ms, [this]() { 82 | timerId = 0; 83 | Reset(); 84 | cb(); 85 | }); 86 | 87 | return ms; 88 | } 89 | 90 | void 91 | Timeout::Stop() 92 | { 93 | if (timerId > 0) { 94 | transport->CancelTimer(timerId); 95 | timerId = 0; 96 | } 97 | } 98 | 99 | bool 100 | Timeout::Active() const 101 | { 102 | return (timerId != 0); 103 | } 104 | -------------------------------------------------------------------------------- /lib/transport.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * transport.h: 5 | * message-passing network interface definition 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _LIB_TRANSPORT_H_ 32 | #define _LIB_TRANSPORT_H_ 33 | 34 | #include "lib/configuration.h" 35 | 36 | #include 37 | #include 38 | 39 | class TransportAddress 40 | { 41 | public: 42 | virtual ~TransportAddress() { } 43 | virtual TransportAddress *clone() const = 0; 44 | }; 45 | 46 | class TransportReceiver 47 | { 48 | protected: 49 | typedef ::google::protobuf::Message Message; 50 | 51 | public: 52 | virtual ~TransportReceiver(); 53 | virtual void SetAddress(const TransportAddress *addr); 54 | virtual const TransportAddress& GetAddress(); 55 | 56 | virtual void ReceiveMessage(const TransportAddress &remote, 57 | const string &type, const string &data) = 0; 58 | 59 | 60 | protected: 61 | const TransportAddress *myAddress; 62 | }; 63 | 64 | typedef std::function timer_callback_t; 65 | 66 | class Transport 67 | { 68 | protected: 69 | typedef ::google::protobuf::Message Message; 70 | public: 71 | virtual ~Transport() {} 72 | virtual void Register(TransportReceiver *receiver, 73 | const specpaxos::Configuration &config, 74 | int replicaIdx) = 0; 75 | virtual bool SendMessage(TransportReceiver *src, const TransportAddress &dst, 76 | const Message &m) = 0; 77 | virtual bool SendMessageToReplica(TransportReceiver *src, int replicaIdx, const Message &m) = 0; 78 | virtual bool SendMessageToAll(TransportReceiver *src, const Message &m) = 0; 79 | virtual int Timer(uint64_t ms, timer_callback_t cb) = 0; 80 | virtual bool CancelTimer(int id) = 0; 81 | virtual void CancelAllTimers() = 0; 82 | }; 83 | 84 | class Timeout 85 | { 86 | public: 87 | Timeout(Transport *transport, uint64_t ms, timer_callback_t cb); 88 | virtual ~Timeout(); 89 | virtual void SetTimeout(uint64_t ms); 90 | virtual uint64_t Start(); 91 | virtual uint64_t Reset(); 92 | virtual void Stop(); 93 | virtual bool Active() const; 94 | 95 | private: 96 | Transport *transport; 97 | uint64_t ms; 98 | timer_callback_t cb; 99 | int timerId; 100 | }; 101 | 102 | #endif // _LIB_TRANSPORT_H_ 103 | -------------------------------------------------------------------------------- /lib/udptransport.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * udptransport.h: 5 | * message-passing network interface that uses UDP message delivery 6 | * and libasync 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _LIB_UDPTRANSPORT_H_ 33 | #define _LIB_UDPTRANSPORT_H_ 34 | 35 | #include "lib/configuration.h" 36 | #include "lib/transport.h" 37 | #include "lib/transportcommon.h" 38 | 39 | #include 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | class UDPTransportAddress : public TransportAddress 49 | { 50 | public: 51 | UDPTransportAddress * clone() const; 52 | private: 53 | UDPTransportAddress(const sockaddr_in &addr); 54 | sockaddr_in addr; 55 | friend class UDPTransport; 56 | friend bool operator==(const UDPTransportAddress &a, 57 | const UDPTransportAddress &b); 58 | friend bool operator!=(const UDPTransportAddress &a, 59 | const UDPTransportAddress &b); 60 | friend bool operator<(const UDPTransportAddress &a, 61 | const UDPTransportAddress &b); 62 | }; 63 | 64 | class UDPTransport : public TransportCommon 65 | { 66 | public: 67 | UDPTransport(double dropRate = 0.0, double reorderRate = 0.0, 68 | int dscp = 0, event_base *evbase = nullptr); 69 | virtual ~UDPTransport(); 70 | void Register(TransportReceiver *receiver, 71 | const specpaxos::Configuration &config, 72 | int replicaIdx); 73 | void Run(); 74 | int Timer(uint64_t ms, timer_callback_t cb); 75 | bool CancelTimer(int id); 76 | void CancelAllTimers(); 77 | 78 | private: 79 | struct UDPTransportTimerInfo 80 | { 81 | UDPTransport *transport; 82 | timer_callback_t cb; 83 | event *ev; 84 | int id; 85 | }; 86 | 87 | double dropRate; 88 | double reorderRate; 89 | std::uniform_real_distribution uniformDist; 90 | std::default_random_engine randomEngine; 91 | struct 92 | { 93 | bool valid; 94 | UDPTransportAddress *addr; 95 | string msgType; 96 | string message; 97 | int fd; 98 | } reorderBuffer; 99 | int dscp; 100 | 101 | event_base *libeventBase; 102 | std::vector listenerEvents; 103 | std::vector signalEvents; 104 | std::map receivers; // fd -> receiver 105 | std::map fds; // receiver -> fd 106 | std::map multicastFds; 107 | std::map multicastConfigs; 108 | int lastTimerId; 109 | std::map timers; 110 | uint64_t lastFragMsgId; 111 | struct UDPTransportFragInfo 112 | { 113 | uint64_t msgId; 114 | string data; 115 | }; 116 | std::map fragInfo; 117 | 118 | bool SendMessageInternal(TransportReceiver *src, 119 | const UDPTransportAddress &dst, 120 | const Message &m, bool multicast = false); 121 | UDPTransportAddress 122 | LookupAddress(const specpaxos::ReplicaAddress &addr); 123 | UDPTransportAddress 124 | LookupAddress(const specpaxos::Configuration &cfg, 125 | int replicaIdx); 126 | const UDPTransportAddress * 127 | LookupMulticastAddress(const specpaxos::Configuration *cfg); 128 | void ListenOnMulticastPort(const specpaxos::Configuration 129 | *canonicalConfig); 130 | void OnReadable(int fd); 131 | void OnTimer(UDPTransportTimerInfo *info); 132 | static void SocketCallback(evutil_socket_t fd, 133 | short what, void *arg); 134 | static void TimerCallback(evutil_socket_t fd, 135 | short what, void *arg); 136 | static void LogCallback(int severity, const char *msg); 137 | static void FatalCallback(int err); 138 | static void SignalCallback(evutil_socket_t fd, 139 | short what, void *arg); 140 | }; 141 | 142 | #endif // _LIB_UDPTRANSPORT_H_ 143 | -------------------------------------------------------------------------------- /lib/viewstamp.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * viewstamp.h: 5 | * definition of types and utility functions for viewstamps and 6 | * related types 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _LIB_VIEWSTAMP_H_ 33 | #define _LIB_VIEWSTAMP_H_ 34 | 35 | #define __STDC_FORMAT_MACROS 36 | #include 37 | 38 | typedef uint64_t view_t; 39 | typedef uint64_t opnum_t; 40 | 41 | struct viewstamp_t 42 | { 43 | view_t view; 44 | opnum_t opnum; 45 | 46 | viewstamp_t() : view(0), opnum(0) {} 47 | viewstamp_t(view_t view, opnum_t opnum) : view(view), opnum(opnum) {} 48 | }; 49 | 50 | #define FMT_VIEW "%" PRIu64 51 | #define FMT_OPNUM "%" PRIu64 52 | 53 | #define FMT_VIEWSTAMP "<" FMT_VIEW "," FMT_OPNUM ">" 54 | #define VA_VIEWSTAMP(x) x.view, x.opnum 55 | 56 | static inline int 57 | Viewstamp_Compare(viewstamp_t a, viewstamp_t b) 58 | { 59 | if (a.view < b.view) return -1; 60 | if (a.view > b.view) return 1; 61 | if (a.opnum < b.opnum) return -1; 62 | if (a.opnum > b.opnum) return 1; 63 | return 0; 64 | } 65 | 66 | inline bool operator==(const viewstamp_t& lhs, const viewstamp_t& rhs){ return Viewstamp_Compare(lhs,rhs) == 0; } 67 | inline bool operator!=(const viewstamp_t& lhs, const viewstamp_t& rhs){return !operator==(lhs,rhs);} 68 | inline bool operator< (const viewstamp_t& lhs, const viewstamp_t& rhs){ return Viewstamp_Compare(lhs,rhs) < 0; } 69 | inline bool operator> (const viewstamp_t& lhs, const viewstamp_t& rhs){return operator< (rhs,lhs);} 70 | inline bool operator<=(const viewstamp_t& lhs, const viewstamp_t& rhs){return !operator> (lhs,rhs);} 71 | inline bool operator>=(const viewstamp_t& lhs, const viewstamp_t& rhs){return !operator< (lhs,rhs);} 72 | 73 | #endif /* _LIB_VIEWSTAMP_H_ */ 74 | -------------------------------------------------------------------------------- /nistore/.gitignore: -------------------------------------------------------------------------------- 1 | benchClient 2 | replica 3 | -------------------------------------------------------------------------------- /nistore/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | SRCS += $(addprefix $(d), \ 4 | client.cc txnstore.cc server.cc \ 5 | lockstore.cc lockserver.cc kvstore.cc \ 6 | benchClient.cc versionedKVStore.cc occstore.cc) 7 | 8 | PROTOS += $(addprefix $(d), \ 9 | request.proto) 10 | 11 | LIB-kvstore := $(o)kvstore.o 12 | 13 | LIB-stores := $(o)lockserver.o $(o)versionedKVStore.o 14 | 15 | OBJS-ni-store := $(o)server.o $(o)txnstore.o $(o)lockstore.o $(o)occstore.o 16 | 17 | OBJS-ni-client := $(o)request.o $(o)client.o \ 18 | $(OBJS-spec-client) $(OBJS-vr-client) $(OBJS-fastpaxos-client) $(LIB-udptransport) 19 | 20 | $(d)benchClient: $(OBJS-ni-client) $(o)benchClient.o 21 | 22 | $(d)replica: $(o)request.o $(OBJS-ni-store) $(LIB-kvstore) $(LIB-stores) \ 23 | $(OBJS-spec-replica) $(OBJS-vr-replica) $(OBJS-fastpaxos-replica) $(LIB-udptransport) 24 | 25 | BINS += $(d)benchClient $(d)replica 26 | -------------------------------------------------------------------------------- /nistore/client.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/client.h: 6 | * NiStore client-side logic and APIs 7 | * 8 | **********************************************************************/ 9 | 10 | #ifndef _NI_CLIENT_H_ 11 | #define _NI_CLIENT_H_ 12 | 13 | #include "lib/assert.h" 14 | #include "lib/message.h" 15 | #include "lib/udptransport.h" 16 | #include "common/client.h" 17 | #include "lib/configuration.h" 18 | #include "spec/client.h" 19 | #include "vr/client.h" 20 | #include "fastpaxos/client.h" 21 | #include "nistore/request.pb.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace nistore { 32 | 33 | using namespace std; 34 | 35 | enum Proto { 36 | PROTO_UNKNOWN, 37 | PROTO_VR, 38 | PROTO_SPEC, 39 | PROTO_FAST 40 | }; 41 | 42 | class Client 43 | { 44 | public: 45 | /* Constructor needs path to shard configs and number of shards. */ 46 | Client(Proto mode, string configPath, int nshards); 47 | ~Client(); 48 | 49 | /* API Calls for NiStore. */ 50 | void Begin(); 51 | bool Get(const string &key, string &value); 52 | void Put(const string &key, const string &value); 53 | bool Commit(); 54 | void Abort(); 55 | 56 | private: 57 | long client_id; // Unique ID for this client. 58 | long nshards; // Number of shards in niStore 59 | 60 | UDPTransport transport; // Transport used by paxos client proxies. 61 | thread *clientTransport; // Thread running the transport event loop. 62 | 63 | vector shard; // List of shard client proxies. 64 | specpaxos::Client *tss; // Timestamp server shard. 65 | 66 | mutex cv_m; // Synchronize access to all state in this class and cv. 67 | condition_variable cv; // To block api calls till a replica reply. 68 | 69 | /* Transaction specific variables. */ 70 | bool op_pending; // True if a transaction is ongoing. 71 | bool status; // Whether to commit transaction & reply status. 72 | unsigned int nreplies; // Number of replies received back in 2PC. 73 | set all_participants; // Participants in ongoing transaction. 74 | set yes_participants; // Participants who replies YES. 75 | string replica_reply; // Reply back from a shard. 76 | 77 | /* Private helper functions. */ 78 | void run_client(); // Runs the transport event loop. 79 | void send_begin(unsigned int); // Sends BEGIN message to shard[i]. 80 | 81 | /* Callbacks for hearing back from a shard for an operation. */ 82 | void beginCallback(const int, const string &, const string &); 83 | void getCallback(const int, const string &, const string &); 84 | void putCallback(const int, const string &, const string &); 85 | void prepareCallback(const int, const string &, const string &); 86 | void commitCallback(const int, const string &, const string &); 87 | void abortCallback(const int, const string &, const string &); 88 | 89 | void tssCallback(const string &request, const string &reply); 90 | 91 | // Sharding logic: Given key, generates a number b/w 0 to nshards-1 92 | long key_to_shard(const string &key); 93 | }; 94 | 95 | } // namespace nistore 96 | 97 | #endif /* _NI_CLIENT_H_ */ 98 | -------------------------------------------------------------------------------- /nistore/kvstore.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/kvstore.cc: 6 | * Simple versioned key-value store 7 | * 8 | **********************************************************************/ 9 | 10 | #include "nistore/kvstore.h" 11 | #include "lib/assert.h" 12 | #include "lib/message.h" 13 | 14 | namespace nistore { 15 | using namespace std; 16 | 17 | KVStore::KVStore() { } 18 | 19 | KVStore::~KVStore() { } 20 | 21 | 22 | bool 23 | KVStore::get(const string &key, string &value) 24 | { 25 | // check for existence of key in store 26 | if (store.find(key) == store.end() || store[key].empty()) { 27 | return false; 28 | } else { 29 | value = store[key].back(); 30 | return true; 31 | } 32 | } 33 | 34 | 35 | bool 36 | KVStore::put(const string &key, const string &value) 37 | { 38 | store[key].push_back(value); 39 | return true; 40 | } 41 | 42 | /* Delete the latest version of this key. */ 43 | bool 44 | KVStore::remove(const string &key, string &value) 45 | { 46 | if (store.find(key) == store.end() || store[key].empty()) { 47 | return false; 48 | } 49 | 50 | store[key].pop_back(); 51 | return true; 52 | } 53 | 54 | } // namespace nistore 55 | -------------------------------------------------------------------------------- /nistore/kvstore.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/kvstore.h: 6 | * Simple historied key-value store 7 | * 8 | **********************************************************************/ 9 | 10 | #ifndef _KV_STORE_H_ 11 | #define _KV_STORE_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace nistore { 22 | using namespace std; 23 | 24 | class KVStore 25 | { 26 | 27 | public: 28 | KVStore(); 29 | ~KVStore(); 30 | 31 | bool get(const string &key, string &value); 32 | bool put(const string &key, const string &value); 33 | bool remove(const string &key, string &value); 34 | 35 | private: 36 | /* Global store which keep key -> (timestamp, value) list. */ 37 | unordered_map> store; 38 | }; 39 | 40 | } // namespace nistore 41 | 42 | #endif /* _KV_STORE_H_ */ 43 | -------------------------------------------------------------------------------- /nistore/lockserver.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/lockserver.h: 6 | * Simple multi-reader, single-writer lock server 7 | * 8 | **********************************************************************/ 9 | 10 | #ifndef _LOCK_SERVER_H_ 11 | #define _LOCK_SERVER_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace nistore { 25 | using namespace std; 26 | 27 | #define LOCK_WAIT_TIMEOUT 5000 28 | 29 | class LockServer 30 | { 31 | 32 | public: 33 | LockServer(); 34 | ~LockServer(); 35 | 36 | bool lockForRead(const string &lock, uint64_t requester); 37 | bool lockForWrite(const string &lock, uint64_t requester); 38 | void releaseForRead(const string &lock, uint64_t holder); 39 | void releaseForWrite(const string &lock, uint64_t holder); 40 | 41 | private: 42 | enum LockState { 43 | UNLOCKED, 44 | LOCKED_FOR_READ, 45 | LOCKED_FOR_WRITE, 46 | LOCKED_FOR_READ_WRITE 47 | }; 48 | 49 | struct Waiter { 50 | bool write; 51 | struct timeval waitTime; 52 | 53 | Waiter() {write = false;} 54 | Waiter(bool w) { 55 | gettimeofday(&waitTime, NULL); 56 | write = w; 57 | } 58 | 59 | bool checkTimeout(const struct timeval &now); 60 | }; 61 | 62 | struct Lock { 63 | LockState state; 64 | unordered_set holders; 65 | queue waitQ; 66 | map waiters; 67 | 68 | Lock() { 69 | state = UNLOCKED; 70 | }; 71 | void waitForLock(uint64_t requester, bool write); 72 | bool tryAcquireLock(uint64_t requester, bool write); 73 | bool isWriteNext(); 74 | }; 75 | 76 | 77 | /* Global store which keep key -> (timestamp, value) list. */ 78 | unordered_map locks; 79 | 80 | uint64_t readers; 81 | uint64_t writers; 82 | }; 83 | 84 | } // namespace nistore 85 | 86 | #endif /* _LOCK_SERVER_H_ */ 87 | -------------------------------------------------------------------------------- /nistore/lockstore.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/lockstore.h 6 | * Key-value store with support for transactions using S2PL 7 | * 8 | **********************************************************************/ 9 | 10 | #ifndef _NI_LOCK_STORE_H_ 11 | #define _NI_LOCK_STORE_H_ 12 | 13 | #include "nistore/kvstore.h" 14 | #include "nistore/lockserver.h" 15 | #include "nistore/txnstore.h" 16 | #include "lib/viewstamp.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace nistore { 24 | 25 | using namespace std; 26 | 27 | class LockStore : public TxnStore 28 | { 29 | public: 30 | LockStore(); 31 | ~LockStore(); 32 | 33 | // begin a transaction 34 | virtual void begin(uint64_t id); 35 | // add key to read set 36 | virtual int get(uint64_t id, const string &key, string &value); 37 | // add key to write set 38 | virtual int put(uint64_t id, const string &key, const string &value); 39 | // check whether we can commit or abort this transaction 40 | // and lock the read/write set 41 | virtual int prepare(uint64_t id, opnum_t op); 42 | // commit the transaction 43 | virtual void commit(uint64_t id, uint64_t timestamp, opnum_t op); 44 | // abort a running transaction 45 | virtual void abortTxn(uint64_t id, opnum_t op); 46 | 47 | // undo operations from Spec Paxos 48 | virtual void unbegin(uint64_t id); 49 | virtual void unget(uint64_t id, const string &key); 50 | virtual void unput(uint64_t id, const string &key, const string &value); 51 | virtual void unprepare(uint64_t id, opnum_t op); 52 | virtual void uncommit(uint64_t id, uint64_t timestamp, opnum_t op); 53 | virtual void unabort(uint64_t id, opnum_t op); 54 | 55 | // upcall from Spec Paxos to clean up 56 | virtual void specCommit(opnum_t op); 57 | 58 | private: 59 | // data store 60 | KVStore store; 61 | // locks 62 | LockServer locks; 63 | 64 | // Currently active transaction 65 | // may be running or prepared 66 | struct Transaction { 67 | uint64_t id; 68 | // list of keys read and number of times each has been read 69 | map readSet; 70 | // map between key and value(s) 71 | map> writeSet; 72 | 73 | Transaction() : id(0) { }; 74 | Transaction(uint64_t i) : id(i) { }; 75 | 76 | bool operator== (const Transaction &t); 77 | }; 78 | 79 | enum RetiredState { 80 | COMMITTED, 81 | ABORTED_PREPARED, 82 | ABORTED_RUNNING 83 | }; 84 | 85 | struct RetiredTxn { 86 | Transaction txn; 87 | RetiredState state; 88 | 89 | RetiredTxn(Transaction t, RetiredState s) : 90 | txn(t), state(s) { }; 91 | }; 92 | 93 | map running; 94 | map prepared; 95 | list> retired; 96 | 97 | Transaction& getTxn(uint64_t id); 98 | Transaction getRetiredTxn(opnum_t op, uint64_t id, RetiredState state); 99 | void dropLocks(const Transaction &txn); 100 | void getLocks(const Transaction &txn); 101 | }; 102 | 103 | } // namespace nistore 104 | 105 | #endif /* _NI_LOCK_STORE_H_ */ 106 | -------------------------------------------------------------------------------- /nistore/occstore.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/occstore.h: 6 | * Key-value store with support for transactions using OCC 7 | * 8 | **********************************************************************/ 9 | 10 | #ifndef _NI_OCC_STORE_H_ 11 | #define _NI_OCC_STORE_H_ 12 | 13 | #include "nistore/versionedKVStore.h" 14 | #include "nistore/txnstore.h" 15 | #include "lib/viewstamp.h" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace nistore { 23 | 24 | using namespace std; 25 | 26 | class OCCStore : public TxnStore 27 | { 28 | public: 29 | OCCStore(); 30 | ~OCCStore(); 31 | 32 | // begin a transaction 33 | virtual void begin(uint64_t id); 34 | // add key to read set 35 | virtual int get(uint64_t id, const string &key, string &value); 36 | // add key to write set 37 | virtual int put(uint64_t id, const string &key, const string &value); 38 | // check whether we can commit or abort this transaction 39 | // and lock the read/write set 40 | virtual int prepare(uint64_t id, opnum_t op); 41 | // commit the transaction 42 | virtual void commit(uint64_t id, uint64_t timestamp, opnum_t op); 43 | // abort a running transaction 44 | virtual void abortTxn(uint64_t id, opnum_t op); 45 | 46 | // undo operations from Spec Paxos 47 | virtual void unbegin(uint64_t id); 48 | virtual void unget(uint64_t id, const string &key); 49 | virtual void unput(uint64_t id, const string &key, const string &value); 50 | virtual void unprepare(uint64_t id, opnum_t op); 51 | virtual void uncommit(uint64_t id, uint64_t timestamp, opnum_t op); 52 | virtual void unabort(uint64_t id, opnum_t op); 53 | 54 | // upcall from Spec Paxos to clean up 55 | virtual void specCommit(opnum_t op); 56 | 57 | private: 58 | // data store 59 | VersionedKVStore store; 60 | 61 | // Currently active transaction 62 | // may be running or prepared 63 | struct Transaction { 64 | uint64_t id; 65 | // map between key and timestamp at 66 | // which the read happened and how 67 | // many times this key has been read 68 | map> readSet; 69 | // map between key and value(s) 70 | map> writeSet; 71 | 72 | Transaction() : id(0) { }; 73 | Transaction(uint64_t i) : id(i) { }; 74 | 75 | bool operator== (const Transaction &t); 76 | }; 77 | 78 | enum RetiredState { 79 | COMMITTED, 80 | ABORTED_PREPARED, 81 | ABORTED_RUNNING 82 | }; 83 | 84 | struct RetiredTxn { 85 | Transaction txn; 86 | RetiredState state; 87 | 88 | RetiredTxn(Transaction t, RetiredState s) : 89 | txn(t), state(s) { }; 90 | }; 91 | 92 | map running; 93 | map prepared; 94 | list> retired; 95 | 96 | set getPreparedWrites(); 97 | set getPreparedReadWrites(); 98 | Transaction& getTxn(uint64_t id); 99 | Transaction getRetiredTxn(opnum_t op, uint64_t id, RetiredState state); 100 | }; 101 | 102 | } // namespace nistore 103 | 104 | #endif /* _NI_OCC_STORE_H_ */ 105 | -------------------------------------------------------------------------------- /nistore/request.proto: -------------------------------------------------------------------------------- 1 | package nistore; 2 | 3 | message Request { 4 | enum Operation { 5 | BEGIN = 1; 6 | GET = 2; 7 | PUT = 3; 8 | PREPARE = 4; 9 | COMMIT = 5; 10 | ABORT = 6; 11 | } 12 | 13 | required Operation op = 1; 14 | required uint64 txnid = 2; 15 | optional string arg0 = 3; 16 | optional string arg1 = 4; 17 | } 18 | 19 | message Reply { 20 | // 0 = OK 21 | // -1 = failed 22 | // -2 = retry 23 | // -3 = abstain/no reply 24 | required int32 status = 1; 25 | optional string value = 2; 26 | } 27 | -------------------------------------------------------------------------------- /nistore/server.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/server.h: 6 | * NiStore application server logic 7 | * 8 | **********************************************************************/ 9 | 10 | #ifndef _NI_SERVER_H_ 11 | #define _NI_SERVER_H_ 12 | 13 | #include "lib/configuration.h" 14 | #include "common/replica.h" 15 | #include "lib/udptransport.h" 16 | #include "spec/replica.h" 17 | #include "vr/replica.h" 18 | #include "fastpaxos/replica.h" 19 | #include "nistore/lockstore.h" 20 | #include "nistore/occstore.h" 21 | #include 22 | 23 | namespace nistore { 24 | 25 | using namespace std; 26 | 27 | class Server : public specpaxos::AppReplica 28 | { 29 | public: 30 | // set up the store 31 | Server() {store = OCCStore(); }; 32 | Server(bool locking) {locking ? store = LockStore() : OCCStore();}; 33 | ~Server() { }; 34 | void ReplicaUpcall(opnum_t opnum, const string &str1, string &str2); 35 | void RollbackUpcall(opnum_t current, opnum_t to, const std::map &opMap); 36 | void CommitUpcall(opnum_t opnum); 37 | 38 | private: 39 | // data store 40 | TxnStore store; 41 | 42 | struct Operation 43 | { 44 | long id; // client ID 45 | string op; // requested operation 46 | std::vector args; // arguments 47 | }; 48 | 49 | Operation parse(string str); 50 | vector split(string str); 51 | }; 52 | 53 | } // namespace nistore 54 | 55 | #endif /* _NI_SERVER_H_ */ 56 | -------------------------------------------------------------------------------- /nistore/shard.tss.config: -------------------------------------------------------------------------------- 1 | f 1 2 | replica localhost:51733 3 | replica localhost:51734 4 | replica localhost:51735 5 | -------------------------------------------------------------------------------- /nistore/shard0.config: -------------------------------------------------------------------------------- 1 | f 1 2 | replica localhost:51729 3 | replica localhost:51730 4 | replica localhost:51731 5 | -------------------------------------------------------------------------------- /nistore/txnstore.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/txnstore.cc: 6 | * Transactional Key-value store interface 7 | * 8 | **********************************************************************/ 9 | #include "nistore/txnstore.h" 10 | #include "lib/assert.h" 11 | #include "lib/message.h" 12 | 13 | namespace nistore { 14 | 15 | using namespace std; 16 | 17 | TxnStore::TxnStore() { } 18 | TxnStore::~TxnStore() { } 19 | 20 | void 21 | TxnStore::begin(uint64_t id) 22 | { 23 | Debug("[%" PRIu64 "] BEGIN", id); 24 | } 25 | 26 | void 27 | TxnStore::unbegin(uint64_t id) 28 | { 29 | Debug("[%" PRIu64 "] UNDO BEGIN", id); 30 | } 31 | 32 | int 33 | TxnStore::get(uint64_t id, const string &key, string &value) 34 | { 35 | Debug("[%" PRIu64 "] GET %s", id, key.c_str()); 36 | return 0; 37 | } 38 | 39 | void 40 | TxnStore::unget(uint64_t id, const string &key) 41 | { 42 | Debug("[%" PRIu64 "] UNDO GET %s", id, key.c_str()); 43 | } 44 | 45 | int 46 | TxnStore::put(uint64_t id, const string &key, const string &value) 47 | { 48 | Debug("[%" PRIu64 "] PUT %s %s", id, key.c_str(), value.c_str()); 49 | return 0; 50 | } 51 | 52 | void 53 | TxnStore::unput(uint64_t id, const string &key, const string &value) 54 | { 55 | Debug("[%" PRIu64 "] UNDO PUT %s %s", id, key.c_str(), value.c_str()); 56 | } 57 | 58 | int 59 | TxnStore::prepare(uint64_t id, opnum_t op) 60 | { 61 | Debug("[%" PRIu64 "] START PREPARE", id); 62 | return 0; 63 | } 64 | 65 | void 66 | TxnStore::unprepare(uint64_t id, opnum_t op) 67 | { 68 | Debug("[%" PRIu64 "] UNDO PREPARE", id); 69 | } 70 | 71 | void 72 | TxnStore::commit(uint64_t id, uint64_t timestamp, opnum_t op) 73 | { 74 | Debug("[%" PRIu64 "] COMMIT", id); 75 | } 76 | 77 | void 78 | TxnStore::uncommit(uint64_t id, uint64_t timestamp, opnum_t op) 79 | { 80 | // do some uncommit stuff 81 | Debug("[%" PRIu64 "] UNDO COMMIT", id); 82 | 83 | } 84 | 85 | void 86 | TxnStore::abortTxn(uint64_t id, opnum_t op) 87 | { 88 | Debug("[%" PRIu64 "] ABORT", id); 89 | } 90 | 91 | void 92 | TxnStore::unabort(uint64_t id, opnum_t op) 93 | { 94 | Debug("[%" PRIu64 "] UNDO ABORT", id); 95 | } 96 | 97 | void 98 | TxnStore::specCommit(opnum_t op) 99 | { 100 | Debug("SPEC COMMIT"); 101 | } 102 | 103 | } // namespace nistore 104 | -------------------------------------------------------------------------------- /nistore/txnstore.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/txnstore.h: 6 | * Interface for a single node transactional store serving as a 7 | * server-side backend for NiStore 8 | * 9 | **********************************************************************/ 10 | 11 | #ifndef _TXN_STORE_H_ 12 | #define _TXN_STORE_H_ 13 | 14 | #include "lib/viewstamp.h" 15 | #include 16 | 17 | namespace nistore { 18 | 19 | using namespace std; 20 | 21 | class TxnStore 22 | { 23 | public: 24 | 25 | TxnStore(); 26 | ~TxnStore(); 27 | 28 | // begin a transaction 29 | virtual void begin(uint64_t id); 30 | // add key to read set 31 | virtual int get(uint64_t id, const string &key, string &value); 32 | // add key to write set 33 | virtual int put(uint64_t id, const string &key, const string &value); 34 | // check whether we can commit or abort this transaction 35 | // and lock the read/write set 36 | virtual int prepare(uint64_t id, opnum_t op); 37 | // commit the transaction 38 | virtual void commit(uint64_t id, uint64_t timestamp, opnum_t op); 39 | // abort a running transaction 40 | virtual void abortTxn(uint64_t id, opnum_t op); 41 | 42 | // undo operations from Spec Paxos 43 | virtual void unbegin(uint64_t id); 44 | virtual void unget(uint64_t id, const string &key); 45 | virtual void unput(uint64_t id, const string &key, const string &value); 46 | virtual void unprepare(uint64_t id, opnum_t op); 47 | virtual void uncommit(uint64_t id, uint64_t timestamp, opnum_t op); 48 | virtual void unabort(uint64_t id, opnum_t op); 49 | 50 | // upcall from Spec Paxos to clean up 51 | virtual void specCommit(opnum_t op); 52 | }; 53 | 54 | } // namespace nistore 55 | 56 | #endif /* _TXN_STORE_H_ */ 57 | -------------------------------------------------------------------------------- /nistore/versionedKVStore.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/versionedKVStore.cc: 6 | * Simple versioned key-value store 7 | * 8 | **********************************************************************/ 9 | 10 | #include "nistore/versionedKVStore.h" 11 | #include "lib/assert.h" 12 | #include "lib/message.h" 13 | 14 | namespace nistore { 15 | using namespace std; 16 | 17 | VersionedKVStore::VersionedKVStore() { } 18 | 19 | VersionedKVStore::~VersionedKVStore() { } 20 | 21 | 22 | /* Returns the most recent value and timestamp for given key. 23 | * Error if key does not exist. */ 24 | bool 25 | VersionedKVStore::get(const string &key, pair &value) 26 | { 27 | // check for existence of key in store 28 | if (store.find(key) == store.end()) { 29 | return false; 30 | } else { 31 | value = store[key].front(); 32 | return true; 33 | } 34 | } 35 | 36 | /* Returns the value valid at given timestamp. 37 | * Error if key did not exist at the timestamp. */ 38 | bool 39 | VersionedKVStore::get(const string &key, uint64_t timestamp, string &value) 40 | { 41 | // check for existence of key in store 42 | if (store.find(key) == store.end()) { 43 | return false; 44 | } else { 45 | list >::iterator it = store[key].begin(); 46 | while (it != store[key].end()) { 47 | if ( timestamp >= (*it).first ) { 48 | value = (*it).second; 49 | return true; 50 | } 51 | it++; 52 | } 53 | } 54 | return false; 55 | } 56 | 57 | bool 58 | VersionedKVStore::put(const string &key, const string &value, uint64_t timestamp) 59 | { 60 | // Key does not exist. Create a list and an entry. 61 | if (store.find(key) == store.end()) { 62 | list > l; 63 | l.push_front(make_pair(timestamp, value)); 64 | store[key] = l; 65 | return true; 66 | } 67 | 68 | // Key exists, add it to list of values if newer timestamp. 69 | if (timestamp > store[key].front().first) { 70 | store[key].push_front(make_pair(timestamp, value)); 71 | return true; 72 | } else { 73 | // newer version exists, insert older version 74 | list >::iterator it = store[key].begin(); 75 | while (it != store[key].end()) { 76 | if ( timestamp > (*it).first ) { 77 | store[key].insert(it, make_pair(timestamp, value)); 78 | return true; 79 | } 80 | it++; 81 | } 82 | return true; 83 | } 84 | 85 | return false; 86 | } 87 | 88 | /* Delete the latest version of this key. */ 89 | bool 90 | VersionedKVStore::remove(const string &key, pair &value) 91 | { 92 | if (store.find(key) == store.end()) { 93 | return false; 94 | } 95 | 96 | value = store[key].front(); 97 | store[key].pop_front(); 98 | return true; 99 | } 100 | 101 | } // namespace nistore 102 | -------------------------------------------------------------------------------- /nistore/versionedKVStore.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | // vim: set ts=4 sw=4: 3 | /*********************************************************************** 4 | * 5 | * nistore/versionedKVStore.h: 6 | * Simple versioned key-value store 7 | * 8 | **********************************************************************/ 9 | 10 | #ifndef _NI_VERSIONED_KV_STORE_H_ 11 | #define _NI_VERSIONED_KV_STORE_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace nistore { 22 | using namespace std; 23 | 24 | class VersionedKVStore 25 | { 26 | 27 | public: 28 | VersionedKVStore(); 29 | ~VersionedKVStore(); 30 | 31 | bool get(const string &key, pair &value); 32 | bool get(const string &key, uint64_t timestamp, string &value); 33 | bool put(const string &key, const string &value, uint64_t timestamp); 34 | bool remove(const string &key, pair &value); 35 | 36 | private: 37 | /* Global store which keep key -> (timestamp, value) list. */ 38 | unordered_map > > store; 39 | }; 40 | 41 | } // namespace nistore 42 | 43 | #endif /* _NI_VERSIONED_KV_STORE_H_ */ 44 | -------------------------------------------------------------------------------- /spec/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | SRCS += $(addprefix $(d), \ 4 | replica.cc client.cc) 5 | 6 | PROTOS += $(addprefix $(d), \ 7 | spec-proto.proto) 8 | 9 | OBJS-spec-client := $(o)client.o $(o)spec-proto.o \ 10 | $(OBJS-client) $(LIB-message) \ 11 | $(LIB-configuration) 12 | 13 | OBJS-spec-replica := $(o)replica.o $(o)spec-proto.o \ 14 | $(OBJS-replica) $(LIB-message) \ 15 | $(LIB-configuration) $(LIB-latency) 16 | 17 | include $(d)tests/Rules.mk 18 | 19 | -------------------------------------------------------------------------------- /spec/client.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * spec/client.h: 5 | * Speculative Paxos client 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _SPEC_CLIENT_H_ 32 | #define _SPEC_CLIENT_H_ 33 | 34 | #include "common/client.h" 35 | #include "lib/configuration.h" 36 | #include "common/quorumset.h" 37 | #include "spec/spec-proto.pb.h" 38 | 39 | namespace specpaxos { 40 | namespace spec { 41 | 42 | class SpecClient : public Client 43 | { 44 | public: 45 | SpecClient(const Configuration &config, 46 | Transport *transport, 47 | uint64_t clientid = 0); 48 | virtual ~SpecClient(); 49 | virtual void Invoke(const string &request, 50 | continuation_t continuation); 51 | virtual void InvokeUnlogged(int replicaIdx, 52 | const string &request, 53 | continuation_t continuation, 54 | timeout_continuation_t timeoutContinuation = nullptr, 55 | uint32_t timeout = DEFAULT_UNLOGGED_OP_TIMEOUT); 56 | virtual void ReceiveMessage(const TransportAddress &remote, 57 | const string &type, const string &data); 58 | 59 | protected: 60 | view_t view; 61 | opnum_t opnum; 62 | uint64_t lastReqId; 63 | 64 | struct PendingRequest 65 | { 66 | string request; 67 | uint64_t clientReqId; 68 | continuation_t continuation; 69 | timeout_continuation_t timeoutContinuation; 70 | inline PendingRequest(string request, uint64_t clientReqId, 71 | continuation_t continuation) 72 | : request(request), clientReqId(clientReqId), 73 | continuation(continuation) { } 74 | }; 75 | PendingRequest *pendingRequest; 76 | PendingRequest *pendingUnloggedRequest; 77 | Timeout *requestTimeout; 78 | Timeout *unloggedRequestTimeout; 79 | QuorumSet speculativeReplyQuorum; 80 | 81 | void SendRequest(); 82 | void ResendRequest(); 83 | void CompleteOperation(const proto::SpeculativeReplyMessage &msg); 84 | void HandleReply(const TransportAddress &remote, 85 | const proto::SpeculativeReplyMessage &msg); 86 | void HandleUnloggedReply(const TransportAddress &remote, 87 | const proto::UnloggedReplyMessage &msg); 88 | void UnloggedRequestTimeoutCallback(); 89 | }; 90 | 91 | } // namespace specpaxos::spec 92 | } // namespace specpaxos 93 | 94 | #endif /* _SPEC_CLIENT_H_ */ 95 | -------------------------------------------------------------------------------- /spec/replica.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * spec/replica.h: 5 | * Speculative Paxos protocol 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _SPEC_REPLICA_H_ 32 | #define _SPEC_REPLICA_H_ 33 | 34 | #include "lib/configuration.h" 35 | #include "lib/latency.h" 36 | #include "common/log.h" 37 | #include "common/replica.h" 38 | #include "common/quorumset.h" 39 | #include "spec/spec-proto.pb.h" 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | class LogMergeTest; 48 | 49 | namespace specpaxos { 50 | namespace spec { 51 | 52 | #define FMT_CLIENTREQID "%" PRIu64 53 | 54 | class SpecReplica : public Replica 55 | { 56 | public: 57 | SpecReplica(Configuration config, int myIdx, bool initialize, 58 | Transport *transport, AppReplica *app); 59 | ~SpecReplica(); 60 | 61 | void ReceiveMessage(const TransportAddress &remote, 62 | const string &type, const string &data); 63 | 64 | public: // XXX public for unit testing 65 | Log log; 66 | private: 67 | view_t view; 68 | opnum_t lastCommitted; 69 | opnum_t lastCommittedSent; 70 | opnum_t lastSpeculative; 71 | opnum_t pendingSync; 72 | opnum_t lastSync; 73 | view_t sentDoViewChange; 74 | view_t needFillDVC; 75 | std::map > clientAddresses; 76 | struct ClientTableEntry 77 | { 78 | uint64_t lastReqId; 79 | // We need the opnum to identify the correct entry in the 80 | // log. What we really want is the SpeculativeReplyMessage 81 | // corresponding to the last request, but we need to stuff 82 | // that in the log instead of keeping it here -- in order to 83 | // keep this up to date even if we roll back the log. 84 | opnum_t lastReqOpnum; 85 | }; 86 | std::map clientTable; 87 | std::list > pendingRequests; 89 | 90 | QuorumSet syncReplyQuorum; 91 | QuorumSet startViewChangeQuorum; 92 | QuorumSet doViewChangeQuorum; 93 | QuorumSet inViewQuorum; 94 | 95 | Timeout *syncTimeout; 96 | Timeout *failedSyncTimeout; 97 | Timeout *viewChangeTimeout; 98 | 99 | Latency_t reconciliationLatency; 100 | Latency_t mergeLatency; 101 | Latency_t requestLatency; 102 | 103 | bool AmLeader() const; 104 | void SendSync(); 105 | void CommitUpTo(opnum_t upto); 106 | void RollbackTo(opnum_t backto); 107 | void UpdateClientTable(const Request &req, 108 | LogEntry &entry, 109 | const proto::SpeculativeReplyMessage &reply); 110 | void EnterView(view_t newview); 111 | void StartViewChange(view_t newview); 112 | void MergeLogs(view_t newView, opnum_t maxStart, 113 | const std::map &dvcs, 114 | std::vector &out); 115 | void InstallLog(const std::vector &entries); 116 | void SendFillDVCGapMessage(int replicaIdx, view_t view); 117 | void NeedFillDVCGap(view_t view); 118 | void SendSyncReply(opnum_t opnum); 119 | 120 | void HandleRequest(const TransportAddress &remote, 121 | const proto::RequestMessage &msg); 122 | void HandleUnloggedRequest(const TransportAddress &remote, 123 | const proto::UnloggedRequestMessage &msg); 124 | void HandleSync(const TransportAddress &remote, 125 | const proto::SyncMessage &msg); 126 | void HandleSyncReply(const TransportAddress &remote, 127 | const proto::SyncReplyMessage &msg); 128 | void HandleRequestViewChange(const TransportAddress &remote, 129 | const proto::RequestViewChangeMessage &msg); 130 | void HandleStartViewChange(const TransportAddress &remote, 131 | const proto::StartViewChangeMessage &msg); 132 | void HandleDoViewChange(const TransportAddress &remote, 133 | const proto::DoViewChangeMessage &msg); 134 | void HandleStartView(const TransportAddress &remote, 135 | const proto::StartViewMessage &msg); 136 | void HandleInView(const TransportAddress &remote, 137 | const proto::InViewMessage &msg); 138 | void HandleFillLogGap(const TransportAddress &remote, 139 | const proto::FillLogGapMessage &msg); 140 | void HandleFillDVCGap(const TransportAddress &remote, 141 | const proto::FillDVCGapMessage &msg); 142 | 143 | friend class ::LogMergeTest; 144 | }; 145 | 146 | 147 | } // namespace specpaxos::spec 148 | } // namespace specpaxos 149 | 150 | #endif /* _SPEC_REPLICA_H_ */ 151 | -------------------------------------------------------------------------------- /spec/spec-proto.proto: -------------------------------------------------------------------------------- 1 | import "common/request.proto"; 2 | 3 | package specpaxos.spec.proto; 4 | 5 | message RequestMessage { 6 | required specpaxos.Request req = 1; 7 | } 8 | 9 | message SpeculativeReplyMessage { 10 | required uint64 clientreqid = 1; 11 | required uint32 replicaidx = 2; 12 | required uint64 view = 3; 13 | required uint64 opnum = 4; 14 | required bytes loghash = 5; 15 | required bytes reply = 6; 16 | required bool committed = 7; 17 | } 18 | 19 | message UnloggedRequestMessage { 20 | required specpaxos.UnloggedRequest req = 1; 21 | } 22 | 23 | message UnloggedReplyMessage { 24 | required bytes reply = 1; 25 | } 26 | 27 | message SyncMessage { 28 | required uint64 view = 1; 29 | optional uint64 lastCommitted = 2; 30 | optional bytes lastCommittedHash = 3; 31 | optional uint64 lastSpeculative = 4; 32 | } 33 | 34 | message SyncReplyMessage { 35 | required uint64 view = 1; 36 | required uint64 lastSpeculative = 2; 37 | required bytes lastSpeculativeHash = 3; 38 | required uint32 replicaidx = 4; 39 | } 40 | 41 | // This is from a client to server. 42 | message RequestViewChangeMessage { 43 | // Note that this is the view the client saw an operation fail in, 44 | // not the desired new view. (It's not really the client's place 45 | // to specify what the new view should be!) 46 | required uint64 view = 1; 47 | } 48 | 49 | message StartViewChangeMessage { 50 | required uint64 view = 1; 51 | required uint32 replicaIdx = 2; 52 | required uint64 lastCommitted = 3; 53 | } 54 | 55 | message DoViewChangeMessage { 56 | message LogEntry { 57 | required uint64 view = 1; 58 | required uint64 opnum = 2; 59 | required specpaxos.Request request = 3; 60 | required uint32 state = 4; 61 | optional bytes hash = 5; 62 | } 63 | required uint64 view = 1; 64 | required uint64 lastNormalView = 2; 65 | required uint64 lastSpeculative = 3; 66 | required uint64 lastCommitted = 4; 67 | repeated LogEntry entries = 5; 68 | required uint32 replicaIdx = 6; 69 | } 70 | 71 | message StartViewMessage { 72 | message LogEntry { 73 | required uint64 view = 1; 74 | required uint64 opnum = 2; 75 | required specpaxos.Request request = 3; 76 | required uint32 state = 4; 77 | required bytes hash = 5; 78 | } 79 | required uint64 view = 1; 80 | required uint64 lastSpeculative = 2; 81 | required uint64 lastCommitted = 3; 82 | repeated LogEntry entries = 4; 83 | } 84 | 85 | message InViewMessage { 86 | required uint64 view = 1; 87 | required uint64 lastSpeculative = 2; 88 | required uint32 replicaIdx = 3; 89 | } 90 | 91 | message FillLogGapMessage { 92 | required uint64 view = 1; 93 | required uint64 lastCommitted = 2; 94 | } 95 | 96 | message FillDVCGapMessage { 97 | required uint64 view = 1; 98 | required uint64 lastCommitted = 2; 99 | } 100 | -------------------------------------------------------------------------------- /spec/tests/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | GTEST_SRCS += $(d)spec-test.cc $(d)merge-test.cc 4 | PROTOS += $(d)merge-test-case.proto 5 | 6 | $(d)spec-test: $(o)spec-test.o \ 7 | $(OBJS-spec-replica) $(OBJS-spec-client) \ 8 | $(LIB-simtransport) \ 9 | $(GTEST_MAIN) 10 | 11 | $(d)merge-test: $(o)merge-test.o $(o)merge-test-case.o \ 12 | $(OBJS-spec-replica) $(OBJS-spec-client) \ 13 | $(LIB-simtransport) \ 14 | $(GTEST_MAIN) 15 | 16 | TEST_BINS += $(d)merge-test $(d)spec-test 17 | -------------------------------------------------------------------------------- /spec/tests/merge-test-case.proto: -------------------------------------------------------------------------------- 1 | package specpaxos.spec.test; 2 | 3 | message TestLogEntry 4 | { 5 | required uint64 view = 1; 6 | required uint64 opnum = 2; 7 | required string id = 3; 8 | required bool spec = 4; 9 | } 10 | 11 | message TestLog 12 | { 13 | required uint32 replicaidx = 1; 14 | required uint64 view = 2; 15 | required uint64 lastNormalView = 3; 16 | repeated TestLogEntry entries = 4; 17 | } 18 | 19 | message MergeTestCase 20 | { 21 | required uint64 newview = 1; 22 | repeated TestLog log = 2; 23 | repeated TestLogEntry expected = 3; 24 | } 25 | -------------------------------------------------------------------------------- /spec/tests/merge-tests/AllCommitted: -------------------------------------------------------------------------------- 1 | newview: 2 2 | log: { 3 | replicaidx: 0 4 | view: 2 5 | lastNormalView: 1 6 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 7 | entries: {view: 0; opnum: 2; id: "1/2"; spec: f} 8 | } 9 | 10 | log: { 11 | replicaidx: 1 12 | view: 2 13 | lastNormalView: 1 14 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 15 | entries: {view: 0; opnum: 2; id: "1/2"; spec: f} 16 | } 17 | 18 | expected: {view: 0; opnum: 1; id: "1/1"; spec: f} 19 | expected: {view: 0; opnum: 2; id: "1/2"; spec: f} 20 | -------------------------------------------------------------------------------- /spec/tests/merge-tests/Conflict: -------------------------------------------------------------------------------- 1 | newview: 2 2 | log: { 3 | replicaidx: 0 4 | view: 2 5 | lastNormalView: 1 6 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 7 | entries: {view: 1; opnum: 2; id: "2/1"; spec: t} 8 | } 9 | 10 | log: { 11 | replicaidx: 1 12 | view: 2 13 | lastNormalView: 1 14 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 15 | entries: {view: 1; opnum: 2; id: "1/2"; spec: t} 16 | } 17 | 18 | expected: {view: 0; opnum: 1; id: "1/1"; spec: f} 19 | expected: {view: 2; opnum: 2; id: "2/1"; spec: t} 20 | expected: {view: 2; opnum: 3; id: "1/2"; spec: t} 21 | -------------------------------------------------------------------------------- /spec/tests/merge-tests/Divergence: -------------------------------------------------------------------------------- 1 | newview: 2 2 | log: { 3 | replicaidx: 0 4 | view: 2 5 | lastNormalView: 1 6 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 7 | entries: {view: 1; opnum: 2; id: "2/1"; spec: t} 8 | entries: {view: 1; opnum: 3; id: "1/2"; spec: t} 9 | entries: {view: 1; opnum: 4; id: "3/1"; spec: t} 10 | } 11 | 12 | log: { 13 | replicaidx: 1 14 | view: 2 15 | lastNormalView: 1 16 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 17 | entries: {view: 1; opnum: 2; id: "1/2"; spec: t} 18 | entries: {view: 1; opnum: 3; id: "2/1"; spec: t} 19 | entries: {view: 1; opnum: 4; id: "3/1"; spec: t} 20 | } 21 | 22 | expected: {view: 0; opnum: 1; id: "1/1"; spec: f} 23 | expected: {view: 2; opnum: 2; id: "2/1"; spec: t} 24 | expected: {view: 2; opnum: 3; id: "1/2"; spec: t} 25 | expected: {view: 2; opnum: 4; id: "3/1"; spec: t} 26 | -------------------------------------------------------------------------------- /spec/tests/merge-tests/EmptyLogs: -------------------------------------------------------------------------------- 1 | newview: 1 2 | log: { 3 | replicaidx: 0 4 | view: 0 5 | lastNormalView: 0 6 | } 7 | 8 | log: { 9 | replicaidx: 1 10 | view: 0 11 | lastNormalView: 0 12 | } 13 | -------------------------------------------------------------------------------- /spec/tests/merge-tests/OneEmpty: -------------------------------------------------------------------------------- 1 | newview: 1 2 | log: { 3 | replicaidx: 0 4 | view: 0 5 | lastNormalView: 0 6 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 7 | entries: {view: 0; opnum: 2; id: "1/2"; spec: f} 8 | } 9 | 10 | log: { 11 | replicaidx: 1 12 | view: 0 13 | lastNormalView: 0 14 | } 15 | 16 | expected: {view: 0; opnum: 1; id: "1/1"; spec: f} 17 | expected: {view: 0; opnum: 2; id: "1/2"; spec: f} 18 | -------------------------------------------------------------------------------- /spec/tests/merge-tests/SpeculativeNoQuorum: -------------------------------------------------------------------------------- 1 | newview: 2 2 | log: { 3 | replicaidx: 0 4 | view: 2 5 | lastNormalView: 1 6 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 7 | } 8 | 9 | log: { 10 | replicaidx: 1 11 | view: 2 12 | lastNormalView: 1 13 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 14 | entries: {view: 1; opnum: 2; id: "1/2"; spec: t} 15 | } 16 | 17 | expected: {view: 0; opnum: 1; id: "1/1"; spec: f} 18 | expected: {view: 2; opnum: 2; id: "1/2"; spec: t} 19 | -------------------------------------------------------------------------------- /spec/tests/merge-tests/SpeculativeQuorum: -------------------------------------------------------------------------------- 1 | newview: 2 2 | log: { 3 | replicaidx: 0 4 | view: 2 5 | lastNormalView: 1 6 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 7 | entries: {view: 1; opnum: 2; id: "1/2"; spec: t} 8 | } 9 | 10 | log: { 11 | replicaidx: 1 12 | view: 2 13 | lastNormalView: 1 14 | entries: {view: 0; opnum: 1; id: "1/1"; spec: f} 15 | entries: {view: 1; opnum: 2; id: "1/2"; spec: t} 16 | } 17 | 18 | expected: {view: 0; opnum: 1; id: "1/1"; spec: f} 19 | expected: {view: 1; opnum: 2; id: "1/2"; spec: f} 20 | -------------------------------------------------------------------------------- /timeserver/.gitignore: -------------------------------------------------------------------------------- 1 | replica 2 | -------------------------------------------------------------------------------- /timeserver/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | SRCS += $(addprefix $(d), timeserver.cc) 4 | 5 | $(d)replica: $(o)timeserver.o $(OBJS-fastpaxos-replica) \ 6 | $(OBJS-spec-replica) $(OBJS-vr-replica) $(LIB-udptransport) 7 | 8 | BINS += $(d)replica 9 | -------------------------------------------------------------------------------- /timeserver/timeserver.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * timeserver/timeserver.cc: 5 | * Single TimeStamp Server. 6 | * 7 | **********************************************************************/ 8 | 9 | #include "timeserver/timeserver.h" 10 | 11 | TimeStampServer::TimeStampServer() 12 | { 13 | ts = 0; 14 | } 15 | 16 | TimeStampServer::~TimeStampServer() { } 17 | 18 | string 19 | TimeStampServer::newTimeStamp() 20 | { 21 | ts++; 22 | return to_string(ts); 23 | } 24 | 25 | void 26 | TimeStampServer::ReplicaUpcall(opnum_t opnum, 27 | const string &str1, 28 | string &str2) 29 | { 30 | Debug("Received Upcall: " FMT_OPNUM ", %s", opnum, str1.c_str()); 31 | // Get a new timestamp from the TimeStampServer 32 | str2 = newTimeStamp(); 33 | } 34 | 35 | /* Ignore for now, will be used when running specpaxos. */ 36 | void 37 | TimeStampServer::RollbackUpcall(opnum_t current, 38 | opnum_t to, 39 | const std::map &opMap) 40 | { 41 | Debug("Received Rollback Upcall: " FMT_OPNUM ", " FMT_OPNUM, current, to); 42 | for (auto op : opMap) { 43 | ts--; 44 | } 45 | } 46 | 47 | 48 | /* Ignore for now, will be used when running specpaxos. */ 49 | void 50 | TimeStampServer::CommitUpcall(opnum_t commitOpnum) 51 | { 52 | Debug("Received Commit Upcall: " FMT_OPNUM, commitOpnum); 53 | } 54 | 55 | static void Usage(const char *progName) 56 | { 57 | fprintf(stderr, "usage: %s -c conf-file -i replica-index\n", 58 | progName); 59 | exit(1); 60 | } 61 | 62 | int 63 | main(int argc, char **argv) 64 | { 65 | int index = -1; 66 | const char *configPath = NULL; 67 | enum { 68 | PROTO_UNKNOWN, 69 | PROTO_VR, 70 | PROTO_SPEC, 71 | PROTO_FAST 72 | } proto = PROTO_UNKNOWN; 73 | 74 | // Parse arguments 75 | int opt; 76 | while ((opt = getopt(argc, argv, "c:i:m:")) != -1) { 77 | switch (opt) { 78 | case 'c': 79 | configPath = optarg; 80 | break; 81 | 82 | case 'i': 83 | { 84 | char *strtolPtr; 85 | index = strtoul(optarg, &strtolPtr, 10); 86 | if ((*optarg == '\0') || (*strtolPtr != '\0') || (index < 0)) 87 | { 88 | fprintf(stderr, 89 | "option -i requires a numeric arg\n"); 90 | Usage(argv[0]); 91 | } 92 | break; 93 | } 94 | 95 | case 'm': 96 | { 97 | if (strcasecmp(optarg, "vr") == 0) { 98 | proto = PROTO_VR; 99 | } else if (strcasecmp(optarg, "spec") == 0) { 100 | proto = PROTO_SPEC; 101 | } else if (strcasecmp(optarg, "fast") == 0) { 102 | proto = PROTO_FAST; 103 | } else { 104 | proto = PROTO_VR; 105 | } 106 | break; 107 | } 108 | 109 | default: 110 | fprintf(stderr, "Unknown argument %s\n", argv[optind]); 111 | break; 112 | } 113 | } 114 | 115 | if (!configPath) { 116 | fprintf(stderr, "option -c is required\n"); 117 | Usage(argv[0]); 118 | } 119 | 120 | if (index == -1) { 121 | fprintf(stderr, "option -i is required\n"); 122 | Usage(argv[0]); 123 | } 124 | 125 | if (proto == PROTO_UNKNOWN) { 126 | fprintf(stderr, "option -i is required\n"); 127 | Usage(argv[0]); 128 | } 129 | 130 | // Load configuration 131 | std::ifstream configStream(configPath); 132 | if (configStream.fail()) { 133 | fprintf(stderr, "unable to read configuration file: %s\n", 134 | configPath); 135 | Usage(argv[0]); 136 | } 137 | specpaxos::Configuration config(configStream); 138 | 139 | if (index >= config.n) { 140 | fprintf(stderr, "replica index %d is out of bounds; " 141 | "only %d replicas defined\n", index, config.n); 142 | Usage(argv[0]); 143 | } 144 | 145 | UDPTransport transport(0.0, 0.0, 0); 146 | 147 | specpaxos::Replica *replica; 148 | TimeStampServer server; 149 | 150 | switch (proto) { 151 | case PROTO_VR: 152 | replica = new specpaxos::vr::VRReplica(config, index, true, &transport, 1, &server); 153 | break; 154 | 155 | case PROTO_SPEC: 156 | replica = new specpaxos::spec::SpecReplica( 157 | config, index, true, &transport, &server); 158 | break; 159 | 160 | case PROTO_FAST: 161 | replica = new specpaxos::fastpaxos::FastPaxosReplica(config, index, true, &transport, &server); 162 | break; 163 | 164 | default: 165 | NOT_REACHABLE(); 166 | } 167 | 168 | (void)replica; // silence warning 169 | transport.Run(); 170 | 171 | return 0; 172 | } 173 | -------------------------------------------------------------------------------- /timeserver/timeserver.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * timeserver/timeserver.h: 5 | * Timeserver API 6 | * 7 | **********************************************************************/ 8 | 9 | #ifndef _TIME_SERVER_H_ 10 | #define _TIME_SERVER_H_ 11 | 12 | #include "lib/configuration.h" 13 | #include "common/replica.h" 14 | #include "lib/udptransport.h" 15 | #include "spec/replica.h" 16 | #include "vr/replica.h" 17 | #include "fastpaxos/replica.h" 18 | 19 | #include 20 | 21 | using namespace std; 22 | 23 | class TimeStampServer : public specpaxos::AppReplica 24 | { 25 | public: 26 | TimeStampServer(); 27 | ~TimeStampServer(); 28 | 29 | void ReplicaUpcall(opnum_t opnum, const string &str1, string &str2); 30 | void RollbackUpcall(opnum_t current, opnum_t to, const std::map &opMap); 31 | void CommitUpcall(opnum_t op); 32 | private: 33 | long ts; 34 | string newTimeStamp(); 35 | 36 | }; 37 | #endif /* _TIME_SERVER_H_ */ 38 | -------------------------------------------------------------------------------- /unreplicated/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | SRCS += $(addprefix $(d), \ 4 | replica.cc client.cc) 5 | 6 | PROTOS += $(addprefix $(d), \ 7 | unreplicated-proto.proto) 8 | 9 | OBJS-unreplicated-client := $(o)client.o $(o)unreplicated-proto.o \ 10 | $(OBJS-client) $(LIB-message) \ 11 | $(LIB-configuration) 12 | 13 | OBJS-unreplicated-replica := $(o)replica.o $(o)unreplicated-proto.o \ 14 | $(OBJS-replica) $(LIB-message) \ 15 | $(LIB-configuration) 16 | 17 | include $(d)tests/Rules.mk 18 | 19 | -------------------------------------------------------------------------------- /unreplicated/client.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * unreplicated/client.cc: 5 | * dummy unreplicated client 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #include "common/client.h" 32 | #include "common/request.pb.h" 33 | #include "lib/message.h" 34 | #include "lib/transport.h" 35 | #include "unreplicated/client.h" 36 | #include "unreplicated/unreplicated-proto.pb.h" 37 | 38 | namespace specpaxos { 39 | namespace unreplicated { 40 | 41 | UnreplicatedClient::UnreplicatedClient(const Configuration &config, 42 | Transport *transport, 43 | uint64_t clientid) 44 | : Client(config, transport, clientid) 45 | { 46 | pendingRequest = NULL; 47 | pendingUnloggedRequest = NULL; 48 | } 49 | 50 | UnreplicatedClient::~UnreplicatedClient() 51 | { 52 | if (pendingRequest) { 53 | delete pendingRequest; 54 | } 55 | if (pendingUnloggedRequest) { 56 | delete pendingUnloggedRequest; 57 | } 58 | } 59 | 60 | void 61 | UnreplicatedClient::Invoke(const string &request, 62 | continuation_t continuation) 63 | { 64 | // XXX Can only handle one pending request for now 65 | if (pendingRequest != NULL) { 66 | Panic("Client only supports one pending request"); 67 | } 68 | 69 | pendingRequest = new PendingRequest(request, continuation); 70 | 71 | proto::RequestMessage reqMsg; 72 | reqMsg.mutable_req()->set_op(pendingRequest->request); 73 | reqMsg.mutable_req()->set_clientid(clientid); 74 | reqMsg.mutable_req()->set_clientreqid(0); 75 | 76 | // Unreplicated: just send to replica 0 77 | transport->SendMessageToReplica(this, 0, reqMsg); 78 | 79 | } 80 | 81 | void 82 | UnreplicatedClient::InvokeUnlogged(int replicaIdx, 83 | const string &request, 84 | continuation_t continuation, 85 | timeout_continuation_t timeoutContinuation, 86 | uint32_t timeout) 87 | { 88 | // XXX Can only handle one pending request for now 89 | if (pendingUnloggedRequest != NULL) { 90 | Panic("Client only supports one pending request"); 91 | } 92 | 93 | pendingUnloggedRequest = new PendingRequest(request, continuation); 94 | 95 | proto::UnloggedRequestMessage reqMsg; 96 | reqMsg.mutable_req()->set_op(pendingUnloggedRequest->request); 97 | reqMsg.mutable_req()->set_clientid(clientid); 98 | reqMsg.mutable_req()->set_clientreqid(0); 99 | 100 | // Unreplicated: just send to replica 0 101 | if (replicaIdx != 0) { 102 | Panic("Attempt to invoke unlogged operation on replica that doesn't exist"); 103 | } 104 | transport->SendMessageToReplica(this, 0, reqMsg); 105 | 106 | } 107 | 108 | void 109 | UnreplicatedClient::ReceiveMessage(const TransportAddress &remote, 110 | const string &type, 111 | const string &data) 112 | { 113 | static proto::ReplyMessage reply; 114 | static proto::UnloggedReplyMessage unloggedReply; 115 | 116 | if (type == reply.GetTypeName()) { 117 | reply.ParseFromString(data); 118 | HandleReply(remote, reply); 119 | } else if (type == unloggedReply.GetTypeName()) { 120 | unloggedReply.ParseFromString(data); 121 | HandleUnloggedReply(remote, unloggedReply); 122 | } else { 123 | Client::ReceiveMessage(remote, type, data); 124 | } 125 | } 126 | 127 | void 128 | UnreplicatedClient::HandleReply(const TransportAddress &remote, 129 | const proto::ReplyMessage &msg) 130 | { 131 | if (pendingRequest == NULL) { 132 | Warning("Received reply when no request was pending"); 133 | } 134 | 135 | Debug("Client received reply"); 136 | 137 | PendingRequest *req = pendingRequest; 138 | pendingRequest = NULL; 139 | 140 | req->continuation(req->request, msg.reply()); 141 | delete req; 142 | } 143 | 144 | void 145 | UnreplicatedClient::HandleUnloggedReply(const TransportAddress &remote, 146 | const proto::UnloggedReplyMessage &msg) 147 | { 148 | if (pendingUnloggedRequest == NULL) { 149 | Warning("Received unloggedReply when no request was pending"); 150 | } 151 | 152 | Debug("Client received unloggedReply"); 153 | 154 | PendingRequest *req = pendingUnloggedRequest; 155 | pendingUnloggedRequest = NULL; 156 | 157 | req->continuation(req->request, msg.reply()); 158 | delete req; 159 | } 160 | 161 | } // namespace specpaxos::unreplicated 162 | } // namespace specpaxos 163 | -------------------------------------------------------------------------------- /unreplicated/client.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * unreplicated/client.h: 5 | * dummy implementation of replication interface that just uses a 6 | * single replica and passes commands directly to it 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _UNREPLICATED_CLIENT_H_ 33 | #define _UNREPLICATED_CLIENT_H_ 34 | 35 | #include "common/client.h" 36 | #include "lib/configuration.h" 37 | #include "unreplicated/unreplicated-proto.pb.h" 38 | 39 | namespace specpaxos { 40 | namespace unreplicated { 41 | 42 | class UnreplicatedClient : public Client 43 | { 44 | public: 45 | UnreplicatedClient(const Configuration &config, 46 | Transport *transport, 47 | uint64_t clientid = 0); 48 | virtual ~UnreplicatedClient(); 49 | virtual void Invoke(const string &request, continuation_t continuation); 50 | virtual void InvokeUnlogged(int replicaIdx, 51 | const string &request, 52 | continuation_t continuation, 53 | timeout_continuation_t timeoutContinuation = nullptr, 54 | uint32_t timeout = DEFAULT_UNLOGGED_OP_TIMEOUT); 55 | virtual void ReceiveMessage(const TransportAddress &remote, 56 | const string &type, const string &data); 57 | 58 | protected: 59 | struct PendingRequest 60 | { 61 | string request; 62 | continuation_t continuation; 63 | inline PendingRequest(string request, continuation_t continuation) 64 | : request(request), continuation(continuation) { } 65 | }; 66 | PendingRequest *pendingRequest; 67 | PendingRequest *pendingUnloggedRequest; 68 | 69 | void HandleReply(const TransportAddress &remote, 70 | const proto::ReplyMessage &msg); 71 | void HandleUnloggedReply(const TransportAddress &remote, 72 | const proto::UnloggedReplyMessage &msg); 73 | }; 74 | 75 | } // namespace specpaxos::unreplicated 76 | } // namespace specpaxos 77 | 78 | #endif /* _UNREPLICATED_CLIENT_H_ */ 79 | -------------------------------------------------------------------------------- /unreplicated/replica.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * unreplicated.cc: 5 | * dummy implementation of replication interface that just uses a 6 | * single replica and passes commands directly to it 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #include "common/replica.h" 33 | #include "unreplicated/replica.h" 34 | #include "unreplicated/unreplicated-proto.pb.h" 35 | 36 | #include "lib/message.h" 37 | #include "lib/transport.h" 38 | 39 | namespace specpaxos { 40 | namespace unreplicated { 41 | 42 | using namespace proto; 43 | 44 | void 45 | UnreplicatedReplica::HandleRequest(const TransportAddress &remote, 46 | const proto::RequestMessage &msg) 47 | { 48 | proto::ReplyMessage reply; 49 | 50 | Debug("Received request %s", (char *)msg.req().op().c_str()); 51 | 52 | Execute(0, msg.req(), reply); 53 | 54 | // The protocol defines these as required, even if they're not 55 | // meaningful. 56 | reply.set_view(0); 57 | reply.set_opnum(0); 58 | 59 | if (!(transport->SendMessage(this, remote, reply))) 60 | Warning("Failed to send reply message"); 61 | } 62 | 63 | void 64 | UnreplicatedReplica::HandleUnloggedRequest(const TransportAddress &remote, 65 | const proto::UnloggedRequestMessage &msg) 66 | { 67 | proto::UnloggedReplyMessage reply; 68 | 69 | Debug("Received unlogged request %s", (char *)msg.req().op().c_str()); 70 | 71 | ExecuteUnlogged(msg.req(), reply); 72 | 73 | if (!(transport->SendMessage(this, remote, reply))) 74 | Warning("Failed to send reply message"); 75 | } 76 | 77 | UnreplicatedReplica::UnreplicatedReplica(Configuration config, 78 | int myIdx, 79 | bool initialize, 80 | Transport *transport, 81 | AppReplica *app) 82 | : Replica(config, myIdx, initialize, transport, app) 83 | { 84 | if (!initialize) { 85 | Panic("Recovery does not make sense for unreplicated mode"); 86 | } 87 | 88 | this->status = STATUS_NORMAL; 89 | } 90 | 91 | void 92 | UnreplicatedReplica::ReceiveMessage(const TransportAddress &remote, 93 | const string &type, const string &data) 94 | { 95 | static proto::RequestMessage request; 96 | static proto::UnloggedRequestMessage unloggedRequest; 97 | 98 | if (type == request.GetTypeName()) { 99 | request.ParseFromString(data); 100 | HandleRequest(remote, request); 101 | } else if (type == unloggedRequest.GetTypeName()) { 102 | unloggedRequest.ParseFromString(data); 103 | HandleUnloggedRequest(remote, unloggedRequest); 104 | } else { 105 | Panic("Received unexpected message type in unreplicated proto: %s", 106 | type.c_str()); 107 | } 108 | } 109 | 110 | } // namespace specpaxos::unreplicated 111 | } // namespace specpaxos 112 | -------------------------------------------------------------------------------- /unreplicated/replica.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * unreplicated/replica.h: 5 | * dummy implementation of replication interface that just uses a 6 | * single replica and passes commands directly to it 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _UNREPLICATED_REPLICA_H_ 33 | #define _UNREPLICATED_REPLICA_H_ 34 | 35 | #include "common/replica.h" 36 | #include "unreplicated/unreplicated-proto.pb.h" 37 | 38 | namespace specpaxos { 39 | namespace unreplicated { 40 | 41 | class UnreplicatedReplica : public Replica 42 | { 43 | public: 44 | UnreplicatedReplica(Configuration config, int myIdx, 45 | bool initialize, 46 | Transport *transport, AppReplica *app); 47 | void ReceiveMessage(const TransportAddress &remote, 48 | const string &type, const string &data); 49 | 50 | private: 51 | void HandleRequest(const TransportAddress &remote, 52 | const proto::RequestMessage &msg); 53 | void HandleUnloggedRequest(const TransportAddress &remote, 54 | const proto::UnloggedRequestMessage &msg); 55 | }; 56 | 57 | } // namespace specpaxos::unreplicated 58 | } // namespace specpaxos 59 | 60 | #endif /* _UNREPLICATED_REPLICA_H_ */ 61 | -------------------------------------------------------------------------------- /unreplicated/tests/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | GTEST_SRCS += $(d)unreplicated-test.cc 4 | 5 | $(d)unreplicated-test: $(o)unreplicated-test.o \ 6 | $(OBJS-unreplicated-replica) $(OBJS-unreplicated-client) \ 7 | $(LIB-simtransport) \ 8 | $(GTEST_MAIN) 9 | 10 | TEST_BINS += $(d)unreplicated-test 11 | -------------------------------------------------------------------------------- /unreplicated/tests/unreplicated-test.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * unreplicated-test.cc: 5 | * test cases for unreplicated protocol 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #include "lib/configuration.h" 32 | #include "common/client.h" 33 | #include "common/replica.h" 34 | #include "lib/transport.h" 35 | #include "lib/simtransport.h" 36 | #include "unreplicated/client.h" 37 | #include "unreplicated/replica.h" 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | using namespace specpaxos; 44 | using namespace specpaxos::unreplicated; 45 | using namespace specpaxos::unreplicated::proto; 46 | 47 | static string replicaLastOp; 48 | static string clientLastOp; 49 | static string clientLastReply; 50 | static string replicaLastUnloggedOp; 51 | 52 | class UnrepTestApp : public AppReplica { 53 | public: 54 | UnrepTestApp() { }; 55 | ~UnrepTestApp() { }; 56 | 57 | void ReplicaUpcall(opnum_t opnum, const string &req, string &reply) { 58 | replicaLastOp = req; 59 | reply = "reply: " + req; 60 | } 61 | 62 | void UnloggedUpcall(const string &req, string &reply) { 63 | replicaLastUnloggedOp = req; 64 | reply = "unlreply: " + req; 65 | } 66 | }; 67 | 68 | static void ClientUpcallHandler(const string &req, const string &reply) 69 | { 70 | clientLastOp = req; 71 | clientLastReply = reply; 72 | } 73 | 74 | 75 | TEST(Unreplicated, OneOp) 76 | { 77 | std::vector replicaAddrs = 78 | { { "localhost", "12345" } }; 79 | Configuration c(1, 0, replicaAddrs); 80 | 81 | SimulatedTransport transport; 82 | 83 | UnrepTestApp app; 84 | 85 | UnreplicatedReplica replica(c, 0, true, &transport, &app); 86 | UnreplicatedClient client(c, &transport); 87 | 88 | client.Invoke(string("test"), ClientUpcallHandler); 89 | 90 | transport.Run(); 91 | 92 | EXPECT_EQ(replicaLastOp, "test"); 93 | EXPECT_EQ(clientLastOp, "test"); 94 | EXPECT_EQ(clientLastReply, "reply: test"); 95 | EXPECT_EQ(replicaLastUnloggedOp, ""); 96 | } 97 | 98 | TEST(Unreplicated, Unlogged) 99 | { 100 | std::vector replicaAddrs = 101 | { { "localhost", "12345" } }; 102 | Configuration c(1, 0, replicaAddrs); 103 | 104 | SimulatedTransport transport; 105 | UnrepTestApp app; 106 | 107 | UnreplicatedReplica replica(c, 0, true, &transport, &app); 108 | UnreplicatedClient client(c, &transport); 109 | 110 | client.InvokeUnlogged(0, string("test2"), ClientUpcallHandler); 111 | 112 | transport.Run(); 113 | 114 | EXPECT_EQ(replicaLastOp, "test"); 115 | EXPECT_EQ(replicaLastUnloggedOp, "test2"); 116 | EXPECT_EQ(clientLastOp, "test2"); 117 | EXPECT_EQ(clientLastReply, "unlreply: test2"); 118 | } 119 | -------------------------------------------------------------------------------- /unreplicated/unreplicated-proto.proto: -------------------------------------------------------------------------------- 1 | import "common/request.proto"; 2 | 3 | package specpaxos.unreplicated.proto; 4 | 5 | message RequestMessage { 6 | required specpaxos.Request req = 1; 7 | } 8 | 9 | message ReplyMessage { 10 | optional uint64 view = 1; 11 | optional uint64 opnum = 2; 12 | required bytes reply = 3; 13 | } 14 | 15 | message UnloggedRequestMessage { 16 | required specpaxos.UnloggedRequest req = 1; 17 | } 18 | 19 | message UnloggedReplyMessage { 20 | required bytes reply = 1; 21 | } 22 | -------------------------------------------------------------------------------- /vr/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | SRCS += $(addprefix $(d), \ 4 | replica.cc client.cc) 5 | 6 | PROTOS += $(addprefix $(d), \ 7 | vr-proto.proto) 8 | 9 | OBJS-vr-client := $(o)client.o $(o)vr-proto.o \ 10 | $(OBJS-client) $(LIB-message) \ 11 | $(LIB-configuration) 12 | 13 | OBJS-vr-replica := $(o)replica.o $(o)vr-proto.o \ 14 | $(OBJS-replica) $(LIB-message) \ 15 | $(LIB-configuration) $(LIB-latency) 16 | 17 | include $(d)tests/Rules.mk 18 | 19 | -------------------------------------------------------------------------------- /vr/client.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * vr/client.cc: 5 | * Viewstamped Replication clinet 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #include "common/client.h" 32 | #include "common/request.pb.h" 33 | #include "lib/assert.h" 34 | #include "lib/message.h" 35 | #include "lib/transport.h" 36 | #include "vr/client.h" 37 | #include "vr/vr-proto.pb.h" 38 | 39 | namespace specpaxos { 40 | namespace vr { 41 | 42 | VRClient::VRClient(const Configuration &config, 43 | Transport *transport, 44 | uint64_t clientid) 45 | : Client(config, transport, clientid) 46 | { 47 | pendingRequest = NULL; 48 | pendingUnloggedRequest = NULL; 49 | lastReqId = 0; 50 | 51 | requestTimeout = new Timeout(transport, 7000, [this]() { 52 | ResendRequest(); 53 | }); 54 | unloggedRequestTimeout = new Timeout(transport, 1000, [this]() { 55 | UnloggedRequestTimeoutCallback(); 56 | }); 57 | } 58 | 59 | VRClient::~VRClient() 60 | { 61 | if (pendingRequest) { 62 | delete pendingRequest; 63 | } 64 | if (pendingUnloggedRequest) { 65 | delete pendingUnloggedRequest; 66 | } 67 | delete requestTimeout; 68 | delete unloggedRequestTimeout; 69 | } 70 | 71 | void 72 | VRClient::Invoke(const string &request, 73 | continuation_t continuation) 74 | { 75 | // XXX Can only handle one pending request for now 76 | if (pendingRequest != NULL) { 77 | Panic("Client only supports one pending request"); 78 | } 79 | 80 | ++lastReqId; 81 | uint64_t reqId = lastReqId; 82 | pendingRequest = new PendingRequest(request, reqId, continuation); 83 | 84 | SendRequest(); 85 | } 86 | 87 | void 88 | VRClient::InvokeUnlogged(int replicaIdx, 89 | const string &request, 90 | continuation_t continuation, 91 | timeout_continuation_t timeoutContinuation, 92 | uint32_t timeout) 93 | { 94 | // XXX Can only handle one pending request for now 95 | if (pendingUnloggedRequest != NULL) { 96 | Panic("Client only supports one pending request"); 97 | } 98 | 99 | ++lastReqId; 100 | uint64_t reqId = lastReqId; 101 | 102 | pendingUnloggedRequest = new PendingRequest(request, reqId, continuation); 103 | pendingUnloggedRequest->timeoutContinuation = timeoutContinuation; 104 | 105 | proto::UnloggedRequestMessage reqMsg; 106 | reqMsg.mutable_req()->set_op(pendingUnloggedRequest->request); 107 | reqMsg.mutable_req()->set_clientid(clientid); 108 | reqMsg.mutable_req()->set_clientreqid(pendingUnloggedRequest->clientReqId); 109 | 110 | ASSERT(!unloggedRequestTimeout->Active()); 111 | unloggedRequestTimeout->SetTimeout(timeout); 112 | unloggedRequestTimeout->Start(); 113 | 114 | transport->SendMessageToReplica(this, replicaIdx, reqMsg); 115 | } 116 | 117 | void 118 | VRClient::SendRequest() 119 | { 120 | proto::RequestMessage reqMsg; 121 | reqMsg.mutable_req()->set_op(pendingRequest->request); 122 | reqMsg.mutable_req()->set_clientid(clientid); 123 | reqMsg.mutable_req()->set_clientreqid(pendingRequest->clientReqId); 124 | 125 | // XXX Try sending only to (what we think is) the leader first 126 | transport->SendMessageToAll(this, reqMsg); 127 | 128 | requestTimeout->Reset(); 129 | } 130 | 131 | void 132 | VRClient::ResendRequest() 133 | { 134 | Warning("Client timeout; resending request"); 135 | SendRequest(); 136 | } 137 | 138 | 139 | void 140 | VRClient::ReceiveMessage(const TransportAddress &remote, 141 | const string &type, 142 | const string &data) 143 | { 144 | static proto::ReplyMessage reply; 145 | static proto::UnloggedReplyMessage unloggedReply; 146 | 147 | if (type == reply.GetTypeName()) { 148 | reply.ParseFromString(data); 149 | HandleReply(remote, reply); 150 | } else if (type == unloggedReply.GetTypeName()) { 151 | unloggedReply.ParseFromString(data); 152 | HandleUnloggedReply(remote, unloggedReply); 153 | } else { 154 | Client::ReceiveMessage(remote, type, data); 155 | } 156 | } 157 | 158 | void 159 | VRClient::HandleReply(const TransportAddress &remote, 160 | const proto::ReplyMessage &msg) 161 | { 162 | if (pendingRequest == NULL) { 163 | Warning("Received reply when no request was pending"); 164 | return; 165 | } 166 | if (msg.clientreqid() != pendingRequest->clientReqId) { 167 | Warning("Received reply for a different request"); 168 | return; 169 | } 170 | 171 | Debug("Client received reply"); 172 | 173 | requestTimeout->Stop(); 174 | 175 | PendingRequest *req = pendingRequest; 176 | pendingRequest = NULL; 177 | 178 | req->continuation(req->request, msg.reply()); 179 | delete req; 180 | } 181 | 182 | void 183 | VRClient::HandleUnloggedReply(const TransportAddress &remote, 184 | const proto::UnloggedReplyMessage &msg) 185 | { 186 | if (pendingUnloggedRequest == NULL) { 187 | Warning("Received unloggedReply when no request was pending"); 188 | return; 189 | } 190 | 191 | Debug("Client received unloggedReply"); 192 | 193 | unloggedRequestTimeout->Stop(); 194 | 195 | PendingRequest *req = pendingUnloggedRequest; 196 | pendingUnloggedRequest = NULL; 197 | 198 | req->continuation(req->request, msg.reply()); 199 | delete req; 200 | } 201 | 202 | void 203 | VRClient::UnloggedRequestTimeoutCallback() 204 | { 205 | PendingRequest *req = pendingUnloggedRequest; 206 | pendingUnloggedRequest = NULL; 207 | 208 | Warning("Unlogged request timed out"); 209 | 210 | unloggedRequestTimeout->Stop(); 211 | 212 | req->timeoutContinuation(req->request); 213 | } 214 | 215 | } // namespace vr 216 | } // namespace specpaxos 217 | -------------------------------------------------------------------------------- /vr/client.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * vr/client.h: 5 | * dummy implementation of replication interface that just uses a 6 | * single replica and passes commands directly to it 7 | * 8 | * Copyright 2013-2016 Dan R. K. Ports 9 | * 10 | * Permission is hereby granted, free of charge, to any person 11 | * obtaining a copy of this software and associated documentation 12 | * files (the "Software"), to deal in the Software without 13 | * restriction, including without limitation the rights to use, copy, 14 | * modify, merge, publish, distribute, sublicense, and/or sell copies 15 | * of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | * 30 | **********************************************************************/ 31 | 32 | #ifndef _VR_CLIENT_H_ 33 | #define _VR_CLIENT_H_ 34 | 35 | #include "common/client.h" 36 | #include "lib/configuration.h" 37 | #include "vr/vr-proto.pb.h" 38 | 39 | namespace specpaxos { 40 | namespace vr { 41 | 42 | class VRClient : public Client 43 | { 44 | public: 45 | VRClient(const Configuration &config, 46 | Transport *transport, 47 | uint64_t clientid = 0); 48 | virtual ~VRClient(); 49 | virtual void Invoke(const string &request, 50 | continuation_t continuation); 51 | virtual void InvokeUnlogged(int replicaIdx, 52 | const string &request, 53 | continuation_t continuation, 54 | timeout_continuation_t timeoutContinuation = nullptr, 55 | uint32_t timeout = DEFAULT_UNLOGGED_OP_TIMEOUT); 56 | virtual void ReceiveMessage(const TransportAddress &remote, 57 | const string &type, const string &data); 58 | 59 | protected: 60 | int view; 61 | int opnumber; 62 | uint64_t lastReqId; 63 | 64 | struct PendingRequest 65 | { 66 | string request; 67 | uint64_t clientReqId; 68 | continuation_t continuation; 69 | timeout_continuation_t timeoutContinuation; 70 | inline PendingRequest(string request, uint64_t clientReqId, 71 | continuation_t continuation) 72 | : request(request), clientReqId(clientReqId), 73 | continuation(continuation) { } 74 | }; 75 | PendingRequest *pendingRequest; 76 | PendingRequest *pendingUnloggedRequest; 77 | Timeout *requestTimeout; 78 | Timeout *unloggedRequestTimeout; 79 | 80 | void SendRequest(); 81 | void ResendRequest(); 82 | void HandleReply(const TransportAddress &remote, 83 | const proto::ReplyMessage &msg); 84 | void HandleUnloggedReply(const TransportAddress &remote, 85 | const proto::UnloggedReplyMessage &msg); 86 | void UnloggedRequestTimeoutCallback(); 87 | }; 88 | 89 | } // namespace specpaxos::vr 90 | } // namespace specpaxos 91 | 92 | #endif /* _VR_CLIENT_H_ */ 93 | -------------------------------------------------------------------------------- /vr/replica.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-file-style: "k&r"; c-basic-offset: 4 -*- 2 | /*********************************************************************** 3 | * 4 | * vr/replica.h: 5 | * Viewstamped Replication protocol 6 | * 7 | * Copyright 2013-2016 Dan R. K. Ports 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without 12 | * restriction, including without limitation the rights to use, copy, 13 | * modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | * 29 | **********************************************************************/ 30 | 31 | #ifndef _VR_REPLICA_H_ 32 | #define _VR_REPLICA_H_ 33 | 34 | #include "lib/configuration.h" 35 | #include "lib/latency.h" 36 | #include "common/log.h" 37 | #include "common/replica.h" 38 | #include "common/quorumset.h" 39 | #include "vr/vr-proto.pb.h" 40 | 41 | #include 42 | #include 43 | #include 44 | 45 | namespace specpaxos { 46 | namespace vr { 47 | 48 | class VRReplica : public Replica 49 | { 50 | public: 51 | VRReplica(Configuration config, int myIdx, bool initialize, 52 | Transport *transport, int batchSize, 53 | AppReplica *app); 54 | ~VRReplica(); 55 | 56 | void ReceiveMessage(const TransportAddress &remote, 57 | const string &type, const string &data); 58 | 59 | private: 60 | view_t view; 61 | opnum_t lastCommitted; 62 | opnum_t lastOp; 63 | view_t lastRequestStateTransferView; 64 | opnum_t lastRequestStateTransferOpnum; 65 | uint64_t recoveryNonce; 66 | std::list > pendingPrepares; 68 | proto::PrepareMessage lastPrepare; 69 | int batchSize; 70 | opnum_t lastBatchEnd; 71 | bool batchComplete; 72 | 73 | Log log; 74 | std::map > clientAddresses; 75 | struct ClientTableEntry 76 | { 77 | uint64_t lastReqId; 78 | bool replied; 79 | proto::ReplyMessage reply; 80 | }; 81 | std::map clientTable; 82 | 83 | QuorumSet prepareOKQuorum; 84 | QuorumSet startViewChangeQuorum; 85 | QuorumSet doViewChangeQuorum; 86 | QuorumSet recoveryResponseQuorum; 87 | 88 | Timeout *viewChangeTimeout; 89 | Timeout *nullCommitTimeout; 90 | Timeout *stateTransferTimeout; 91 | Timeout *resendPrepareTimeout; 92 | Timeout *closeBatchTimeout; 93 | Timeout *recoveryTimeout; 94 | 95 | Latency_t requestLatency; 96 | Latency_t executeAndReplyLatency; 97 | 98 | uint64_t GenerateNonce() const; 99 | bool AmLeader() const; 100 | void CommitUpTo(opnum_t upto); 101 | void SendPrepareOKs(opnum_t oldLastOp); 102 | void SendRecoveryMessages(); 103 | void RequestStateTransfer(); 104 | void EnterView(view_t newview); 105 | void StartViewChange(view_t newview); 106 | void SendNullCommit(); 107 | void UpdateClientTable(const Request &req); 108 | void ResendPrepare(); 109 | void CloseBatch(); 110 | 111 | void HandleRequest(const TransportAddress &remote, 112 | const proto::RequestMessage &msg); 113 | void HandleUnloggedRequest(const TransportAddress &remote, 114 | const proto::UnloggedRequestMessage &msg); 115 | 116 | void HandlePrepare(const TransportAddress &remote, 117 | const proto::PrepareMessage &msg); 118 | void HandlePrepareOK(const TransportAddress &remote, 119 | const proto::PrepareOKMessage &msg); 120 | void HandleCommit(const TransportAddress &remote, 121 | const proto::CommitMessage &msg); 122 | void HandleRequestStateTransfer(const TransportAddress &remote, 123 | const proto::RequestStateTransferMessage &msg); 124 | void HandleStateTransfer(const TransportAddress &remote, 125 | const proto::StateTransferMessage &msg); 126 | void HandleStartViewChange(const TransportAddress &remote, 127 | const proto::StartViewChangeMessage &msg); 128 | void HandleDoViewChange(const TransportAddress &remote, 129 | const proto::DoViewChangeMessage &msg); 130 | void HandleStartView(const TransportAddress &remote, 131 | const proto::StartViewMessage &msg); 132 | void HandleRecovery(const TransportAddress &remote, 133 | const proto::RecoveryMessage &msg); 134 | void HandleRecoveryResponse(const TransportAddress &remote, 135 | const proto::RecoveryResponseMessage &msg); 136 | }; 137 | 138 | } // namespace specpaxos::vr 139 | } // namespace specpaxos 140 | 141 | #endif /* _VR_REPLICA_H_ */ 142 | -------------------------------------------------------------------------------- /vr/tests/Rules.mk: -------------------------------------------------------------------------------- 1 | d := $(dir $(lastword $(MAKEFILE_LIST))) 2 | 3 | GTEST_SRCS += $(d)vr-test.cc 4 | 5 | $(d)vr-test: $(o)vr-test.o \ 6 | $(OBJS-vr-replica) $(OBJS-vr-client) \ 7 | $(LIB-simtransport) \ 8 | $(GTEST_MAIN) 9 | 10 | TEST_BINS += $(d)vr-test 11 | -------------------------------------------------------------------------------- /vr/vr-proto.proto: -------------------------------------------------------------------------------- 1 | import "common/request.proto"; 2 | 3 | package specpaxos.vr.proto; 4 | 5 | message RequestMessage { 6 | required specpaxos.Request req = 1; 7 | } 8 | 9 | message ReplyMessage { 10 | required uint64 view = 1; 11 | required uint64 opnum = 2; 12 | required bytes reply = 3; 13 | required uint64 clientreqid = 4; 14 | } 15 | 16 | message UnloggedRequestMessage { 17 | required specpaxos.UnloggedRequest req = 1; 18 | } 19 | 20 | message UnloggedReplyMessage { 21 | required bytes reply = 1; 22 | } 23 | 24 | message PrepareMessage { 25 | required uint64 view = 1; 26 | required uint64 opnum = 2; 27 | required uint64 batchstart = 3; 28 | repeated Request request = 4; 29 | } 30 | 31 | message PrepareOKMessage { 32 | required uint64 view = 1; 33 | required uint64 opnum = 2; 34 | required uint32 replicaIdx = 3; 35 | } 36 | 37 | message CommitMessage { 38 | required uint64 view = 1; 39 | required uint64 opnum = 2; 40 | } 41 | 42 | message RequestStateTransferMessage { 43 | required uint64 view = 1; 44 | required uint64 opnum = 2; 45 | } 46 | 47 | message StateTransferMessage { 48 | message LogEntry { 49 | required uint64 view = 1; 50 | required uint64 opnum = 2; 51 | required specpaxos.Request request = 3; 52 | optional uint32 state = 4; 53 | optional bytes hash = 5; 54 | } 55 | required uint64 view = 1; 56 | required uint64 opnum = 2; 57 | repeated LogEntry entries = 3; 58 | } 59 | 60 | message StartViewChangeMessage { 61 | required uint64 view = 1; 62 | required uint32 replicaIdx = 2; 63 | required uint64 lastCommitted = 3; 64 | } 65 | 66 | message DoViewChangeMessage { 67 | message LogEntry { 68 | required uint64 view = 1; 69 | required uint64 opnum = 2; 70 | required specpaxos.Request request = 3; 71 | optional uint32 state = 4; 72 | optional bytes hash = 5; 73 | } 74 | required uint64 view = 1; 75 | required uint64 lastNormalView = 2; 76 | required uint64 lastOp = 3; 77 | required uint64 lastCommitted = 4; 78 | repeated LogEntry entries = 5; 79 | required uint32 replicaIdx = 6; 80 | } 81 | 82 | message StartViewMessage { 83 | message LogEntry { 84 | required uint64 view = 1; 85 | required uint64 opnum = 2; 86 | required specpaxos.Request request = 3; 87 | optional uint32 state = 4; 88 | optional bytes hash = 5; 89 | } 90 | required uint64 view = 1; 91 | required uint64 lastOp = 2; 92 | required uint64 lastCommitted = 3; 93 | repeated LogEntry entries = 4; 94 | } 95 | 96 | message RecoveryMessage { 97 | required uint32 replicaIdx = 1; 98 | required uint64 nonce = 2; 99 | } 100 | 101 | message RecoveryResponseMessage { 102 | message LogEntry { 103 | required uint64 view = 1; 104 | required uint64 opnum = 2; 105 | required specpaxos.Request request = 3; 106 | optional uint32 state = 4; 107 | optional bytes hash = 5; 108 | } 109 | required uint64 view = 1; 110 | required uint64 nonce = 2; 111 | repeated LogEntry entries = 3; 112 | optional uint64 lastOp = 4; 113 | optional uint64 lastCommitted = 5; 114 | required uint32 replicaIdx = 6; 115 | } 116 | --------------------------------------------------------------------------------