├── testcases ├── read.conf ├── create.conf ├── delete.conf └── update.conf ├── kvstore.png ├── MP2-specification-document.pdf ├── common.h ├── Readme.txt ├── Entry.h ├── Queue.h ├── Node.h ├── HashTable.h ├── Trace.h ├── stdincludes.h ├── Params.h ├── run.sh ├── Entry.cpp ├── Message.h ├── README.md ├── Makefile ├── Log.h ├── Params.cpp ├── Application.h ├── Node.cpp ├── MP1Node.h ├── EmulNet.h ├── HashTable.cpp ├── Member.h ├── MP2Node.h ├── Trace.cpp ├── submit.py ├── Member.cpp ├── Message.cpp ├── EmulNet.cpp ├── Log.cpp ├── MP1Node.cpp ├── MP2Node.cpp ├── KVStoreGrader.sh └── Application.cpp /testcases/read.conf: -------------------------------------------------------------------------------- 1 | MAX_NNB: 10 2 | CRUD_TEST: READ 3 | -------------------------------------------------------------------------------- /testcases/create.conf: -------------------------------------------------------------------------------- 1 | MAX_NNB: 10 2 | CRUD_TEST: CREATE 3 | -------------------------------------------------------------------------------- /testcases/delete.conf: -------------------------------------------------------------------------------- 1 | MAX_NNB: 10 2 | CRUD_TEST: DELETE 3 | -------------------------------------------------------------------------------- /testcases/update.conf: -------------------------------------------------------------------------------- 1 | MAX_NNB: 10 2 | CRUD_TEST: UPDATE 3 | -------------------------------------------------------------------------------- /kvstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin85421/Cloud-Computing-Concepts-Part-2/HEAD/kvstore.png -------------------------------------------------------------------------------- /MP2-specification-document.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin85421/Cloud-Computing-Concepts-Part-2/HEAD/MP2-specification-document.pdf -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H_ 2 | #define COMMON_H_ 3 | 4 | /** 5 | * Global variable 6 | */ 7 | // Transaction Id 8 | static int g_transID = 0; 9 | 10 | // message types, reply is the message from node to coordinator 11 | enum MessageType {CREATE, READ, UPDATE, DELETE, REPLY, READREPLY}; 12 | // enum of replica types 13 | enum ReplicaType {PRIMARY, SECONDARY, TERTIARY}; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /Readme.txt: -------------------------------------------------------------------------------- 1 | Read the specification document thoroughly. 2 | 3 | Create a high level design covering all scenarios / test cases before you start coding. 4 | 5 | How do I run only the CRUD tests ? 6 | 7 | $ make clean 8 | $ make 9 | $ ./Application ./testcases/create.conf 10 | or 11 | $ ./Application ./testcases/delete.conf 12 | or 13 | $ ./Application ./testcases/read.conf 14 | or 15 | $ ./Application ./testcases/update.conf 16 | 17 | How do I test if my code passes all the test cases ? 18 | Run the grader. Check the run procedure in KVStoreGrader.sh -------------------------------------------------------------------------------- /Entry.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Application.h 3 | * 4 | * DESCRIPTION: Header file Entry class 5 | **********************************/ 6 | 7 | #include "stdincludes.h" 8 | #include "Message.h" 9 | 10 | /** 11 | * CLASS NAME: Entry 12 | * 13 | * DESCRIPTION: This class describes the entry for each key in the DHT 14 | */ 15 | class Entry{ 16 | public: 17 | string value; 18 | int timestamp; 19 | ReplicaType replica; 20 | string delimiter; 21 | 22 | Entry(string entry); 23 | Entry(string _value, int _timestamp, ReplicaType _replica); 24 | string convertToString(); 25 | }; 26 | -------------------------------------------------------------------------------- /Queue.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Queue.h 3 | * 4 | * DESCRIPTION: Header file for std:: related functions 5 | **********************************/ 6 | 7 | #ifndef QUEUE_H_ 8 | #define QUEUE_H_ 9 | 10 | #include "stdincludes.h" 11 | #include "Member.h" 12 | 13 | /** 14 | * Class name: Queue 15 | * 16 | * Description: This function wraps std::queue related functions 17 | */ 18 | class Queue { 19 | public: 20 | Queue() {} 21 | virtual ~Queue() {} 22 | static bool enqueue(queue *queue, void *buffer, int size) { 23 | q_elt element(buffer, size); 24 | queue->emplace(element); 25 | return true; 26 | } 27 | }; 28 | 29 | #endif /* QUEUE_H_ */ 30 | -------------------------------------------------------------------------------- /Node.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Node.h 3 | * 4 | * DESCRIPTION: Header file Node class 5 | **********************************/ 6 | 7 | #ifndef NODE_H_ 8 | #define NODE_H_ 9 | 10 | #include "stdincludes.h" 11 | #include "Member.h" 12 | 13 | class Node { 14 | public: 15 | Address nodeAddress; 16 | size_t nodeHashCode; 17 | std::hash hashFunc; 18 | Node(); 19 | Node(Address address); 20 | Node(const Node& another); 21 | Node& operator=(const Node& another); 22 | bool operator < (const Node& another) const; 23 | void computeHashCode(); 24 | size_t getHashCode(); 25 | Address * getAddress(); 26 | void setHashCode(size_t hashCode); 27 | void setAddress(Address address); 28 | virtual ~Node(); 29 | }; 30 | 31 | #endif /* NODE_H_ */ 32 | -------------------------------------------------------------------------------- /HashTable.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: HashTable.h 3 | * 4 | * DESCRIPTION: Header file HashTable class 5 | **********************************/ 6 | 7 | #ifndef HASHTABLE_H_ 8 | #define HASHTABLE_H_ 9 | 10 | /** 11 | * Header files 12 | */ 13 | #include "stdincludes.h" 14 | #include "common.h" 15 | #include "Entry.h" 16 | 17 | /** 18 | * CLASS NAME: HashTable 19 | * 20 | * DESCRIPTION: This class is a wrapper to the map provided by C++ STL. 21 | * 22 | */ 23 | class HashTable { 24 | public: 25 | map hashTable; 26 | //public: 27 | HashTable(); 28 | bool create(string key, string value); 29 | string read(string key); 30 | bool update(string key, string newValue); 31 | bool deleteKey(string key); 32 | bool isEmpty(); 33 | unsigned long currentSize(); 34 | void clear(); 35 | unsigned long count(string key); 36 | virtual ~HashTable(); 37 | }; 38 | 39 | #endif /* HASHTABLE_H_ */ 40 | -------------------------------------------------------------------------------- /Trace.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Trace.h 3 | * 4 | * DESCRIPTION: Header file Trace class 5 | **********************************/ 6 | 7 | #ifndef TRACE_H_ 8 | #define TRACE_H_ 9 | 10 | #include "stdincludes.h" 11 | 12 | /* 13 | * Macros 14 | */ 15 | #define LOG_FILE_LOCATION "machine.log" 16 | 17 | /** 18 | * CLASS NAME: Trace 19 | * 20 | * DESCRIPTION: Creates a trace of function entry, exit and variable values for debugging 21 | */ 22 | class Trace { 23 | public: 24 | FILE *logF; 25 | int traceFileCreate(); 26 | int printToTrace( 27 | char *keyMessage, // Message to be written as key 28 | char *valueMessage // Message to be written as value 29 | ); 30 | int traceFileClose(); 31 | int funcEntry( 32 | char *valueMessage // Value 33 | ); 34 | int funcExit( 35 | char *valueMessage, // Value 36 | int f_rc = SUCCESS // Function RC 37 | ); 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /stdincludes.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: stdincludes.h 3 | * 4 | * DESCRIPTION: standard header file 5 | **********************************/ 6 | 7 | #ifndef _STDINCLUDES_H_ 8 | #define _STDINCLUDES_H_ 9 | 10 | /* 11 | * Macros 12 | */ 13 | #define RING_SIZE 512 14 | #define FAILURE -1 15 | #define SUCCESS 0 16 | 17 | /* 18 | * Standard Header files 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | using namespace std; 40 | 41 | #define STDCLLBKARGS (void *env, char *data, int size) 42 | #define STDCLLBKRET void 43 | #define DEBUGLOG 1 44 | 45 | #endif /* _STDINCLUDES_H_ */ 46 | -------------------------------------------------------------------------------- /Params.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Params.h 3 | * 4 | * DESCRIPTION: Header file of Parameter class 5 | **********************************/ 6 | 7 | #ifndef _PARAMS_H_ 8 | #define _PARAMS_H_ 9 | 10 | #include "stdincludes.h" 11 | #include "Params.h" 12 | #include "Member.h" 13 | 14 | enum testTYPE { CREATE_TEST, READ_TEST, UPDATE_TEST, DELETE_TEST }; 15 | 16 | /** 17 | * CLASS NAME: Params 18 | * 19 | * DESCRIPTION: Params class describing the test cases 20 | */ 21 | class Params{ 22 | public: 23 | int MAX_NNB; // max number of neighbors 24 | int SINGLE_FAILURE; // single/multi failure 25 | double MSG_DROP_PROB; // message drop probability 26 | double STEP_RATE; // dictates the rate of insertion 27 | int EN_GPSZ; // actual number of peers 28 | int MAX_MSG_SIZE; 29 | int DROP_MSG; 30 | int dropmsg; 31 | int globaltime; 32 | int allNodesJoined; 33 | short PORTNUM; 34 | int CRUDTEST; 35 | Params(); 36 | void setparams(char *); 37 | int getcurrtime(); 38 | }; 39 | 40 | #endif /* _PARAMS_H_ */ 41 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #********************** 2 | #* 3 | #* Progam Name: MP1. Membership Protocol. 4 | #* 5 | #* Current file: run.sh 6 | #* About this file: Submission shell script. 7 | #* 8 | #*********************** 9 | #!/bin/sh 10 | rm -rf grade-dir # Make sure grade-dir is clean before starting 11 | rm -f dbg.*.log 12 | 13 | mkdir grade-dir 14 | cd grade-dir 15 | wget https://spark-public.s3.amazonaws.com/cloudcomputing2/assignments/mp2_assignment.zip || { echo 'ERROR ... Please install wget' ; exit 1; } 16 | unzip mp2_assignment.zip || { echo 'ERROR ... Zip file not found' ; exit 1; } 17 | cd mp2_assignment 18 | cp ../../MP2Node.* . 19 | cp ../../MP1Node.* . 20 | make clean > /dev/null 2>&1 21 | make > /dev/null 2>&1 22 | 23 | echo "CREATE test" 24 | ./Application testcases/create.conf > /dev/null 2>&1 25 | cp dbg.log ../../dbg.0.log 26 | echo "DELETE test" 27 | ./Application testcases/delete.conf > /dev/null 2>&1 28 | cp dbg.log ../../dbg.1.log 29 | echo "READ test" 30 | ./Application testcases/read.conf > /dev/null 2>&1 31 | cp dbg.log ../../dbg.2.log 32 | echo "UPDATE test" 33 | ./Application testcases/update.conf > /dev/null 2>&1 34 | cp dbg.log ../../dbg.3.log 35 | 36 | cd ../.. 37 | rm -rf grade-dir 38 | -------------------------------------------------------------------------------- /Entry.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Application.h 3 | * 4 | * DESCRIPTION: Entry class definition 5 | **********************************/ 6 | #include "Entry.h" 7 | 8 | /** 9 | * constructor 10 | */ 11 | Entry::Entry(string _value, int _timestamp, ReplicaType _replica){ 12 | this->delimiter = ":"; 13 | value = _value; 14 | timestamp = _timestamp; 15 | replica = _replica; 16 | } 17 | 18 | /** 19 | * constructor 20 | * 21 | * DESCRIPTION: Convert string to get an Entry object 22 | */ 23 | Entry::Entry(string entry){ 24 | vector tuple; 25 | this->delimiter = ":"; 26 | size_t pos = entry.find(delimiter); 27 | size_t start = 0; 28 | while (pos != string::npos) { 29 | string field = entry.substr(start, pos-start); 30 | tuple.push_back(field); 31 | start = pos + delimiter.size(); 32 | pos = entry.find(delimiter, start); 33 | } 34 | tuple.push_back(entry.substr(start)); 35 | 36 | value = tuple.at(0); 37 | timestamp = stoi(tuple.at(1)); 38 | replica = static_cast(stoi(tuple.at(2))); 39 | } 40 | 41 | /** 42 | * FUNCTION NAME: converToString 43 | * 44 | * DESCRIPTION: Convert the object to a string representation 45 | */ 46 | string Entry::convertToString() { 47 | return value + delimiter + to_string(timestamp) + delimiter + to_string(replica); 48 | } 49 | -------------------------------------------------------------------------------- /Message.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Message.h 3 | * 4 | * DESCRIPTION: Message class header file 5 | **********************************/ 6 | #ifndef MESSAGE_H_ 7 | #define MESSAGE_H_ 8 | 9 | #include "stdincludes.h" 10 | #include "Member.h" 11 | #include "common.h" 12 | 13 | /** 14 | * CLASS NAME: Message 15 | * 16 | * DESCRIPTION: This class is used for message passing among nodes 17 | */ 18 | class Message{ 19 | public: 20 | MessageType type; 21 | ReplicaType replica; 22 | string key; 23 | string value; 24 | Address fromAddr; 25 | int transID; 26 | bool success; // success or not 27 | // delimiter 28 | string delimiter; 29 | // construct a message from a string 30 | Message(string message); 31 | Message(const Message& anotherMessage); 32 | // construct a create or update message 33 | Message(int _transID, Address _fromAddr, MessageType _type, string _key, string _value); 34 | Message(int _transID, Address _fromAddr, MessageType _type, string _key, string _value, ReplicaType _replica); 35 | // construct a read or delete message 36 | Message(int _transID, Address _fromAddr, MessageType _type, string _key); 37 | // construct reply message 38 | Message(int _transID, Address _fromAddr, MessageType _type, bool _success); 39 | // construct read reply message 40 | Message(int _transID, Address _fromAddr, string _value); 41 | Message& operator = (const Message& anotherMessage); 42 | // serialize to a string 43 | string toString(); 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloud-Computing-Concepts-Part-2 2 | Coursera course : https://www.coursera.org/specializations/cloud-computing 3 | # What is the project? 4 | * Building a **Fault-Tolerant Key-Value Store**. 5 | 6 | * The main functionalities of the Key-Value Store : 7 | * **CRUD operations :** A key-value store supporting CRUD operations (Create, Read, Update, Delete). 8 | * **Load-balancing :** via a consistent hashing ring to hash both servers and keys. 9 | * **Fault-tolerance up to two failures :** by replicating each key three times to three successive nodes in the ring, starting from the first node at or to the clockwise of the hashed key. 10 | * **Quorum consistency level** for both reads and writes (at least two replicas). 11 | * **Stabilization :** after failure (recreate three replicas after failure). 12 | 13 | # Principle of **Fault-Tolerant Key-Value Store** : 14 | ![image](https://github.com/kevin85421/Cloud-Computing-Concepts-Part-2/blob/master/kvstore.png) 15 | 16 | # How do I run the Grader on my computer ? 17 | * There is a grader script KVStoreGrader.sh. The tests include: 18 | * **Basic CRUD** tests that test if three replicas respond 19 | * **Single failure** followed immediately by operations which should succeed (as quorum can still be 20 | reached with 1 failure) 21 | * **Multiple failures** followed immediately by operations which should fail as quorum cannot be 22 | reached 23 | * Failures followed by a time for the system to re-stabilize, followed by operations that should 24 | succeed because the key has been re-replicated again at three nodes. 25 | ``` 26 | $ chmod +x KVStoreGrader.sh 27 | $ ./KVStoreGrader.sh 28 | ``` 29 | # Result 30 | * Points achieved: 90 out of 90 [100%] 31 | 32 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #********************** 2 | #* 3 | #* Progam Name: MP1. Membership Protocol. 4 | #* 5 | #* Current file: Makefile 6 | #* About this file: Build Script. 7 | #* 8 | #*********************** 9 | 10 | CFLAGS = -Wall -g -std=c++11 11 | 12 | all: Application 13 | 14 | Application: MP1Node.o EmulNet.o Application.o Log.o Params.o Member.o Trace.o MP2Node.o Node.o HashTable.o Entry.o Message.o 15 | g++ -o Application MP1Node.o EmulNet.o Application.o Log.o Params.o Member.o Trace.o MP2Node.o Node.o HashTable.o Entry.o Message.o ${CFLAGS} 16 | 17 | MP1Node.o: MP1Node.cpp MP1Node.h Log.h Params.h Member.h EmulNet.h Queue.h 18 | g++ -c MP1Node.cpp ${CFLAGS} 19 | 20 | EmulNet.o: EmulNet.cpp EmulNet.h Params.h Member.h 21 | g++ -c EmulNet.cpp ${CFLAGS} 22 | 23 | Application.o: Application.cpp Application.h Member.h Log.h Params.h Member.h EmulNet.h Queue.h 24 | g++ -c Application.cpp ${CFLAGS} 25 | 26 | Log.o: Log.cpp Log.h Params.h Member.h 27 | g++ -c Log.cpp ${CFLAGS} 28 | 29 | Params.o: Params.cpp Params.h 30 | g++ -c Params.cpp ${CFLAGS} 31 | 32 | Member.o: Member.cpp Member.h 33 | g++ -c Member.cpp ${CFLAGS} 34 | 35 | Trace.o: Trace.cpp Trace.h 36 | g++ -c Trace.cpp ${CFLAGS} 37 | 38 | MP2Node.o: MP2Node.cpp MP2Node.h EmulNet.h Params.h Member.h Trace.h Node.h HashTable.h Log.h Params.h Message.h 39 | g++ -c MP2Node.cpp ${CFLAGS} 40 | 41 | Node.o: Node.cpp Node.h Member.h 42 | g++ -c Node.cpp ${CFLAGS} 43 | 44 | HashTable.o: HashTable.cpp HashTable.h common.h Entry.h 45 | g++ -c HashTable.cpp ${CFLAGS} 46 | 47 | Entry.o: Entry.cpp Entry.h Message.h 48 | g++ -c Entry.cpp ${CFLAGS} 49 | 50 | Message.o: Message.cpp Message.h Member.h common.h 51 | g++ -c Message.cpp ${CFLAGS} 52 | 53 | clean: 54 | rm -rf *.o Application dbg.log msgcount.log stats.log machine.log 55 | -------------------------------------------------------------------------------- /Log.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Log.h 3 | * 4 | * DESCRIPTION: Header file of Log class 5 | **********************************/ 6 | 7 | #ifndef _LOG_H_ 8 | #define _LOG_H_ 9 | 10 | #include "stdincludes.h" 11 | #include "Params.h" 12 | #include "Member.h" 13 | 14 | /* 15 | * Macros 16 | */ 17 | // number of writes after which to flush file 18 | #define MAXWRITES 1 19 | #define MAGIC_NUMBER "CS425" 20 | #define DBG_LOG "dbg.log" 21 | #define STATS_LOG "stats.log" 22 | 23 | /** 24 | * CLASS NAME: Log 25 | * 26 | * DESCRIPTION: Functions to log messages in a debug log 27 | */ 28 | class Log{ 29 | private: 30 | Params *par; 31 | bool firstTime; 32 | public: 33 | Log(Params *p); 34 | Log(const Log &anotherLog); 35 | Log& operator = (const Log &anotherLog); 36 | virtual ~Log(); 37 | void LOG(Address *, const char * str, ...); 38 | void logNodeAdd(Address *, Address *); 39 | void logNodeRemove(Address *, Address *); 40 | // success 41 | void logCreateSuccess(Address * address, bool isCoordinator, int transID, string key, string value); 42 | void logReadSuccess(Address * address, bool isCoordinator, int transID, string key, string value); 43 | void logUpdateSuccess(Address * address, bool isCoordinator, int transID, string key, string newValue); 44 | void logDeleteSuccess(Address * address, bool isCoordinator, int transID, string key); 45 | // fail 46 | void logCreateFail(Address * address, bool isCoordinator, int transID, string key, string value); 47 | void logReadFail(Address * address, bool isCoordinator, int transID, string key); 48 | void logUpdateFail(Address * address, bool isCoordinator, int transID, string key, string newValue); 49 | void logDeleteFail(Address * address, bool isCoordinator, int transID, string key); 50 | }; 51 | 52 | #endif /* _LOG_H_ */ 53 | -------------------------------------------------------------------------------- /Params.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Params.cpp 3 | * 4 | * DESCRIPTION: Definition of Parameter class 5 | **********************************/ 6 | 7 | #include "Params.h" 8 | 9 | /** 10 | * Constructor 11 | */ 12 | Params::Params(): PORTNUM(8001) {} 13 | 14 | /** 15 | * FUNCTION NAME: setparams 16 | * 17 | * DESCRIPTION: Set the parameters for this test case 18 | */ 19 | void Params::setparams(char *config_file) { 20 | //trace.funcEntry("Params::setparams"); 21 | char CRUD[10]; 22 | FILE *fp = fopen(config_file,"r"); 23 | 24 | fscanf(fp,"MAX_NNB: %d", &MAX_NNB); 25 | fscanf(fp,"\nSINGLE_FAILURE: %d", &SINGLE_FAILURE); 26 | fscanf(fp,"\nDROP_MSG: %d", &DROP_MSG); 27 | fscanf(fp,"\nMSG_DROP_PROB: %lf", &MSG_DROP_PROB); 28 | fscanf(fp,"\nCRUD_TEST: %s", CRUD); 29 | 30 | if ( 0 == strcmp(CRUD, "CREATE") ) { 31 | this->CRUDTEST = CREATE_TEST; 32 | } 33 | else if ( 0 == strcmp(CRUD, "READ") ) { 34 | this->CRUDTEST = READ_TEST; 35 | } 36 | else if ( 0 == strcmp(CRUD, "UPDATE") ) { 37 | this->CRUDTEST = UPDATE_TEST; 38 | } 39 | else if ( 0 == strcmp(CRUD, "DELETE") ) { 40 | this->CRUDTEST = DELETE_TEST; 41 | } 42 | 43 | //printf("Parameters of the test case: %d %d %d %lf\n", MAX_NNB, SINGLE_FAILURE, DROP_MSG, MSG_DROP_PROB); 44 | 45 | EN_GPSZ = MAX_NNB; 46 | STEP_RATE=.25; 47 | MAX_MSG_SIZE = 4000; 48 | globaltime = 0; 49 | dropmsg = 0; 50 | allNodesJoined = 0; 51 | for ( unsigned int i = 0; i < EN_GPSZ; i++ ) { 52 | allNodesJoined += i; 53 | } 54 | fclose(fp); 55 | //trace.funcExit("Params::setparams", SUCCESS); 56 | return; 57 | } 58 | 59 | /** 60 | * FUNCTION NAME: getcurrtime 61 | * 62 | * DESCRIPTION: Return time since start of program, in time units. 63 | * For a 'real' implementation, this return time would be the UTC time. 64 | */ 65 | int Params::getcurrtime(){ 66 | return globaltime; 67 | } 68 | -------------------------------------------------------------------------------- /Application.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Application.h 3 | * 4 | * DESCRIPTION: Header file of all classes pertaining to the Application Layer 5 | **********************************/ 6 | 7 | #ifndef _APPLICATION_H_ 8 | #define _APPLICATION_H_ 9 | 10 | #include "stdincludes.h" 11 | #include "MP1Node.h" 12 | #include "Log.h" 13 | #include "Params.h" 14 | #include "Member.h" 15 | #include "EmulNet.h" 16 | #include "Queue.h" 17 | #include "MP2Node.h" 18 | #include "Node.h" 19 | #include "common.h" 20 | 21 | /** 22 | * global variables 23 | */ 24 | int nodeCount = 0; 25 | static const char alphanum[] = 26 | "0123456789" 27 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 28 | "abcdefghijklmnopqrstuvwxyz"; 29 | 30 | /* 31 | * Macros 32 | */ 33 | #define ARGS_COUNT 2 34 | #define TOTAL_RUNNING_TIME 700 35 | #define INSERT_TIME (TOTAL_RUNNING_TIME-600) 36 | #define TEST_TIME (INSERT_TIME+50) 37 | #define STABILIZE_TIME 50 38 | #define FIRST_FAIL_TIME 25 39 | #define LAST_FAIL_TIME 10 40 | #define RF 3 41 | #define NUMBER_OF_INSERTS 100 42 | #define KEY_LENGTH 5 43 | 44 | /** 45 | * CLASS NAME: Application 46 | * 47 | * DESCRIPTION: Application layer of the distributed system 48 | */ 49 | class Application{ 50 | private: 51 | // Address for introduction to the group 52 | // Coordinator Node 53 | char JOINADDR[30]; 54 | EmulNet *en; 55 | EmulNet *en1; 56 | Log *log; 57 | MP1Node **mp1; 58 | MP2Node **mp2; 59 | Params *par; 60 | map testKVPairs; 61 | public: 62 | Application(char *); 63 | virtual ~Application(); 64 | Address getjoinaddr(); 65 | void initTestKVPairs(); 66 | int run(); 67 | void mp1Run(); 68 | void mp2Run(); 69 | void fail(); 70 | void insertTestKVPairs(); 71 | int findARandomNodeThatIsAlive(); 72 | void deleteTest(); 73 | void readTest(); 74 | void updateTest(); 75 | }; 76 | 77 | #endif /* _APPLICATION_H__ */ 78 | -------------------------------------------------------------------------------- /Node.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Node.cpp 3 | * 4 | * DESCRIPTION: Node class definition 5 | **********************************/ 6 | 7 | #include "Node.h" 8 | 9 | /** 10 | * constructor 11 | */ 12 | Node::Node() {} 13 | 14 | /** 15 | * constructor 16 | */ 17 | Node::Node(Address address) { 18 | this->nodeAddress = address; 19 | computeHashCode(); 20 | } 21 | 22 | /** 23 | * Destructor 24 | */ 25 | Node::~Node() {} 26 | 27 | /** 28 | * FUNCTION NAME: computeHashCode 29 | * 30 | * DESCRIPTION: This function computes the hash code of the node address 31 | */ 32 | void Node::computeHashCode() { 33 | nodeHashCode = hashFunc(nodeAddress.addr)%RING_SIZE; 34 | } 35 | 36 | /** 37 | * copy constructor 38 | */ 39 | Node::Node(const Node& another) { 40 | this->nodeAddress = another.nodeAddress; 41 | this->nodeHashCode = another.nodeHashCode; 42 | } 43 | 44 | /** 45 | * Assignment operator overloading 46 | */ 47 | Node& Node::operator=(const Node& another) { 48 | this->nodeAddress = another.nodeAddress; 49 | this->nodeHashCode = another.nodeHashCode; 50 | return *this; 51 | } 52 | 53 | /** 54 | * operator overloading 55 | */ 56 | bool Node::operator < (const Node& another) const { 57 | return this->nodeHashCode < another.nodeHashCode; 58 | } 59 | 60 | /** 61 | * FUNCTION NAME: getHashCode 62 | * 63 | * DESCRIPTION: return hash code of the node 64 | */ 65 | size_t Node::getHashCode() { 66 | return nodeHashCode; 67 | } 68 | 69 | /** 70 | * FUNCTION NAME: getAddress 71 | * 72 | * DESCRIPTION: return the address of the node 73 | */ 74 | Address * Node::getAddress() { 75 | return &nodeAddress; 76 | } 77 | 78 | /** 79 | * FUNCTION NAME: setHashCode 80 | * 81 | * DESCRIPTION: set the hash code of the node 82 | */ 83 | void Node::setHashCode(size_t hashCode) { 84 | this->nodeHashCode = hashCode; 85 | } 86 | 87 | /** 88 | * FUNCTION NAME: setAddress 89 | * 90 | * DESCRIPTION: set the address of the node 91 | */ 92 | void Node::setAddress(Address address) { 93 | this->nodeAddress = address; 94 | } 95 | -------------------------------------------------------------------------------- /MP1Node.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: MP1Node.cpp 3 | * 4 | * DESCRIPTION: Membership protocol run by this Node. 5 | * Header file of MP1Node class. 6 | **********************************/ 7 | 8 | #ifndef _MP1NODE_H_ 9 | #define _MP1NODE_H_ 10 | 11 | #include "stdincludes.h" 12 | #include "Log.h" 13 | #include "Params.h" 14 | #include "Member.h" 15 | #include "EmulNet.h" 16 | #include "Queue.h" 17 | 18 | /** 19 | * Macros 20 | */ 21 | #define TREMOVE 20 22 | #define TFAIL 5 23 | 24 | /* 25 | * Note: You can change/add any functions in MP1Node.{h,cpp} 26 | */ 27 | 28 | /** 29 | * Message Types 30 | */ 31 | enum MsgTypes{ 32 | JOINREQ, 33 | JOINREP, 34 | DUMMYLASTMSGTYPE, 35 | PING 36 | }; 37 | 38 | /** 39 | * STRUCT NAME: MessageHdr 40 | * 41 | * DESCRIPTION: Header and content of a message 42 | */ 43 | typedef struct MessageHdr { 44 | enum MsgTypes msgType; 45 | vector< MemberListEntry> member_vector; 46 | Address* addr; 47 | }MessageHdr; 48 | 49 | /** 50 | * CLASS NAME: MP1Node 51 | * 52 | * DESCRIPTION: Class implementing Membership protocol functionalities for failure detection 53 | */ 54 | class MP1Node { 55 | private: 56 | EmulNet *emulNet; 57 | Log *log; 58 | Params *par; 59 | Member *memberNode; 60 | char NULLADDR[6]; 61 | 62 | public: 63 | MP1Node(Member *, Params *, EmulNet *, Log *, Address *); 64 | Member * getMemberNode() { 65 | return memberNode; 66 | } 67 | int recvLoop(); 68 | static int enqueueWrapper(void *env, char *buff, int size); 69 | void nodeStart(char *servaddrstr, short serverport); 70 | int initThisNode(Address *joinaddr); 71 | int introduceSelfToGroup(Address *joinAddress); 72 | int finishUpThisNode(); 73 | void nodeLoop(); 74 | void checkMessages(); 75 | bool recvCallBack(void *env, char *data, int size); 76 | void nodeLoopOps(); 77 | int isNullAddress(Address *addr); 78 | Address getJoinAddress(); 79 | void initMemberListTable(Member *memberNode); 80 | void printAddress(Address *addr); 81 | virtual ~MP1Node(); 82 | void push_member_list( MessageHdr* msg); 83 | void push_member_list(MemberListEntry* e); 84 | MemberListEntry* check_member_list( int id, short port); 85 | void send_message(Address* toaddr, MsgTypes t); 86 | void ping_handler(MessageHdr* msg); 87 | MemberListEntry* check_member_list(Address* node_addr); 88 | void update_src_member(MessageHdr* msg); 89 | Address* get_address(int id, short port); 90 | }; 91 | 92 | #endif /* _MP1NODE_H_ */ 93 | -------------------------------------------------------------------------------- /EmulNet.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: EmulNet.h 3 | * 4 | * DESCRIPTION: Emulated Network classes header file 5 | **********************************/ 6 | 7 | #ifndef _EMULNET_H_ 8 | #define _EMULNET_H_ 9 | 10 | #define MAX_NODES 1000 11 | #define MAX_TIME 3600 12 | #define ENBUFFSIZE 30000 13 | 14 | #include "stdincludes.h" 15 | #include "Params.h" 16 | #include "Member.h" 17 | 18 | using namespace std; 19 | 20 | /** 21 | * Struct Name: en_msg 22 | */ 23 | typedef struct en_msg { 24 | // Number of bytes after the class 25 | int size; 26 | // Source node 27 | Address from; 28 | // Destination node 29 | Address to; 30 | }en_msg; 31 | 32 | /** 33 | * Class Name: EM 34 | */ 35 | class EM { 36 | public: 37 | int nextid; 38 | int currbuffsize; 39 | int firsteltindex; 40 | en_msg* buff[ENBUFFSIZE]; 41 | EM() {} 42 | EM& operator = (EM &anotherEM) { 43 | this->nextid = anotherEM.getNextId(); 44 | this->currbuffsize = anotherEM.getCurrBuffSize(); 45 | this->firsteltindex = anotherEM.getFirstEltIndex(); 46 | int i = this->currbuffsize; 47 | while (i > 0) { 48 | this->buff[i] = anotherEM.buff[i]; 49 | i--; 50 | } 51 | return *this; 52 | } 53 | int getNextId() { 54 | return nextid; 55 | } 56 | int getCurrBuffSize() { 57 | return currbuffsize; 58 | } 59 | int getFirstEltIndex() { 60 | return firsteltindex; 61 | } 62 | void setNextId(int nextid) { 63 | this->nextid = nextid; 64 | } 65 | void settCurrBuffSize(int currbuffsize) { 66 | this->currbuffsize = currbuffsize; 67 | } 68 | void setFirstEltIndex(int firsteltindex) { 69 | this->firsteltindex = firsteltindex; 70 | } 71 | virtual ~EM() {} 72 | }; 73 | 74 | /** 75 | * CLASS NAME: EmulNet 76 | * 77 | * DESCRIPTION: This class defines an emulated network 78 | */ 79 | class EmulNet 80 | { 81 | private: 82 | Params* par; 83 | int sent_msgs[MAX_NODES + 1][MAX_TIME]; 84 | int recv_msgs[MAX_NODES + 1][MAX_TIME]; 85 | int enInited; 86 | EM emulnet; 87 | public: 88 | EmulNet(Params *p); 89 | EmulNet(EmulNet &anotherEmulNet); 90 | EmulNet& operator = (EmulNet &anotherEmulNet); 91 | virtual ~EmulNet(); 92 | void *ENinit(Address *myaddr, short port); 93 | int ENsend(Address *myaddr, Address *toaddr, string data); 94 | int ENsend(Address *myaddr, Address *toaddr, char *data, int size); 95 | int ENrecv(Address *myaddr, int (* enq)(void *, char *, int), struct timeval *t, int times, void *queue); 96 | int ENcleanup(); 97 | }; 98 | 99 | #endif /* _EMULNET_H_ */ 100 | -------------------------------------------------------------------------------- /HashTable.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: HashTable.cpp 3 | * 4 | * DESCRIPTION: Hash Table class definition 5 | **********************************/ 6 | 7 | #include "HashTable.h" 8 | 9 | HashTable::HashTable() {} 10 | 11 | HashTable::~HashTable() {} 12 | 13 | /** 14 | * FUNCTION NAME: create 15 | * 16 | * DESCRIPTION: This function inserts they (key,value) pair into the local hash table 17 | * 18 | * RETURNS: 19 | * true on SUCCESS 20 | * false in FAILURE 21 | */ 22 | bool HashTable::create(string key, string value) { 23 | hashTable.emplace(key, value); 24 | return true; 25 | } 26 | 27 | /** 28 | * FUNCTION NAME: read 29 | * 30 | * DESCRIPTION: This function searches for the key in the hash table 31 | * 32 | * RETURNS: 33 | * string value if found 34 | * else it returns a NULL 35 | */ 36 | string HashTable::read(string key) { 37 | map::iterator search; 38 | 39 | search = hashTable.find(key); 40 | if ( search != hashTable.end() ) { 41 | // Value found 42 | return search->second; 43 | } 44 | else { 45 | // Value not found 46 | return ""; 47 | } 48 | } 49 | 50 | /** 51 | * FUNCTION NAME: update 52 | * 53 | * DESCRIPTION: This function updates the given key with the updated value passed in 54 | * if the key is found 55 | * 56 | * RETURNS: 57 | * true on SUCCESS 58 | * false on FAILURE 59 | */ 60 | bool HashTable::update(string key, string newValue) { 61 | map::iterator update; 62 | 63 | if (read(key).empty()) { 64 | // Key not found 65 | return false; 66 | } 67 | // Key found 68 | //update = hashTable.at(key) = newValue; 69 | hashTable.at(key) = newValue; 70 | // Update successful 71 | return true; 72 | } 73 | 74 | /** 75 | * FUNCTION NAME: deleteKey 76 | * 77 | * DESCRIPTION: This function deletes the given key and the corresponding value if the key is found 78 | * 79 | * RETURNS: 80 | * true on SUCCESS 81 | * false on FAILURE 82 | */ 83 | bool HashTable::deleteKey(string key) { 84 | uint eraseCount = 0; 85 | 86 | if (read(key).empty()) { 87 | // Key not found 88 | return false; 89 | } 90 | eraseCount = hashTable.erase(key); 91 | if ( eraseCount < 1 ) { 92 | // Could not erase 93 | return false; 94 | } 95 | // Delete was successful 96 | return true; 97 | } 98 | 99 | /** 100 | * FUNCTION NAME: isEmpty 101 | * 102 | * DESCRIPTION: Returns if the hash table is empty 103 | * 104 | * RETURNS: 105 | * true if hash table is empty 106 | * false otherwise 107 | */ 108 | bool HashTable::isEmpty() { 109 | return hashTable.empty(); 110 | } 111 | 112 | /** 113 | * FUNCTION NAME: currentSize 114 | * 115 | * DESCRIPTION: Returns the current size of the hash table 116 | * 117 | * RETURNS: 118 | * size of the table as unit 119 | */ 120 | unsigned long HashTable::currentSize() { 121 | return (unsigned long)hashTable.size(); 122 | } 123 | 124 | /** 125 | * FUNCTION NAME: clear 126 | * 127 | * DESCRIPTION: Clear all contents from the hash table 128 | */ 129 | void HashTable::clear() { 130 | hashTable.clear(); 131 | } 132 | 133 | /** 134 | * FUNCTION NAME: count 135 | * 136 | * DESCRIPTION: Returns the count of the number of values for the passed in key 137 | * 138 | * RETURNS: 139 | * unsigned long count (Should be always 1) 140 | */ 141 | unsigned long HashTable::count(string key) { 142 | return (unsigned long) hashTable.count(key); 143 | } 144 | 145 | -------------------------------------------------------------------------------- /Member.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Member.h 3 | * 4 | * DESCRIPTION: Definition of all Member related class 5 | **********************************/ 6 | 7 | #ifndef MEMBER_H_ 8 | #define MEMBER_H_ 9 | 10 | #include "stdincludes.h" 11 | 12 | /** 13 | * CLASS NAME: q_elt 14 | * 15 | * DESCRIPTION: Entry in the queue 16 | */ 17 | class q_elt { 18 | public: 19 | void *elt; 20 | int size; 21 | q_elt(void *elt, int size); 22 | }; 23 | 24 | /** 25 | * CLASS NAME: Address 26 | * 27 | * DESCRIPTION: Class representing the address of a single node 28 | */ 29 | class Address { 30 | public: 31 | char addr[6]; 32 | Address() {} 33 | // Copy constructor 34 | Address(const Address &anotherAddress); 35 | // Overloaded = operator 36 | Address& operator =(const Address &anotherAddress); 37 | bool operator ==(const Address &anotherAddress); 38 | Address(string address) { 39 | size_t pos = address.find(":"); 40 | int id = stoi(address.substr(0, pos)); 41 | short port = (short)stoi(address.substr(pos + 1, address.size()-pos-1)); 42 | memcpy(&addr[0], &id, sizeof(int)); 43 | memcpy(&addr[4], &port, sizeof(short)); 44 | } 45 | string getAddress() { 46 | int id = 0; 47 | short port; 48 | memcpy(&id, &addr[0], sizeof(int)); 49 | memcpy(&port, &addr[4], sizeof(short)); 50 | return to_string(id) + ":" + to_string(port); 51 | } 52 | void init() { 53 | memset(&addr, 0, sizeof(addr)); 54 | } 55 | }; 56 | 57 | /** 58 | * CLASS NAME: MemberListEntry 59 | * 60 | * DESCRIPTION: Entry in the membership list 61 | */ 62 | class MemberListEntry { 63 | public: 64 | int id; 65 | short port; 66 | long heartbeat; 67 | long timestamp; 68 | MemberListEntry(int id, short port, long heartbeat, long timestamp); 69 | MemberListEntry(int id, short port); 70 | MemberListEntry(): id(0), port(0), heartbeat(0), timestamp(0) {} 71 | MemberListEntry(const MemberListEntry &anotherMLE); 72 | MemberListEntry& operator =(const MemberListEntry &anotherMLE); 73 | int getid(); 74 | short getport(); 75 | long getheartbeat(); 76 | long gettimestamp(); 77 | void setid(int id); 78 | void setport(short port); 79 | void setheartbeat(long hearbeat); 80 | void settimestamp(long timestamp); 81 | }; 82 | 83 | /** 84 | * CLASS NAME: Member 85 | * 86 | * DESCRIPTION: Class representing a member in the distributed system 87 | */ 88 | // Declaration and definition here 89 | class Member { 90 | public: 91 | // This member's Address 92 | Address addr; 93 | // boolean indicating if this member is up 94 | bool inited; 95 | // boolean indicating if this member is in the group 96 | bool inGroup; 97 | // boolean indicating if this member has failed 98 | bool bFailed; 99 | // number of my neighbors 100 | int nnb; 101 | // the node's own heartbeat 102 | long heartbeat; 103 | // counter for next ping 104 | int pingCounter; 105 | // counter for ping timeout 106 | int timeOutCounter; 107 | // Membership table 108 | vector memberList; 109 | // My position in the membership table 110 | vector::iterator myPos; 111 | // Queue for failure detection messages 112 | queue mp1q; 113 | // Queue for KVstore messages 114 | queue mp2q; 115 | /** 116 | * Constructor 117 | */ 118 | Member(): inited(false), inGroup(false), bFailed(false), nnb(0), heartbeat(0), pingCounter(0), timeOutCounter(0) {} 119 | // copy constructor 120 | Member(const Member &anotherMember); 121 | // Assignment operator overloading 122 | Member& operator =(const Member &anotherMember); 123 | virtual ~Member() {} 124 | }; 125 | 126 | #endif /* MEMBER_H_ */ 127 | -------------------------------------------------------------------------------- /MP2Node.h: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: MP2Node.h 3 | * 4 | * DESCRIPTION: MP2Node class header file 5 | **********************************/ 6 | 7 | #ifndef MP2NODE_H_ 8 | #define MP2NODE_H_ 9 | 10 | /** 11 | * Header files 12 | */ 13 | #include "stdincludes.h" 14 | #include "EmulNet.h" 15 | #include "Node.h" 16 | #include "HashTable.h" 17 | #include "Log.h" 18 | #include "Params.h" 19 | #include "Message.h" 20 | #include "Queue.h" 21 | const int STABLE = -1; 22 | 23 | class transaction { 24 | private: 25 | int id; 26 | int timestamp; 27 | public: 28 | transaction(int trans_id, int timestamp, MessageType mType, string key, string value); 29 | int replyCount; 30 | int successCount; 31 | string key; 32 | string value; 33 | MessageType mType; 34 | int getTime(){ return timestamp;}; 35 | }; 36 | 37 | /** 38 | * CLASS NAME: MP2Node 39 | * 40 | * DESCRIPTION: This class encapsulates all the key-value store functionality 41 | * including: 42 | * 1) Ring 43 | * 2) Stabilization Protocol 44 | * 3) Server side CRUD APIs 45 | * 4) Client side CRUD APIs 46 | */ 47 | class MP2Node { 48 | private: 49 | // Vector holding the next two neighbors in the ring who have my replicas 50 | vector hasMyReplicas; 51 | // Vector holding the previous two neighbors in the ring whose replicas I have 52 | vector haveReplicasOf; 53 | // Ring 54 | vector ring; 55 | // Hash Table 56 | HashTable * ht; 57 | // Member representing this member 58 | Member *memberNode; 59 | // Params object 60 | Params *par; 61 | // Object of EmulNet 62 | EmulNet * emulNet; 63 | // Object of Log 64 | Log * log; 65 | // Transactions 66 | map transMap; 67 | // 68 | map transComplete; 69 | 70 | public: 71 | MP2Node(Member *memberNode, Params *par, EmulNet *emulNet, Log *log, Address *addressOfMember); 72 | Member * getMemberNode() { 73 | return this->memberNode; 74 | } 75 | 76 | // ring functionalities 77 | void updateRing(); 78 | vector getMembershipList(); 79 | size_t hashFunction(string key); 80 | void findNeighbors(); 81 | 82 | // client side CRUD APIs 83 | void clientCreate(string key, string value); 84 | void clientRead(string key); 85 | void clientUpdate(string key, string value); 86 | void clientDelete(string key); 87 | 88 | // receive messages from Emulnet 89 | bool recvLoop(); 90 | static int enqueueWrapper(void *env, char *buff, int size); 91 | 92 | // handle messages from receiving queue 93 | void checkMessages(); 94 | 95 | // coordinator dispatches messages to corresponding nodes 96 | void dispatchMessages(Message message); 97 | 98 | // find the addresses of nodes that are responsible for a key 99 | vector findNodes(string key); 100 | 101 | // server 102 | bool createKeyValue(string key, string value, ReplicaType replica, int transID); 103 | string readKey(string key, int transID); 104 | bool updateKeyValue(string key, string value, ReplicaType replica, int transID); 105 | bool deletekey(string key, int transID); 106 | 107 | // stabilization protocol - handle multiple failures 108 | void stabilizationProtocol(); 109 | 110 | // My function 111 | Message constructMsg(MessageType mType, string key, string value = "", bool success = false); 112 | void createTransaction(int trans_id, MessageType mType, string key, string value); 113 | void sendreply(string key, MessageType mType, bool success, Address* fromaddr, int transID, string content = ""); 114 | void checkTransMap(); 115 | void logOperation(transaction* t, bool isCoordinator, bool success, int transID); 116 | 117 | // Destructor 118 | ~MP2Node(); 119 | }; 120 | 121 | 122 | #endif /* MP2NODE_H_ */ 123 | -------------------------------------------------------------------------------- /Trace.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | //**************************************************************************** 3 | // 4 | // FILE NAME: Trace.cpp 5 | // 6 | // DECSRIPTION: This is the source file for trace functionality 7 | // 8 | // CHANGE ACTIVITY: 9 | // Date Who Description 10 | // ========== ======= =============== 11 | // 12 | //**************************************************************************** 13 | ////////////////////////////////////////////////////////////////////////////// 14 | 15 | /* 16 | * Header files 17 | */ 18 | #include "Trace.h" 19 | 20 | /***************************************************************** 21 | * NAME: traceFileCreate 22 | * 23 | * DESCRIPTION: Function creates a trace file 24 | * 25 | * RETURN: 26 | * (int) SUCCESS 27 | * ERROR otherwise 28 | * 29 | ****************************************************************/ 30 | int Trace::traceFileCreate() { 31 | 32 | int rc = SUCCESS; // Return code 33 | 34 | logF = fopen(LOG_FILE_LOCATION, "w"); 35 | if ( NULL == logF ) 36 | { 37 | printf("\nUnable to open log file in write mode\n"); 38 | rc = FAILURE; 39 | } 40 | 41 | return rc; 42 | } 43 | 44 | /***************************************************************** 45 | * NAME: printToTrace 46 | * 47 | * DESCRIPTION: Function takes care of writing into log 48 | * 49 | * PARAMETERS: 50 | * (char *) keyMsg - buffer to be written as key 51 | * (char *) valueMsg - buffer to be written as value 52 | * 53 | * RETURN: 54 | * (int) SUCCESS 55 | * FAILURE otherwise 56 | * 57 | ****************************************************************/ 58 | int Trace::printToTrace(char *keyMsg, char *valueMsg) { 59 | 60 | int rc = SUCCESS; // Return code 61 | 62 | fprintf(logF, "%s : %s\n", keyMsg, valueMsg); 63 | fflush(logF); 64 | 65 | return rc; 66 | } 67 | 68 | /***************************************************************** 69 | * NAME: traceFileClose 70 | * 71 | * DESCRIPTION: Function takes care of closing log 72 | * 73 | * RETURN: 74 | * (int) SUCCESS 75 | * ERROR otherwise 76 | * 77 | ****************************************************************/ 78 | int Trace::traceFileClose() { 79 | 80 | int rc = SUCCESS; 81 | 82 | fclose(logF); 83 | 84 | return rc; 85 | } 86 | 87 | /***************************************************************** 88 | * NAME: funcEntry 89 | * 90 | * DESCRIPTION: Logs function entry 91 | * 92 | * PARAMETERS: 93 | * (char *) keyMsg - ipAddress 94 | * (char *) valueMsg - funcName 95 | * 96 | * RETURN: 97 | * (int) ZERO if success 98 | * ERROR otherwise 99 | * 100 | ****************************************************************/ 101 | int Trace::funcEntry(char *funcName) { 102 | 103 | int rc = SUCCESS; // Return code 104 | 105 | fprintf(logF, "ENTRY - %s\n", funcName); 106 | fflush(logF); 107 | 108 | return rc; 109 | } 110 | 111 | /***************************************************************** 112 | * NAME: funcExit 113 | * 114 | * DESCRIPTION: Logs function exit 115 | * 116 | * PARAMETERS: 117 | * (char *) keyMsg - ipAddress 118 | * (char *) valueMsg - funcName 119 | * (int) f_rc - return code of function 120 | * 121 | * RETURN: 122 | * (int) ZERO if success 123 | * ERROR otherwise 124 | * 125 | ****************************************************************/ 126 | int Trace::funcExit(char *funcName, int f_rc) { 127 | 128 | int rc = SUCCESS; // Return code 129 | 130 | fprintf(logF, "EXIT - %s with rc = %d\n", funcName, f_rc); 131 | fflush(logF); 132 | 133 | return rc; 134 | } 135 | -------------------------------------------------------------------------------- /submit.py: -------------------------------------------------------------------------------- 1 | #********************** 2 | #* 3 | #* Progam Name: MP1. Membership Protocol. 4 | #* 5 | #* Current file: submit.py 6 | #* About this file: Submission python script. 7 | #* 8 | #*********************** 9 | 10 | import urllib 11 | import urllib.request 12 | # import urllib2 13 | import hashlib 14 | import random 15 | import email 16 | import email.message 17 | import email.encoders 18 | # import StringIO 19 | import sys 20 | import subprocess 21 | import json 22 | import os 23 | 24 | try: 25 | from StringIO import StringIO 26 | except ImportError: 27 | from io import StringIO 28 | 29 | 30 | """""""""""""""""""" 31 | """""""""""""""""""" 32 | class NullDevice: 33 | def write(self, s): 34 | pass 35 | 36 | def submit(): 37 | print ('==\n== [sandbox] Submitting Solutions \n==') 38 | 39 | (login, password) = loginPrompt() 40 | if not login: 41 | print ('!! Submission Cancelled') 42 | return 43 | 44 | 45 | output = subprocess.Popen(['sh', 'run.sh', str(0)]).communicate()[0] 46 | submissions = [source(i) for i in range(4)] 47 | submitSolution(login, password, submissions) 48 | 49 | 50 | 51 | # =========================== LOGIN HELPERS - NO NEED TO CONFIGURE THIS ======================================= 52 | 53 | def loginPrompt(): 54 | """Prompt the user for login credentials. Returns a tuple (login, password).""" 55 | (login, password) = basicPrompt() 56 | return login, password 57 | 58 | 59 | def basicPrompt(): 60 | """Prompt the user for login credentials. Returns a tuple (login, password).""" 61 | login = input('Login (Email address): ') 62 | password = input('One-time Password (from the assignment page. This is NOT your own account\'s password): ') 63 | return login, password 64 | 65 | def partPrompt(): 66 | print ('Hello! These are the assignment parts that you can submit:') 67 | counter = 0 68 | for part in partFriendlyNames: 69 | counter += 1 70 | print (str(counter) + ') ' + partFriendlyNames[counter - 1]) 71 | partIdx = int(input('Please enter which part you want to submit (1-' + str(counter) + '): ')) - 1 72 | return (partIdx, partIds[partIdx]) 73 | 74 | 75 | 76 | def submit_url(): 77 | """Returns the submission url.""" 78 | return "https://www.coursera.org/api/onDemandProgrammingScriptSubmissions.v1" 79 | 80 | def submitSolution(email_address, password, submissions): 81 | """Submits a solution to the server. Returns (result, string).""" 82 | values = { 83 | "assignmentKey": akey, \ 84 | "submitterEmail": email_address, \ 85 | "secret": password, \ 86 | "parts": { 87 | partIds[0]: { 88 | "output": submissions[0] 89 | }, 90 | partIds[1]: { 91 | "output": submissions[1] 92 | }, 93 | partIds[2]: { 94 | "output": submissions[2] 95 | }, 96 | partIds[3]: { 97 | "output": submissions[3] 98 | } 99 | } 100 | } 101 | url = submit_url() 102 | data = json.dumps(values).encode('utf-8') 103 | req = urllib.request.Request(url) 104 | req.add_header('Content-Type', 'application/json') 105 | req.add_header('Cache-Control', 'no-cache') 106 | response = urllib.request.urlopen(req, data) 107 | return 108 | 109 | ## This collects the source code (just for logging purposes) 110 | def source(partIdx): 111 | # open the file, get all lines 112 | f = open("dbg.%d.log" % partIdx) 113 | src = f.read() 114 | f.close() 115 | return src 116 | 117 | def cleanup(): 118 | for i in range(4): 119 | try: 120 | os.remove("dbg.%s.log" % i) 121 | except: 122 | pass 123 | 124 | 125 | 126 | akey = 'Lm64BvbLEeWEJw5JS44kjw' 127 | 128 | # the "Identifier" you used when creating the part 129 | partIds = ['PH3Q7', 'PIXym', 'mUKdC', 'peNB6'] 130 | # used to generate readable run-time information for students 131 | partFriendlyNames = ['Create Test', 'Delete Test', 'Read Test', 'Update Test'] 132 | # source files to collect (just for our records) 133 | submit() 134 | 135 | cleanup() 136 | 137 | -------------------------------------------------------------------------------- /Member.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Member.cpp 3 | * 4 | * DESCRIPTION: Definition of all Member related class 5 | **********************************/ 6 | 7 | #include "Member.h" 8 | 9 | /** 10 | * Constructor 11 | */ 12 | q_elt::q_elt(void *elt, int size): elt(elt), size(size) {} 13 | 14 | /** 15 | * Copy constructor 16 | */ 17 | Address::Address(const Address &anotherAddress) { 18 | // strcpy(addr, anotherAddress.addr); 19 | memcpy(&addr, &anotherAddress.addr, sizeof(addr)); 20 | } 21 | 22 | /** 23 | * Assignment operator overloading 24 | */ 25 | Address& Address::operator =(const Address& anotherAddress) { 26 | // strcpy(addr, anotherAddress.addr); 27 | memcpy(&addr, &anotherAddress.addr, sizeof(addr)); 28 | return *this; 29 | } 30 | 31 | /** 32 | * Compare two Address objects 33 | */ 34 | bool Address::operator ==(const Address& anotherAddress) { 35 | return !memcmp(this->addr, anotherAddress.addr, sizeof(this->addr)); 36 | } 37 | 38 | /** 39 | * Constructor 40 | */ 41 | MemberListEntry::MemberListEntry(int id, short port, long heartbeat, long timestamp): id(id), port(port), heartbeat(heartbeat), timestamp(timestamp) {} 42 | 43 | /** 44 | * Constuctor 45 | */ 46 | MemberListEntry::MemberListEntry(int id, short port): id(id), port(port) {} 47 | 48 | /** 49 | * Copy constructor 50 | */ 51 | MemberListEntry::MemberListEntry(const MemberListEntry &anotherMLE) { 52 | this->heartbeat = anotherMLE.heartbeat; 53 | this->id = anotherMLE.id; 54 | this->port = anotherMLE.port; 55 | this->timestamp = anotherMLE.timestamp; 56 | } 57 | 58 | /** 59 | * Assignment operator overloading 60 | */ 61 | MemberListEntry& MemberListEntry::operator =(const MemberListEntry &anotherMLE) { 62 | MemberListEntry temp(anotherMLE); 63 | swap(heartbeat, temp.heartbeat); 64 | swap(id, temp.id); 65 | swap(port, temp.port); 66 | swap(timestamp, temp.timestamp); 67 | return *this; 68 | } 69 | 70 | /** 71 | * FUNCTION NAME: getid 72 | * 73 | * DESCRIPTION: getter 74 | */ 75 | int MemberListEntry::getid() { 76 | return id; 77 | } 78 | 79 | /** 80 | * FUNCTION NAME: getport 81 | * 82 | * DESCRIPTION: getter 83 | */ 84 | short MemberListEntry::getport() { 85 | return port; 86 | } 87 | 88 | /** 89 | * FUNCTION NAME: getheartbeat 90 | * 91 | * DESCRIPTION: getter 92 | */ 93 | long MemberListEntry::getheartbeat() { 94 | return heartbeat; 95 | } 96 | 97 | /** 98 | * FUNCTION NAME: gettimestamp 99 | * 100 | * DESCRIPTION: getter 101 | */ 102 | long MemberListEntry::gettimestamp() { 103 | return timestamp; 104 | } 105 | 106 | /** 107 | * FUNCTION NAME: setid 108 | * 109 | * DESCRIPTION: setter 110 | */ 111 | void MemberListEntry::setid(int id) { 112 | this->id = id; 113 | } 114 | 115 | /** 116 | * FUNCTION NAME: setport 117 | * 118 | * DESCRIPTION: setter 119 | */ 120 | void MemberListEntry::setport(short port) { 121 | this->port = port; 122 | } 123 | 124 | /** 125 | * FUNCTION NAME: setheartbeat 126 | * 127 | * DESCRIPTION: setter 128 | */ 129 | void MemberListEntry::setheartbeat(long hearbeat) { 130 | this->heartbeat = hearbeat; 131 | } 132 | 133 | /** 134 | * FUNCTION NAME: settimestamp 135 | * 136 | * DESCRIPTION: setter 137 | */ 138 | void MemberListEntry::settimestamp(long timestamp) { 139 | this->timestamp = timestamp; 140 | } 141 | 142 | /** 143 | * Copy Constructor 144 | */ 145 | Member::Member(const Member &anotherMember) { 146 | this->addr = anotherMember.addr; 147 | this->inited = anotherMember.inited; 148 | this->inGroup = anotherMember.inGroup; 149 | this->bFailed = anotherMember.bFailed; 150 | this->nnb = anotherMember.nnb; 151 | this->heartbeat = anotherMember.heartbeat; 152 | this->pingCounter = anotherMember.pingCounter; 153 | this->timeOutCounter = anotherMember.timeOutCounter; 154 | this->memberList = anotherMember.memberList; 155 | this->myPos = anotherMember.myPos; 156 | this->mp1q = anotherMember.mp1q; 157 | this->mp2q = anotherMember.mp2q; 158 | } 159 | 160 | /** 161 | * Assignment operator overloading 162 | */ 163 | Member& Member::operator =(const Member& anotherMember) { 164 | this->addr = anotherMember.addr; 165 | this->inited = anotherMember.inited; 166 | this->inGroup = anotherMember.inGroup; 167 | this->bFailed = anotherMember.bFailed; 168 | this->nnb = anotherMember.nnb; 169 | this->heartbeat = anotherMember.heartbeat; 170 | this->pingCounter = anotherMember.pingCounter; 171 | this->timeOutCounter = anotherMember.timeOutCounter; 172 | this->memberList = anotherMember.memberList; 173 | this->myPos = anotherMember.myPos; 174 | this->mp1q = anotherMember.mp1q; 175 | this->mp2q = anotherMember.mp2q; 176 | return *this; 177 | } 178 | -------------------------------------------------------------------------------- /Message.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Message.cpp 3 | * 4 | * DESCRIPTION: Message class definition 5 | **********************************/ 6 | #include "Message.h" 7 | 8 | /** 9 | * Constructor 10 | */ 11 | // transID::fromAddr::CREATE::key::value::ReplicaType 12 | // transID::fromAddr::READ::key 13 | // transID::fromAddr::UPDATE::key::value::ReplicaType 14 | // transID::fromAddr::DELETE::key 15 | // transID::fromAddr::REPLY::sucess 16 | // transID::fromAddr::READREPLY::value 17 | Message::Message(string message){ 18 | this->delimiter = "::"; 19 | vector tuple; 20 | size_t pos = message.find(delimiter); 21 | size_t start = 0; 22 | while (pos != string::npos) { 23 | string field = message.substr(start, pos-start); 24 | tuple.push_back(field); 25 | start = pos + 2; 26 | pos = message.find(delimiter, start); 27 | } 28 | tuple.push_back(message.substr(start)); 29 | 30 | transID = stoi(tuple.at(0)); 31 | Address addr(tuple.at(1)); 32 | fromAddr = addr; 33 | type = static_cast(stoi(tuple.at(2))); 34 | switch(type){ 35 | case CREATE: 36 | case UPDATE: 37 | key = tuple.at(3); 38 | value = tuple.at(4); 39 | if (tuple.size() > 5) 40 | replica = static_cast(stoi(tuple.at(5))); 41 | break; 42 | case READ: 43 | case DELETE: 44 | key = tuple.at(3); 45 | break; 46 | case REPLY: 47 | if (tuple.at(3) == "1") 48 | success = true; 49 | else 50 | success = false; 51 | break; 52 | case READREPLY: 53 | value = tuple.at(3); 54 | break; 55 | } 56 | } 57 | 58 | /** 59 | * Constructor 60 | */ 61 | // construct a create or update message 62 | Message::Message(int _transID, Address _fromAddr, MessageType _type, string _key, string _value, ReplicaType _replica){ 63 | this->delimiter = "::"; 64 | transID = _transID; 65 | fromAddr = _fromAddr; 66 | type = _type; 67 | key = _key; 68 | value = _value; 69 | replica = _replica; 70 | } 71 | 72 | /** 73 | * Constructor 74 | */ 75 | Message::Message(const Message& anotherMessage) { 76 | this->delimiter = anotherMessage.delimiter; 77 | this->fromAddr = anotherMessage.fromAddr; 78 | this->key = anotherMessage.key; 79 | this->replica = anotherMessage.replica; 80 | this->success = anotherMessage.success; 81 | this->transID = anotherMessage.transID; 82 | this->type = anotherMessage.type; 83 | this->value = anotherMessage.value; 84 | } 85 | 86 | /** 87 | * Constructor 88 | */ 89 | Message::Message(int _transID, Address _fromAddr, MessageType _type, string _key, string _value){ 90 | this->delimiter = "::"; 91 | transID = _transID; 92 | fromAddr = _fromAddr; 93 | type = _type; 94 | key = _key; 95 | value = _value; 96 | } 97 | 98 | /** 99 | * Constructor 100 | */ 101 | // construct a read or delete message 102 | Message::Message(int _transID, Address _fromAddr, MessageType _type, string _key){ 103 | this->delimiter = "::"; 104 | transID = _transID; 105 | fromAddr = _fromAddr; 106 | type = _type; 107 | key = _key; 108 | } 109 | 110 | /** 111 | * Constructor 112 | */ 113 | // construct reply message 114 | Message::Message(int _transID, Address _fromAddr, MessageType _type, bool _success){ 115 | this->delimiter = "::"; 116 | transID = _transID; 117 | fromAddr = _fromAddr; 118 | type = _type; 119 | success = _success; 120 | } 121 | 122 | /** 123 | * Constructor 124 | */ 125 | // construct read reply message 126 | Message::Message(int _transID, Address _fromAddr, string _value){ 127 | this->delimiter = "::"; 128 | transID = _transID; 129 | fromAddr = _fromAddr; 130 | type = READREPLY; 131 | value = _value; 132 | } 133 | 134 | /** 135 | * FUNCTION NAME: toString 136 | * 137 | * DESCRIPTION: Serialized Message in string format 138 | */ 139 | string Message::toString(){ 140 | string message = to_string(transID) + delimiter + fromAddr.getAddress() + delimiter + to_string(type) + delimiter; 141 | switch(type){ 142 | case CREATE: 143 | case UPDATE: 144 | message += key + delimiter + value + delimiter + to_string(replica); 145 | break; 146 | case READ: 147 | case DELETE: 148 | message += key; 149 | break; 150 | case REPLY: 151 | if (success) 152 | message += "1"; 153 | else 154 | message += "0"; 155 | break; 156 | case READREPLY: 157 | message += value; 158 | break; 159 | } 160 | return message; 161 | } 162 | 163 | /** 164 | * Assignment operator overloading 165 | */ 166 | Message& Message::operator =(const Message& anotherMessage) { 167 | this->delimiter = anotherMessage.delimiter; 168 | this->fromAddr = anotherMessage.fromAddr; 169 | this->key = anotherMessage.key; 170 | this->replica = anotherMessage.replica; 171 | this->success = anotherMessage.success; 172 | this->transID = anotherMessage.transID; 173 | this->type = anotherMessage.type; 174 | this->value = anotherMessage.value; 175 | return *this; 176 | } 177 | -------------------------------------------------------------------------------- /EmulNet.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: EmulNet.cpp 3 | * 4 | * DESCRIPTION: Emulated Network classes definition 5 | **********************************/ 6 | 7 | #include "EmulNet.h" 8 | 9 | /** 10 | * Constructor 11 | */ 12 | EmulNet::EmulNet(Params *p) 13 | { 14 | //trace.funcEntry("EmulNet::EmulNet"); 15 | int i,j; 16 | par = p; 17 | emulnet.setNextId(1); 18 | emulnet.settCurrBuffSize(0); 19 | enInited=0; 20 | for ( i = 0; i < MAX_NODES; i++ ) { 21 | for ( j = 0; j < MAX_TIME; j++ ) { 22 | sent_msgs[i][j] = 0; 23 | recv_msgs[i][j] = 0; 24 | } 25 | } 26 | //trace.funcExit("EmulNet::EmulNet", SUCCESS); 27 | } 28 | 29 | /** 30 | * Copy constructor 31 | */ 32 | EmulNet::EmulNet(EmulNet &anotherEmulNet) { 33 | int i, j; 34 | this->par = anotherEmulNet.par; 35 | this->enInited = anotherEmulNet.enInited; 36 | for ( i = 0; i < MAX_NODES; i++ ) { 37 | for ( j = 0; j < MAX_TIME; j++ ) { 38 | this->sent_msgs[i][j] = anotherEmulNet.sent_msgs[i][j]; 39 | this->recv_msgs[i][j] = anotherEmulNet.recv_msgs[i][j]; 40 | } 41 | } 42 | this->emulnet = anotherEmulNet.emulnet; 43 | } 44 | 45 | /** 46 | * Assignment operator overloading 47 | */ 48 | EmulNet& EmulNet::operator =(EmulNet &anotherEmulNet) { 49 | int i, j; 50 | this->par = anotherEmulNet.par; 51 | this->enInited = anotherEmulNet.enInited; 52 | for ( i = 0; i < MAX_NODES; i++ ) { 53 | for ( j = 0; j < MAX_TIME; j++ ) { 54 | this->sent_msgs[i][j] = anotherEmulNet.sent_msgs[i][j]; 55 | this->recv_msgs[i][j] = anotherEmulNet.recv_msgs[i][j]; 56 | } 57 | } 58 | this->emulnet = anotherEmulNet.emulnet; 59 | return *this; 60 | } 61 | 62 | /** 63 | * Destructor 64 | */ 65 | EmulNet::~EmulNet() {} 66 | 67 | /** 68 | * FUNCTION NAME: ENinit 69 | * 70 | * DESCRIPTION: Init the emulnet for this node 71 | */ 72 | void *EmulNet::ENinit(Address *myaddr, short port) { 73 | // Initialize data structures for this member 74 | *(int *)(myaddr->addr) = emulnet.nextid++; 75 | *(short *)(&myaddr->addr[4]) = 0; 76 | return myaddr; 77 | } 78 | 79 | /** 80 | * FUNCTION NAME: ENsend 81 | * 82 | * DESCRIPTION: EmulNet send function 83 | * 84 | * RETURNS: 85 | * size 86 | */ 87 | int EmulNet::ENsend(Address *myaddr, Address *toaddr, char *data, int size) { 88 | en_msg *em; 89 | static char temp[2048]; 90 | int sendmsg = rand() % 100; 91 | 92 | if( (emulnet.currbuffsize >= ENBUFFSIZE) || (size + (int)sizeof(en_msg) >= par->MAX_MSG_SIZE) || (par->dropmsg && sendmsg < (int) (par->MSG_DROP_PROB * 100)) ) { 93 | return 0; 94 | } 95 | 96 | em = (en_msg *)malloc(sizeof(en_msg) + size); 97 | em->size = size; 98 | 99 | memcpy(&(em->from.addr), &(myaddr->addr), sizeof(em->from.addr)); 100 | memcpy(&(em->to.addr), &(toaddr->addr), sizeof(em->from.addr)); 101 | memcpy(em + 1, data, size); 102 | 103 | emulnet.buff[emulnet.currbuffsize++] = em; 104 | 105 | int src = *(int *)(myaddr->addr); 106 | int time = par->getcurrtime(); 107 | 108 | assert(src <= MAX_NODES); 109 | assert(time < MAX_TIME); 110 | 111 | sent_msgs[src][time]++; 112 | 113 | #ifdef DEBUGLOG 114 | sprintf(temp, "Sending 4+%d B msg type %d to %d.%d.%d.%d:%d ", size-4, *(int *)data, toaddr->addr[0], toaddr->addr[1], toaddr->addr[2], toaddr->addr[3], *(short *)&toaddr->addr[4]); 115 | #endif 116 | 117 | return size; 118 | } 119 | 120 | /** 121 | * FUNCTION NAME: ENsend 122 | * 123 | * DESCRIPTION: EmulNet send function 124 | * 125 | * RETURNS: 126 | * size 127 | */ 128 | int EmulNet::ENsend(Address *myaddr, Address *toaddr, string data) { 129 | char * str = (char *) malloc(data.length() * sizeof(char)); 130 | memcpy(str, data.c_str(), data.size()); 131 | int ret = this->ENsend(myaddr, toaddr, str, (data.length() * sizeof(char))); 132 | free(str); 133 | return ret; 134 | } 135 | 136 | /** 137 | * FUNCTION NAME: ENrecv 138 | * 139 | * DESCRIPTION: EmulNet receive function 140 | * 141 | * RETURN: 142 | * 0 143 | */ 144 | int EmulNet::ENrecv(Address *myaddr, int (* enq)(void *, char *, int), struct timeval *t, int times, void *queue){ 145 | // times is always assumed to be 1 146 | int i; 147 | char* tmp; 148 | int sz; 149 | en_msg *emsg; 150 | 151 | for( i = emulnet.currbuffsize - 1; i >= 0; i-- ) { 152 | emsg = emulnet.buff[i]; 153 | 154 | if ( 0 == strcmp(emsg->to.addr, myaddr->addr) ) { 155 | sz = emsg->size; 156 | tmp = (char *) malloc(sz * sizeof(char)); 157 | memcpy(tmp, (char *)(emsg+1), sz); 158 | 159 | emulnet.buff[i] = emulnet.buff[emulnet.currbuffsize-1]; 160 | emulnet.currbuffsize--; 161 | 162 | (*enq)(queue, (char *)tmp, sz); 163 | 164 | free(emsg); 165 | 166 | int dst = *(int *)(myaddr->addr); 167 | int time = par->getcurrtime(); 168 | 169 | assert(dst <= MAX_NODES); 170 | assert(time < MAX_TIME); 171 | 172 | recv_msgs[dst][time]++; 173 | } 174 | } 175 | 176 | return 0; 177 | } 178 | 179 | /** 180 | * FUNCTION NAME: ENcleanup 181 | * 182 | * DESCRIPTION: Cleanup the EmulNet. Called exactly once at the end of the program. 183 | */ 184 | int EmulNet::ENcleanup() { 185 | emulnet.nextid=0; 186 | int i, j; 187 | int sent_total, recv_total; 188 | 189 | FILE* file = fopen("msgcount.log", "w+"); 190 | 191 | while(emulnet.currbuffsize > 0) { 192 | free(emulnet.buff[--emulnet.currbuffsize]); 193 | } 194 | 195 | for ( i = 1; i <= par->EN_GPSZ; i++ ) { 196 | fprintf(file, "node %3d ", i); 197 | sent_total = 0; 198 | recv_total = 0; 199 | 200 | for (j = 0; j < par->getcurrtime(); j++) { 201 | 202 | sent_total += sent_msgs[i][j]; 203 | recv_total += recv_msgs[i][j]; 204 | if (i != 67) { 205 | fprintf(file, " (%4d, %4d)", sent_msgs[i][j], recv_msgs[i][j]); 206 | if (j % 10 == 9) { 207 | fprintf(file, "\n "); 208 | } 209 | } 210 | else { 211 | fprintf(file, "special %4d %4d %4d\n", j, sent_msgs[i][j], recv_msgs[i][j]); 212 | } 213 | } 214 | fprintf(file, "\n"); 215 | fprintf(file, "node %3d sent_total %6u recv_total %6u\n\n", i, sent_total, recv_total); 216 | } 217 | 218 | fclose(file); 219 | return 0; 220 | } 221 | -------------------------------------------------------------------------------- /Log.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Log.h 3 | * 4 | * DESCRIPTION: Log class definition 5 | **********************************/ 6 | 7 | #include "Log.h" 8 | 9 | /** 10 | * Constructor 11 | */ 12 | Log::Log(Params *p) { 13 | par = p; 14 | firstTime = false; 15 | } 16 | 17 | /** 18 | * Copy constructor 19 | */ 20 | Log::Log(const Log &anotherLog) { 21 | this->par = anotherLog.par; 22 | this->firstTime = anotherLog.firstTime; 23 | } 24 | 25 | /** 26 | * Assignment Operator Overloading 27 | */ 28 | Log& Log::operator = (const Log& anotherLog) { 29 | this->par = anotherLog.par; 30 | this->firstTime = anotherLog.firstTime; 31 | return *this; 32 | } 33 | 34 | /** 35 | * Destructor 36 | */ 37 | Log::~Log() {} 38 | 39 | /** 40 | * FUNCTION NAME: LOG 41 | * 42 | * DESCRIPTION: Print out to file dbg.log, along with Address of node. 43 | */ 44 | void Log::LOG(Address *addr, const char * str, ...) { 45 | 46 | static FILE *fp; 47 | static FILE *fp2; 48 | va_list vararglist; 49 | static char buffer[30000]; 50 | static int numwrites; 51 | static char stdstring[30]; 52 | static char stdstring2[40]; 53 | static char stdstring3[40]; 54 | static int dbg_opened=0; 55 | 56 | if(dbg_opened != 639){ 57 | numwrites=0; 58 | 59 | stdstring2[0]=0; 60 | 61 | strcpy(stdstring3, stdstring2); 62 | 63 | strcat(stdstring2, DBG_LOG); 64 | strcat(stdstring3, STATS_LOG); 65 | 66 | fp = fopen(stdstring2, "w"); 67 | fp2 = fopen(stdstring3, "w"); 68 | 69 | dbg_opened=639; 70 | } 71 | else 72 | 73 | sprintf(stdstring, "%d.%d.%d.%d:%d ", addr->addr[0], addr->addr[1], addr->addr[2], addr->addr[3], *(short *)&addr->addr[4]); 74 | 75 | va_start(vararglist, str); 76 | vsprintf(buffer, str, vararglist); 77 | va_end(vararglist); 78 | 79 | if (!firstTime) { 80 | int magicNumber = 0; 81 | string magic = MAGIC_NUMBER; 82 | int len = magic.length(); 83 | for ( int i = 0; i < len; i++ ) { 84 | magicNumber += (int)magic.at(i); 85 | } 86 | fprintf(fp, "%x\n", magicNumber); 87 | firstTime = true; 88 | } 89 | 90 | if(memcmp(buffer, "#STATSLOG#", 10)==0){ 91 | fprintf(fp2, "\n %s", stdstring); 92 | fprintf(fp2, "[%d] ", par->getcurrtime()); 93 | 94 | fprintf(fp2, buffer); 95 | } 96 | else{ 97 | fprintf(fp, "\n %s", stdstring); 98 | fprintf(fp, "[%d] ", par->getcurrtime()); 99 | fprintf(fp, buffer); 100 | 101 | } 102 | 103 | if(++numwrites >= MAXWRITES){ 104 | fflush(fp); 105 | fflush(fp2); 106 | numwrites=0; 107 | } 108 | 109 | } 110 | 111 | /** 112 | * FUNCTION NAME: logNodeAdd 113 | * 114 | * DESCRIPTION: To Log a node add 115 | */ 116 | void Log::logNodeAdd(Address *thisNode, Address *addedAddr) { 117 | static char stdstring[100]; 118 | sprintf(stdstring, "Node %d.%d.%d.%d:%d joined at time %d", addedAddr->addr[0], addedAddr->addr[1], addedAddr->addr[2], addedAddr->addr[3], *(short *)&addedAddr->addr[4], par->getcurrtime()); 119 | LOG(thisNode, stdstring); 120 | } 121 | 122 | /** 123 | * FUNCTION NAME: logNodeRemove 124 | * 125 | * DESCRIPTION: To log a node remove 126 | */ 127 | void Log::logNodeRemove(Address *thisNode, Address *removedAddr) { 128 | static char stdstring[30]; 129 | sprintf(stdstring, "Node %d.%d.%d.%d:%d removed at time %d", removedAddr->addr[0], removedAddr->addr[1], removedAddr->addr[2], removedAddr->addr[3], *(short *)&removedAddr->addr[4], par->getcurrtime()); 130 | LOG(thisNode, stdstring); 131 | } 132 | 133 | /** 134 | * FUNCTION NAME: logCreateSuccess 135 | * 136 | * DESCRTION: Call this function after successfully create a key value pair 137 | */ 138 | void Log::logCreateSuccess(Address * address, bool isCoordinator, int transID, string key, string value){ 139 | static char stdstring[100]; 140 | string str; 141 | if (isCoordinator) 142 | str = "coordinator"; 143 | else 144 | str = "server"; 145 | sprintf(stdstring, "%s: create success at time %d, transID=%d, key=%s, value=%s", str.c_str(), par->getcurrtime(), transID, key.c_str(), value.c_str()); 146 | LOG(address, stdstring); 147 | } 148 | 149 | /** 150 | * FUNCTION NAME: logReadSuccess 151 | * 152 | * DESCRIPTION: Call this function after successfully reading a key 153 | */ 154 | void Log::logReadSuccess(Address * address, bool isCoordinator, int transID, string key, string value){ 155 | static char stdstring[100]; 156 | string str; 157 | if (isCoordinator) 158 | str = "coordinator"; 159 | else 160 | str = "server"; 161 | sprintf(stdstring, "%s: read success at time %d, transID=%d, key=%s, value=%s", str.c_str(), par->getcurrtime(), transID, key.c_str(), value.c_str()); 162 | LOG(address, stdstring); 163 | } 164 | 165 | /** 166 | * FUNCTION NAME: logUpdateSuccess 167 | * 168 | * DESCRIPTION: Call this function after successfully updating a key 169 | */ 170 | void Log::logUpdateSuccess(Address * address, bool isCoordinator, int transID, string key, string newValue){ 171 | static char stdstring[100]; 172 | string str; 173 | if (isCoordinator) 174 | str = "coordinator"; 175 | else 176 | str = "server"; 177 | sprintf(stdstring, "%s: update success at time %d, transID=%d, key=%s, value=%s", str.c_str(), par->getcurrtime(), transID, key.c_str(), newValue.c_str()); 178 | LOG(address, stdstring); 179 | } 180 | 181 | /** 182 | * FUNCTION NAME: logDeleteSuccess 183 | * 184 | * DESCRIPTION: Call this function after successfully deleting a key 185 | */ 186 | void Log::logDeleteSuccess(Address * address, bool isCoordinator, int transID, string key){ 187 | static char stdstring[100]; 188 | string str; 189 | if (isCoordinator) 190 | str = "coordinator"; 191 | else 192 | str = "server"; 193 | sprintf(stdstring, "%s: delete success at time %d, transID=%d, key=%s", str.c_str(), par->getcurrtime(), transID, key.c_str()); 194 | LOG(address, stdstring); 195 | } 196 | 197 | /** 198 | * FUNCTION NAME: logCreateFail 199 | * 200 | * DESCRIPTION: Call this function if CREATE failed 201 | */ 202 | void Log::logCreateFail(Address * address, bool isCoordinator, int transID, string key, string value){ 203 | static char stdstring[100]; 204 | string str; 205 | if (isCoordinator) 206 | str = "coordinator"; 207 | else 208 | str = "server"; 209 | sprintf(stdstring, "%s: create fail at time %d, transID=%d, key=%s, value=%s", str.c_str(), par->getcurrtime(), transID, key.c_str(), value.c_str()); 210 | LOG(address, stdstring); 211 | } 212 | 213 | 214 | /** 215 | * FUNCTION NAME: logReadFail 216 | * 217 | * DESCRIPTION: Call this function if READ failed 218 | */ 219 | void Log::logReadFail(Address * address, bool isCoordinator, int transID, string key){ 220 | static char stdstring[100]; 221 | string str; 222 | if (isCoordinator) 223 | str = "coordinator"; 224 | else 225 | str = "server"; 226 | sprintf(stdstring, "%s: read fail at time %d, transID=%d, key=%s", str.c_str(), par->getcurrtime(), transID, key.c_str()); 227 | LOG(address, stdstring); 228 | } 229 | 230 | /** 231 | * FUNCTION NAME: logUpdateFail 232 | * 233 | * DESCRIPTION: Call this function if UPDATE failed 234 | */ 235 | void Log::logUpdateFail(Address * address, bool isCoordinator, int transID, string key, string newValue){ 236 | static char stdstring[100]; 237 | string str; 238 | if (isCoordinator) 239 | str = "coordinator"; 240 | else 241 | str = "server"; 242 | sprintf(stdstring, "%s: update fail at time %d, transID=%d, key=%s, value=%s", str.c_str(), par->getcurrtime(), transID, key.c_str(), newValue.c_str()); 243 | LOG(address, stdstring); 244 | } 245 | 246 | /** 247 | * FUNCTION NAME: logDeleteFail 248 | * 249 | * DESCRIPTION: Call this function if DELETE failed 250 | */ 251 | void Log::logDeleteFail(Address * address, bool isCoordinator, int transID, string key){ 252 | static char stdstring[100]; 253 | string str; 254 | if (isCoordinator) 255 | str = "coordinator"; 256 | else 257 | str = "server"; 258 | sprintf(stdstring, "%s: delete fail at time %d, transID=%d, key=%s", str.c_str(), par->getcurrtime(), transID, key.c_str()); 259 | LOG(address, stdstring); 260 | } 261 | -------------------------------------------------------------------------------- /MP1Node.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: MP1Node.cpp 3 | * 4 | * DESCRIPTION: Membership protocol run by this Node. 5 | * Definition of MP1Node class functions. 6 | **********************************/ 7 | 8 | #include "MP1Node.h" 9 | 10 | /* 11 | * Note: You can change/add any functions in MP1Node.{h,cpp} 12 | */ 13 | 14 | /** 15 | * Overloaded Constructor of the MP1Node class 16 | * You can add new members to the class if you think it 17 | * is necessary for your logic to work 18 | */ 19 | MP1Node::MP1Node(Member *member, Params *params, EmulNet *emul, Log *log, Address *address) { 20 | for( int i = 0; i < 6; i++ ) { 21 | NULLADDR[i] = 0; 22 | } 23 | this->memberNode = member; 24 | this->emulNet = emul; 25 | this->log = log; 26 | this->par = params; 27 | this->memberNode->addr = *address; 28 | } 29 | 30 | /** 31 | * Destructor of the MP1Node class 32 | */ 33 | MP1Node::~MP1Node() {} 34 | 35 | /** 36 | * FUNCTION NAME: recvLoop 37 | * 38 | * DESCRIPTION: This function receives message from the network and pushes into the queue 39 | * This function is called by a node to receive messages currently waiting for it 40 | */ 41 | int MP1Node::recvLoop() { 42 | if ( memberNode->bFailed ) { 43 | return false; 44 | } 45 | else { 46 | return emulNet->ENrecv(&(memberNode->addr), enqueueWrapper, NULL, 1, &(memberNode->mp1q)); 47 | } 48 | } 49 | 50 | /** 51 | * FUNCTION NAME: enqueueWrapper 52 | * 53 | * DESCRIPTION: Enqueue the message from Emulnet into the queue 54 | */ 55 | int MP1Node::enqueueWrapper(void *env, char *buff, int size) { 56 | Queue q; 57 | return q.enqueue((queue *)env, (void *)buff, size); 58 | } 59 | 60 | /** 61 | * FUNCTION NAME: nodeStart 62 | * 63 | * DESCRIPTION: This function bootstraps the node 64 | * All initializations routines for a member. 65 | * Called by the application layer. 66 | */ 67 | void MP1Node::nodeStart(char *servaddrstr, short servport) { 68 | Address joinaddr; 69 | joinaddr = getJoinAddress(); 70 | 71 | // Self booting routines 72 | if( initThisNode(&joinaddr) == -1 ) { 73 | #ifdef DEBUGLOG 74 | log->LOG(&memberNode->addr, "init_thisnode failed. Exit."); 75 | #endif 76 | exit(1); 77 | } 78 | 79 | if( !introduceSelfToGroup(&joinaddr) ) { 80 | finishUpThisNode(); 81 | #ifdef DEBUGLOG 82 | log->LOG(&memberNode->addr, "Unable to join self to group. Exiting."); 83 | #endif 84 | exit(1); 85 | } 86 | 87 | return; 88 | } 89 | 90 | /** 91 | * FUNCTION NAME: initThisNode 92 | * 93 | * DESCRIPTION: Find out who I am and start up 94 | */ 95 | int MP1Node::initThisNode(Address *joinaddr) { 96 | /* 97 | * This function is partially implemented and may require changes 98 | */ 99 | int id = *(int*)(&memberNode->addr.addr); 100 | int port = *(short*)(&memberNode->addr.addr[4]); 101 | 102 | memberNode->bFailed = false; 103 | memberNode->inited = true; 104 | memberNode->inGroup = false; 105 | // node is up! 106 | memberNode->nnb = 0; 107 | memberNode->heartbeat = 0; 108 | memberNode->pingCounter = TFAIL; 109 | memberNode->timeOutCounter = -1; 110 | initMemberListTable(memberNode); 111 | 112 | return 0; 113 | } 114 | 115 | /** 116 | * FUNCTION NAME: introduceSelfToGroup 117 | * 118 | * DESCRIPTION: Join the distributed system 119 | */ 120 | int MP1Node::introduceSelfToGroup(Address *joinaddr) { 121 | MessageHdr *msg; 122 | #ifdef DEBUGLOG 123 | static char s[1024]; 124 | #endif 125 | 126 | if ( 0 == memcmp((char *)&(memberNode->addr.addr), (char *)&(joinaddr->addr), sizeof(memberNode->addr.addr))) { 127 | // I am the group booter (first process to join the group). Boot up the group 128 | #ifdef DEBUGLOG 129 | log->LOG(&memberNode->addr, "Starting up group..."); 130 | #endif 131 | memberNode->inGroup = true; 132 | } 133 | else { 134 | 135 | // Create JOINREQ message 136 | msg = new MessageHdr(); 137 | msg->msgType = JOINREQ; 138 | msg->member_vector = memberNode->memberList; 139 | msg->addr = &memberNode->addr; 140 | 141 | #ifdef DEBUGLOG 142 | sprintf(s, "Trying to join..."); 143 | log->LOG(&memberNode->addr, s); 144 | #endif 145 | 146 | // send JOINREQ message to introducer member 147 | emulNet->ENsend(&memberNode->addr, joinaddr, (char *)msg, sizeof(MessageHdr)); 148 | 149 | free(msg); 150 | } 151 | 152 | return 1; 153 | 154 | } 155 | 156 | /** 157 | * FUNCTION NAME: finishUpThisNode 158 | * 159 | * DESCRIPTION: Wind up this node and clean up state 160 | */ 161 | int MP1Node::finishUpThisNode(){ 162 | /* 163 | * Your code goes here 164 | */ 165 | } 166 | 167 | /** 168 | * FUNCTION NAME: nodeLoop 169 | * 170 | * DESCRIPTION: Executed periodically at each member 171 | * Check your messages in queue and perform membership protocol duties 172 | */ 173 | void MP1Node::nodeLoop() { 174 | if (memberNode->bFailed) { 175 | return; 176 | } 177 | 178 | // Check my messages 179 | checkMessages(); 180 | 181 | // Wait until you're in the group... 182 | if( !memberNode->inGroup ) { 183 | return; 184 | } 185 | 186 | // ...then jump in and share your responsibilites! 187 | nodeLoopOps(); 188 | 189 | return; 190 | } 191 | 192 | /** 193 | * FUNCTION NAME: checkMessages 194 | * 195 | * DESCRIPTION: Check messages in the queue and call the respective message handler 196 | */ 197 | void MP1Node::checkMessages() { 198 | void *ptr; 199 | int size; 200 | 201 | // Pop waiting messages from memberNode's mp1q 202 | while ( !memberNode->mp1q.empty() ) { 203 | ptr = memberNode->mp1q.front().elt; 204 | size = memberNode->mp1q.front().size; 205 | memberNode->mp1q.pop(); 206 | recvCallBack((void *)memberNode, (char *)ptr, size); 207 | } 208 | return; 209 | } 210 | 211 | /** 212 | * FUNCTION NAME: recvCallBack 213 | * 214 | * DESCRIPTION: Message handler for different message types 215 | */ 216 | bool MP1Node::recvCallBack(void *env, char *data, int size ) { 217 | MessageHdr* msg = (MessageHdr*) data; 218 | if(msg->msgType == JOINREQ){ 219 | // 1. add to memberlist 220 | push_member_list(msg); 221 | // 2. send JOINREP to source node 222 | Address* toaddr = msg->addr; 223 | // cout << "JOINREQ : from " << toaddr->getAddress() << " to " << memberNode->addr.getAddress() << endl; 224 | send_message(toaddr, JOINREP); 225 | }else if(msg->msgType == JOINREP){ 226 | // 1. add to memberlist 227 | // cout << "JOINREP : from " << msg->addr->getAddress() << " to " << memberNode->addr.getAddress() << endl; 228 | push_member_list(msg); 229 | // 2. set memberNode->inGroup = true 230 | memberNode->inGroup = true; 231 | }else if(msg->msgType == PING){ 232 | // 1. Check memberlist 233 | // cout << "PING : from " << msg->addr->getAddress() << " to " << memberNode->addr.getAddress() << endl; 234 | ping_handler(msg); 235 | } 236 | delete msg; 237 | return true; 238 | } 239 | /** 240 | * FUNCTION NAME: push_member_list 241 | * 242 | * DESCRIPTION: If a node does not exist in the memberList, it will be pushed to the memberList. 243 | */ 244 | void MP1Node::push_member_list(MessageHdr* msg) { 245 | // id, port, heartbeat, timestamp 246 | int id = 0; 247 | short port; 248 | memcpy(&id, &msg->addr->addr[0], sizeof(int)); 249 | memcpy(&port, &msg->addr->addr[4], sizeof(short)); 250 | long heartbeat = 1; 251 | long timestamp = this->par->getcurrtime(); 252 | if(check_member_list(id, port) != nullptr) 253 | return; 254 | MemberListEntry e(id, port, heartbeat, timestamp); 255 | memberNode->memberList.push_back(e); 256 | Address* added = get_address(id,port); 257 | log->logNodeAdd(&memberNode->addr ,added); 258 | delete added; 259 | } 260 | 261 | void MP1Node::push_member_list(MemberListEntry* e) { 262 | Address* addr = get_address(e->id, e->port); 263 | 264 | if (*addr == memberNode->addr) { 265 | delete addr; 266 | return; 267 | } 268 | 269 | if (par->getcurrtime() - e->timestamp < TREMOVE) { 270 | log->logNodeAdd(&memberNode->addr, addr); 271 | MemberListEntry new_entry = *e; 272 | memberNode->memberList.push_back(new_entry); 273 | } 274 | delete addr; 275 | } 276 | /** 277 | * FUNCTION NAME: get_address 278 | * 279 | * DESCRIPTION: return address 280 | */ 281 | Address* MP1Node::get_address(int id, short port) { 282 | Address* address = new Address(); 283 | memcpy(&address->addr[0], &id, sizeof(int)); 284 | memcpy(&address->addr[4], &port, sizeof(short)); 285 | return address; 286 | } 287 | 288 | /** 289 | * FUNCTION NAME: check_member_list 290 | * 291 | * DESCRIPTION: If the node exists in the memberList, the function will return true. Otherwise, the function will return false. 292 | */ 293 | MemberListEntry* MP1Node::check_member_list(int id, short port) { 294 | for (int i = 0; i < memberNode->memberList.size(); i++){ 295 | if(memberNode->memberList[i].id == id && memberNode->memberList[i].port == port) 296 | return &memberNode->memberList[i]; 297 | } 298 | return nullptr; 299 | } 300 | 301 | MemberListEntry* MP1Node::check_member_list(Address* node_addr) { 302 | for(int i = 0; i < memberNode->memberList.size(); i++) { 303 | int id = 0; 304 | short port = 0; 305 | memcpy(&id, &node_addr->addr[0], sizeof(int)); 306 | memcpy(&port, &node_addr->addr[4], sizeof(short)); 307 | if(memberNode->memberList[i].id == id && memberNode->memberList[i].port == port) 308 | return &memberNode->memberList[i]; 309 | } 310 | return nullptr; 311 | } 312 | 313 | /** 314 | * FUNCTION NAME: send_message 315 | * 316 | * DESCRIPTION: send message 317 | */ 318 | void MP1Node::send_message(Address* toaddr, MsgTypes t) { 319 | MessageHdr* msg = new MessageHdr(); 320 | msg->msgType = t; 321 | msg->member_vector = memberNode->memberList; 322 | msg->addr = &memberNode->addr; 323 | emulNet->ENsend( &memberNode->addr, toaddr, (char*)msg, sizeof(MessageHdr)); 324 | } 325 | 326 | /** 327 | * FUNCTION NAME: ping_handler 328 | * 329 | * DESCRIPTION: The function handles the ping messages. 330 | */ 331 | void MP1Node::ping_handler(MessageHdr* msg) { 332 | update_src_member(msg); 333 | for(int i=0; i < msg->member_vector.size(); i++){ 334 | // cout << " id : " << msg->member_vector[i].id << " , port : " << msg->member_vector[i].port << endl; 335 | if( msg->member_vector[i].id > 10 || msg->member_vector[i].id < 0) { 336 | assert(1!=1); 337 | } 338 | MemberListEntry* node = check_member_list(msg->member_vector[i].id, msg->member_vector[i].port); 339 | if(node != nullptr){ 340 | if(msg->member_vector[i].heartbeat > node->heartbeat){ 341 | node->heartbeat = msg->member_vector[i].heartbeat; 342 | node->timestamp = par->getcurrtime(); 343 | } 344 | }else{ 345 | push_member_list(&msg->member_vector[i]); 346 | } 347 | } 348 | } 349 | 350 | void MP1Node::update_src_member(MessageHdr* msg){ 351 | MemberListEntry* src_member = check_member_list(msg->addr); 352 | if(src_member != nullptr){ 353 | src_member->heartbeat ++; 354 | src_member->timestamp = par->getcurrtime(); 355 | }else{ 356 | push_member_list(msg); 357 | } 358 | } 359 | /** 360 | * FUNCTION NAME: nodeLoopOps 361 | * 362 | * DESCRIPTION: Check if any node hasn't responded within a timeout period and then delete 363 | * the nodes 364 | * Propagate your membership list 365 | */ 366 | void MP1Node::nodeLoopOps() { 367 | 368 | // Update heartbeat 369 | memberNode->heartbeat ++; 370 | // Check TREMOVE 371 | 372 | for (int i = memberNode->memberList.size()-1 ; i >= 0; i--) { 373 | if(par->getcurrtime() - memberNode->memberList[i].timestamp >= TREMOVE) { 374 | Address* removed_addr = get_address(memberNode->memberList[i].id, memberNode->memberList[i].port); 375 | log->logNodeRemove(&memberNode->addr, removed_addr); 376 | memberNode->memberList.erase(memberNode->memberList.begin()+i); 377 | delete removed_addr; 378 | } 379 | } 380 | 381 | // Send PING to the members of memberList 382 | for (int i = 0; i < memberNode->memberList.size(); i++) { 383 | Address* address = get_address(memberNode->memberList[i].id, memberNode->memberList[i].port); 384 | send_message(address, PING); 385 | delete address; 386 | } 387 | return; 388 | } 389 | 390 | /** 391 | * FUNCTION NAME: isNullAddress 392 | * 393 | * DESCRIPTION: Function checks if the address is NULL 394 | */ 395 | int MP1Node::isNullAddress(Address *addr) { 396 | return (memcmp(addr->addr, NULLADDR, 6) == 0 ? 1 : 0); 397 | } 398 | 399 | /** 400 | * FUNCTION NAME: getJoinAddress 401 | * 402 | * DESCRIPTION: Returns the Address of the coordinator 403 | */ 404 | Address MP1Node::getJoinAddress() { 405 | Address joinaddr; 406 | 407 | memset(&joinaddr, 0, sizeof(Address)); 408 | *(int *)(&joinaddr.addr) = 1; 409 | *(short *)(&joinaddr.addr[4]) = 0; 410 | 411 | return joinaddr; 412 | } 413 | 414 | /** 415 | * FUNCTION NAME: initMemberListTable 416 | * 417 | * DESCRIPTION: Initialize the membership list 418 | */ 419 | void MP1Node::initMemberListTable(Member *memberNode) { 420 | memberNode->memberList.clear(); 421 | } 422 | 423 | /** 424 | * FUNCTION NAME: printAddress 425 | * 426 | * DESCRIPTION: Print the Address 427 | */ 428 | void MP1Node::printAddress(Address *addr) 429 | { 430 | printf("%d.%d.%d.%d:%d \n", addr->addr[0],addr->addr[1],addr->addr[2], 431 | addr->addr[3], *(short*)&addr->addr[4]) ; 432 | } 433 | -------------------------------------------------------------------------------- /MP2Node.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: MP2Node.cpp 3 | * 4 | * DESCRIPTION: MP2Node class definition 5 | **********************************/ 6 | #include "MP2Node.h" 7 | 8 | /** 9 | * constructor 10 | */ 11 | MP2Node::MP2Node(Member *memberNode, Params *par, EmulNet * emulNet, Log * log, Address * address) { 12 | this->memberNode = memberNode; 13 | this->par = par; 14 | this->emulNet = emulNet; 15 | this->log = log; 16 | ht = new HashTable(); 17 | this->memberNode->addr = *address; 18 | } 19 | 20 | /** 21 | * Destructor 22 | */ 23 | MP2Node::~MP2Node() { 24 | delete ht; 25 | delete memberNode; 26 | map::iterator it = transMap.begin(); 27 | while(it != transMap.end()) { 28 | delete it->second; 29 | it++; 30 | } 31 | } 32 | 33 | /** 34 | * FUNCTION NAME: updateRing 35 | * 36 | * DESCRIPTION: This function does the following: 37 | * 1) Gets the current membership list from the Membership Protocol (MP1Node) 38 | * The membership list is returned as a vector of Nodes. See Node class in Node.h 39 | * 2) Constructs the ring based on the membership list 40 | * 3) Calls the Stabilization Protocol 41 | */ 42 | 43 | void MP2Node::updateRing() { 44 | /* 45 | * Implement this. Parts of it are already implemented 46 | */ 47 | vector curMemList; 48 | bool change = false; 49 | 50 | /* 51 | * Step 1. Get the current membership list from Membership Protocol / MP1 52 | */ 53 | curMemList = getMembershipList(); 54 | Node myself(this->memberNode->addr); 55 | curMemList.push_back(myself); 56 | 57 | /* 58 | * Step 2: Construct the ring 59 | */ 60 | // Sort the list based on the hashCode 61 | 62 | sort(curMemList.begin(), curMemList.end()); 63 | 64 | 65 | if(ring.size() != curMemList.size()){ 66 | change = true; 67 | }else if(ring.size() != 0){ 68 | for(int i = 0; i < ring.size(); i++){ 69 | if(curMemList[i].getHashCode() != ring[i].getHashCode()){ 70 | change = true; 71 | break; 72 | } 73 | } 74 | } 75 | 76 | /* 77 | * Step 3: Run the stabilization protocol IF REQUIRED 78 | */ 79 | 80 | // Run stabilization protocol if the hash table size is greater than zero and if there has been a changed in the ring 81 | 82 | ring = curMemList; 83 | if(change){ 84 | stabilizationProtocol(); 85 | } 86 | } 87 | 88 | 89 | /** 90 | * FUNCTION NAME: getMembershipList 91 | * 92 | * DESCRIPTION: This function goes through the membership list from the Membership protocol/MP1 and 93 | * i) generates the hash code for each member 94 | * ii) populates the ring member in MP2Node class 95 | * It returns a vector of Nodes. Each element in the vector contain the following fields: 96 | * a) Address of the node 97 | * b) Hash code obtained by consistent hashing of the Address 98 | */ 99 | vector MP2Node::getMembershipList() { 100 | unsigned int i; 101 | vector curMemList; 102 | for ( i = 0 ; i < this->memberNode->memberList.size(); i++ ) { 103 | Address addressOfThisMember; 104 | int id = this->memberNode->memberList.at(i).getid(); 105 | short port = this->memberNode->memberList.at(i).getport(); 106 | memcpy(&addressOfThisMember.addr[0], &id, sizeof(int)); 107 | memcpy(&addressOfThisMember.addr[4], &port, sizeof(short)); 108 | curMemList.emplace_back(Node(addressOfThisMember)); 109 | } 110 | return curMemList; 111 | } 112 | 113 | /** 114 | * FUNCTION NAME: hashFunction 115 | * 116 | * DESCRIPTION: This functions hashes the key and returns the position on the ring 117 | * HASH FUNCTION USED FOR CONSISTENT HASHING 118 | * 119 | * RETURNS: 120 | * size_t position on the ring 121 | */ 122 | size_t MP2Node::hashFunction(string key) { 123 | std::hash hashFunc; 124 | size_t ret = hashFunc(key); 125 | return ret%RING_SIZE; 126 | } 127 | 128 | /** 129 | * FUNCTION NAME: clientCreate 130 | * 131 | * DESCRIPTION: client side CREATE API 132 | * The function does the following: 133 | * 1) Constructs the message 134 | * 2) Finds the replicas of this key 135 | * 3) Sends a message to the replica 136 | */ 137 | void MP2Node::clientCreate(string key, string value) { 138 | vector replicas = findNodes(key); 139 | for (int i =0; i < replicas.size(); i++) { 140 | Message msg = constructMsg(MessageType::CREATE, key, value); 141 | // cout << "client create trans_id :" << msg.transID << " ; address : "<< memberNode->addr.getAddress() << endl; 142 | string data = msg.toString(); 143 | emulNet->ENsend(&memberNode->addr, replicas[i].getAddress(), data); 144 | } 145 | g_transID ++; 146 | 147 | } 148 | 149 | /** 150 | * FUNCTION NAME: clientRead 151 | * 152 | * DESCRIPTION: client side READ API 153 | * The function does the following: 154 | * 1) Constructs the message 155 | * 2) Finds the replicas of this key 156 | * 3) Sends a message to the replica 157 | */ 158 | void MP2Node::clientRead(string key){ 159 | vector replicas = findNodes(key); 160 | for (int i =0; i < replicas.size(); i++) { 161 | Message msg = constructMsg(MessageType::READ, key); 162 | string data = msg.toString(); 163 | emulNet->ENsend(&memberNode->addr, replicas[i].getAddress(), data); 164 | } 165 | g_transID ++; 166 | } 167 | 168 | /** 169 | * FUNCTION NAME: clientUpdate 170 | * 171 | * DESCRIPTION: client side UPDATE API 172 | * The function does the following: 173 | * 1) Constructs the message 174 | * 2) Finds the replicas of this key 175 | * 3) Sends a message to the replica 176 | */ 177 | void MP2Node::clientUpdate(string key, string value){ 178 | vector replicas = findNodes(key); 179 | for (int i =0; i < replicas.size(); i++) { 180 | Message msg = constructMsg(MessageType::UPDATE, key, value); 181 | string data = msg.toString(); 182 | emulNet->ENsend(&memberNode->addr, replicas[i].getAddress(), data); 183 | } 184 | g_transID ++; 185 | 186 | } 187 | 188 | /** 189 | * FUNCTION NAME: clientDelete 190 | * 191 | * DESCRIPTION: client side DELETE API 192 | * The function does the following: 193 | * 1) Constructs the message 194 | * 2) Finds the replicas of this key 195 | * 3) Sends a message to the replica 196 | */ 197 | void MP2Node::clientDelete(string key){ 198 | vector replicas = findNodes(key); 199 | for (int i =0; i < replicas.size(); i++) { 200 | Message msg = constructMsg(MessageType::DELETE, key); 201 | string data = msg.toString(); 202 | emulNet->ENsend(&memberNode->addr, replicas[i].getAddress(), data); 203 | } 204 | g_transID ++; 205 | } 206 | 207 | Message MP2Node::constructMsg(MessageType mType, string key, string value, bool success){ 208 | int trans_id = g_transID; 209 | createTransaction(trans_id, mType, key, value); 210 | if(mType == CREATE || mType == UPDATE){ 211 | Message msg(trans_id, this->memberNode->addr, mType, key, value); 212 | return msg; 213 | }else if(mType == READ || mType == DELETE){ 214 | Message msg(trans_id, this->memberNode->addr, mType, key); 215 | return msg; 216 | }else{ 217 | assert(1!=1); // for debug 218 | } 219 | } 220 | 221 | void MP2Node::createTransaction(int trans_id, MessageType mType, string key, string value){ 222 | int timestamp = this->par->getcurrtime(); 223 | transaction* t = new transaction(trans_id, timestamp, mType, key, value); 224 | this->transMap.emplace(trans_id, t); 225 | } 226 | 227 | // Constructor of transaction 228 | transaction::transaction(int trans_id, int timestamp, MessageType mType, string key, string value){ 229 | this->id = trans_id; 230 | this->timestamp = timestamp; 231 | this->replyCount = 0; 232 | this->successCount = 0; 233 | this->mType = mType; 234 | this->key = key; 235 | this->value = value; 236 | } 237 | /** 238 | * FUNCTION NAME: createKeyValue 239 | * 240 | * DESCRIPTION: Server side CREATE API 241 | * The function does the following: 242 | * 1) Inserts key value into the local hash table 243 | * 2) Return true or false based on success or failure 244 | */ 245 | bool MP2Node::createKeyValue(string key, string value, ReplicaType replica, int transID) { 246 | bool success = false; 247 | if(transID != STABLE){ 248 | success = this->ht->create(key, value); 249 | if(success) 250 | log->logCreateSuccess(&memberNode->addr, false, transID, key, value); 251 | else 252 | log->logCreateFail(&memberNode->addr, false, transID, key, value); 253 | }else{ 254 | string content = this->ht->read(key); 255 | bool exist = (content != ""); 256 | if(!exist){ 257 | success = this->ht->create(key, value); 258 | } 259 | } 260 | return success; 261 | // Insert key, value, replicaType into the hash table 262 | } 263 | 264 | /** 265 | * FUNCTION NAME: readKey 266 | * 267 | * DESCRIPTION: Server side READ API 268 | * This function does the following: 269 | * 1) Read key from local hash table 270 | * 2) Return value 271 | */ 272 | string MP2Node::readKey(string key, int transID) { 273 | string content = this->ht->read(key); 274 | bool success = (content!=""); 275 | if (success) { 276 | log->logReadSuccess(&memberNode->addr, false, transID, key, content); 277 | }else { 278 | log->logReadFail(&memberNode->addr, false, transID, key); 279 | } 280 | 281 | return content; 282 | // Read key from local hash table and return value 283 | } 284 | 285 | /** 286 | * FUNCTION NAME: updateKeyValue 287 | * 288 | * DESCRIPTION: Server side UPDATE API 289 | * This function does the following: 290 | * 1) Update the key to the new value in the local hash table 291 | * 2) Return true or false based on success or failure 292 | */ 293 | bool MP2Node::updateKeyValue(string key, string value, ReplicaType replica, int transID) { 294 | bool success = this->ht->update(key,value); 295 | if (success) { 296 | log->logUpdateSuccess(&memberNode->addr, false, transID, key, value); 297 | } else { 298 | log->logUpdateFail(&memberNode->addr, false, transID, key, value); 299 | } 300 | return success; 301 | // Update key in local hash table and return true or false 302 | } 303 | 304 | /** 305 | * FUNCTION NAME: deleteKey 306 | * 307 | * DESCRIPTION: Server side DELETE API 308 | * This function does the following: 309 | * 1) Delete the key from the local hash table 310 | * 2) Return true or false based on success or failure 311 | */ 312 | bool MP2Node::deletekey(string key, int transID) { 313 | bool success = this->ht->deleteKey(key); 314 | if(transID != STABLE){ 315 | if (success) { 316 | log->logDeleteSuccess(&memberNode->addr, false, transID, key); 317 | } else { 318 | log->logDeleteFail(&memberNode->addr, false, transID, key); 319 | } 320 | } 321 | return success; 322 | // Delete the key from the local hash table 323 | } 324 | 325 | void MP2Node::sendreply(string key, MessageType mType, bool success, Address* fromaddr, int transID, string content) { 326 | MessageType replyType = (mType == MessageType::READ)? MessageType::READREPLY: MessageType::REPLY; 327 | 328 | if(replyType == MessageType::READREPLY){ 329 | Message msg(transID, this->memberNode->addr, content); 330 | string data = msg.toString(); 331 | emulNet->ENsend(&memberNode->addr, fromaddr, data); 332 | }else{ 333 | // MessageType::REPLY 334 | Message msg(transID, this->memberNode->addr, replyType, success); 335 | string data = msg.toString(); 336 | emulNet->ENsend(&memberNode->addr, fromaddr, data); 337 | } 338 | 339 | } 340 | /** 341 | * FUNCTION NAME: checkMessages 342 | * 343 | * DESCRIPTION: This function is the message handler of this node. 344 | * This function does the following: 345 | * 1) Pops messages from the queue 346 | * 2) Handles the messages according to message types 347 | */ 348 | void MP2Node::checkMessages() { 349 | /* 350 | * Implement this. Parts of it are already implemented 351 | */ 352 | char * data; 353 | int size; 354 | 355 | /* 356 | * Declare your local variables here 357 | */ 358 | 359 | // dequeue all messages and handle them 360 | while ( !memberNode->mp2q.empty() ) { 361 | /* 362 | * Pop a message from the queue 363 | */ 364 | data = (char *)memberNode->mp2q.front().elt; 365 | size = memberNode->mp2q.front().size; 366 | memberNode->mp2q.pop(); 367 | 368 | string message(data, data + size); 369 | Message msg(message); 370 | 371 | 372 | switch(msg.type){ 373 | case MessageType::CREATE:{ 374 | bool success = createKeyValue(msg.key, msg.value, msg.replica, msg.transID); 375 | if (msg.transID != STABLE) { 376 | sendreply(msg.key, msg.type, success, &msg.fromAddr, msg.transID); 377 | } 378 | break; 379 | } 380 | case MessageType::DELETE:{ 381 | bool success = deletekey(msg.key, msg.transID); 382 | if (msg.transID != STABLE){ 383 | sendreply(msg.key, msg.type, success, &msg.fromAddr, msg.transID); 384 | } 385 | break; 386 | } 387 | case MessageType::READ:{ 388 | string content = readKey(msg.key, msg.transID); 389 | bool success = !content.empty(); 390 | sendreply(msg.key, msg.type, success, &msg.fromAddr, msg.transID, content); 391 | break; 392 | } 393 | case MessageType::UPDATE:{ 394 | bool success = updateKeyValue(msg.key, msg.value, msg.replica, msg.transID); 395 | sendreply(msg.key, msg.type, success, &msg.fromAddr, msg.transID); 396 | break; 397 | } 398 | case MessageType::READREPLY:{ 399 | map::iterator it = transMap.find(msg.transID); 400 | if(it == transMap.end()) 401 | break; 402 | transaction* t = transMap[msg.transID]; 403 | t->replyCount ++; 404 | t->value = msg.value; // content 405 | bool success = (msg.value != ""); 406 | 407 | if(success) { 408 | t->successCount ++; 409 | } 410 | break; 411 | } 412 | case MessageType::REPLY:{ 413 | map::iterator it = transMap.find(msg.transID); 414 | if(it == transMap.end()){ 415 | break; 416 | } 417 | 418 | transaction* t = transMap[msg.transID]; 419 | t->replyCount ++; 420 | if(msg.success) 421 | t->successCount ++; 422 | break; 423 | } 424 | } 425 | /* 426 | * Handle the message types here 427 | */ 428 | checkTransMap(); 429 | 430 | 431 | } 432 | 433 | /* 434 | * This function should also ensure all READ and UPDATE operation 435 | * get QUORUM replies 436 | */ 437 | } 438 | 439 | void MP2Node::checkTransMap(){ 440 | map::iterator it = transMap.begin(); 441 | while (it != transMap.end()){ 442 | if(it->second->replyCount == 3) { 443 | if(it->second->successCount >= 2) { 444 | logOperation(it->second, true, true, it->first); 445 | }else{ 446 | logOperation(it->second, true, false, it->first); 447 | } 448 | delete it->second; 449 | it = transMap.erase(it); 450 | continue; 451 | }else { 452 | if(it->second->successCount == 2) { 453 | logOperation(it->second, true, true, it->first); 454 | transComplete.emplace(it->first, true); 455 | delete it->second; 456 | it = transMap.erase(it); 457 | continue; 458 | } 459 | 460 | if(it->second->replyCount - it->second->successCount == 2) { 461 | logOperation(it->second, true, false, it->first); 462 | transComplete.emplace(it->first, false); 463 | delete it->second; 464 | it = transMap.erase(it); 465 | continue; 466 | } 467 | } 468 | 469 | // time limit 470 | if(this->par->getcurrtime() - it->second->getTime() > 10) { 471 | logOperation(it->second, true, false, it->first); 472 | transComplete.emplace(it->first, false); 473 | delete it->second; 474 | it = transMap.erase(it); 475 | continue; 476 | } 477 | 478 | it++; 479 | } 480 | } 481 | 482 | void MP2Node::logOperation(transaction* t, bool isCoordinator, bool success, int transID) { 483 | switch (t->mType) { 484 | case CREATE: { 485 | if (success) { 486 | log->logCreateSuccess(&memberNode->addr, isCoordinator, transID, t->key, t->value); 487 | } else { 488 | log->logCreateFail(&memberNode->addr, isCoordinator, transID, t->key, t->value); 489 | } 490 | break; 491 | } 492 | 493 | case READ: { 494 | if (success) { 495 | log->logReadSuccess(&memberNode->addr, isCoordinator, transID, t->key, t->value); 496 | } else { 497 | log->logReadFail(&memberNode->addr, isCoordinator, transID, t->key); 498 | } 499 | break; 500 | } 501 | 502 | case UPDATE: { 503 | if (success) { 504 | log->logUpdateSuccess(&memberNode->addr, isCoordinator, transID, t->key, t->value); 505 | } else { 506 | log->logUpdateFail(&memberNode->addr, isCoordinator, transID, t->key, t->value); 507 | } 508 | break; 509 | } 510 | 511 | case DELETE: { 512 | if (success) { 513 | log->logDeleteSuccess(&memberNode->addr, isCoordinator, transID, t->key); 514 | } else { 515 | log->logDeleteFail(&memberNode->addr, isCoordinator, transID, t->key); 516 | } 517 | break; 518 | } 519 | } 520 | } 521 | 522 | /** 523 | * FUNCTION NAME: findNodes 524 | * 525 | * DESCRIPTION: Find the replicas of the given keyfunction 526 | * This function is responsible for finding the replicas of a key 527 | */ 528 | vector MP2Node::findNodes(string key) { 529 | size_t pos = hashFunction(key); 530 | vector addr_vec; 531 | if (ring.size() >= 3) { 532 | // if pos <= min || pos > max, the leader is the min 533 | if (pos <= ring.at(0).getHashCode() || pos > ring.at(ring.size()-1).getHashCode()) { 534 | addr_vec.emplace_back(ring.at(0)); 535 | addr_vec.emplace_back(ring.at(1)); 536 | addr_vec.emplace_back(ring.at(2)); 537 | } 538 | else { 539 | // go through the ring until pos <= node 540 | for (int i=1; ibFailed ) { 562 | return false; 563 | } 564 | else { 565 | return emulNet->ENrecv(&(memberNode->addr), this->enqueueWrapper, NULL, 1, &(memberNode->mp2q)); 566 | } 567 | } 568 | 569 | /** 570 | * FUNCTION NAME: enqueueWrapper 571 | * 572 | * DESCRIPTION: Enqueue the message from Emulnet into the queue of MP2Node 573 | */ 574 | int MP2Node::enqueueWrapper(void *env, char *buff, int size) { 575 | Queue q; 576 | return q.enqueue((queue *)env, (void *)buff, size); 577 | } 578 | /** 579 | * FUNCTION NAME: stabilizationProtocol 580 | * 581 | * DESCRIPTION: This runs the stabilization protocol in case of Node joins and leaves 582 | * It ensures that there always 3 copies of all keys in the DHT at all times 583 | * The function does the following: 584 | * 1) Ensures that there are three "CORRECT" replicas of all the keys in spite of failures and joins 585 | * Note:- "CORRECT" replicas implies that every key is replicated in its two neighboring nodes in the ring 586 | */ 587 | void MP2Node::stabilizationProtocol() { 588 | map::iterator it; 589 | for(it = this->ht->hashTable.begin(); it != this->ht->hashTable.end(); it++) { 590 | string key = it->first; 591 | string value = it->second; 592 | vector replicas = findNodes(key); 593 | for (int i = 0; i < replicas.size(); i++) { 594 | // create 595 | Message createMsg(STABLE, this->memberNode->addr, MessageType::CREATE, key, value); 596 | string createData = createMsg.toString(); 597 | emulNet->ENsend(&memberNode->addr, replicas[i].getAddress(), createData); 598 | } 599 | } 600 | } 601 | -------------------------------------------------------------------------------- /KVStoreGrader.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################################# 4 | # FILE NAME: KVStoreGrader.sh 5 | # 6 | # DESCRIPTION: Grader for MP2 7 | # 8 | # RUN PROCEDURE: 9 | # $ chmod +x KVStoreGrader.sh 10 | # $ ./KVStoreGrader.sh 11 | ################################################# 12 | 13 | function contains () { 14 | local e 15 | for e in "${@:2}" 16 | do 17 | if [ "$e" == "$1" ]; then 18 | echo 1 19 | return 1; 20 | fi 21 | done 22 | echo 0 23 | } 24 | 25 | #### 26 | # Main function 27 | #### 28 | 29 | verbose=$(contains "-v" "$@") 30 | 31 | ### 32 | # Global variables 33 | ### 34 | SUCCESS=0 35 | FAILURE=-1 36 | RF=3 37 | RFPLUSONE=4 38 | CREATE_OPERATION="CREATE OPERATION" 39 | CREATE_SUCCESS="create success" 40 | GRADE=0 41 | DELETE_OPERATION="DELETE OPERATION" 42 | DELETE_SUCCESS="delete success" 43 | DELETE_FAILURE="delete fail" 44 | INVALID_KEY="invalidKey" 45 | READ_OPERATION="READ OPERATION" 46 | READ_SUCCESS="read success" 47 | READ_FAILURE="read fail" 48 | QUORUM=2 49 | QUORUMPLUSONE=3 50 | UPDATE_OPERATION="UPDATE OPERATION" 51 | UPDATE_SUCCESS="update success" 52 | UPDATE_FAILURE="update fail" 53 | 54 | echo "" 55 | echo "############################" 56 | echo " CREATE TEST" 57 | echo "############################" 58 | echo "" 59 | 60 | CREATE_TEST_STATUS="${SUCCESS}" 61 | CREATE_TEST_SCORE=0 62 | 63 | if [ "${verbose}" -eq 0 ] 64 | then 65 | make clean > /dev/null 2>&1 66 | make > /dev/null 2>&1 67 | if [ $? -ne "${SUCCESS}" ] 68 | then 69 | echo "COMPILATION ERROR !!!" 70 | exit 71 | fi 72 | ./Application ./testcases/create.conf > /dev/null 2>&1 73 | else 74 | make clean 75 | make 76 | if [ $? -ne "${SUCCESS}" ] 77 | then 78 | echo "COMPILATION ERROR !!!" 79 | exit 80 | fi 81 | ./Application ./testcases/create.conf 82 | fi 83 | 84 | echo "TEST 1: Create 3 replicas of every key" 85 | 86 | create_count=`grep -i "${CREATE_OPERATION}" dbg.log | wc -l` 87 | create_success_count=`grep -i "${CREATE_SUCCESS}" dbg.log | wc -l` 88 | expected_count=$(( ${create_count} * ${RFPLUSONE} )) 89 | 90 | if [ ${create_success_count} -ne ${expected_count} ] 91 | then 92 | CREATE_TEST_STATUS="${FAILURE}" 93 | else 94 | keys=`grep -i "${CREATE_OPERATION}" dbg.log | cut -d" " -f7` 95 | for key in ${keys} 96 | do 97 | key_create_success_count=`grep -i "${CREATE_SUCCESS}" dbg.log | grep "${key}" | wc -l` 98 | if [ "${key_create_success_count}" -ne "${RFPLUSONE}" ] 99 | then 100 | CREATE_TEST_STATUS="${FAILURE}" 101 | break 102 | fi 103 | done 104 | fi 105 | 106 | if [ "${CREATE_TEST_STATUS}" -eq "${SUCCESS}" ] 107 | then 108 | CREATE_TEST_SCORE=3 109 | fi 110 | 111 | # Display score 112 | echo "TEST 1 SCORE..................: ${CREATE_TEST_SCORE} / 3" 113 | # Add to grade 114 | GRADE=$(( ${GRADE} + ${CREATE_TEST_SCORE} )) 115 | 116 | #echo "" 117 | #echo "############################" 118 | #echo " CREATE TEST ENDS" 119 | #echo "############################" 120 | #echo "" 121 | 122 | echo "" 123 | echo "############################" 124 | echo " DELETE TEST" 125 | echo "############################" 126 | echo "" 127 | 128 | DELETE_TEST1_STATUS="${SUCCESS}" 129 | DELETE_TEST2_STATUS="${SUCCESS}" 130 | DELETE_TEST1_SCORE=0 131 | DELETE_TEST2_SCORE=0 132 | 133 | if [ "${verbose}" -eq 0 ] 134 | then 135 | make clean > /dev/null 2>&1 136 | make > /dev/null 2>&1 137 | if [ $? -ne "${SUCCESS}" ] 138 | then 139 | echo "COMPILATION ERROR !!!" 140 | exit 141 | fi 142 | ./Application ./testcases/delete.conf > /dev/null 2>&1 143 | else 144 | make clean 145 | make 146 | if [ $? -ne "${SUCCESS}" ] 147 | then 148 | echo "COMPILATION ERROR !!!" 149 | exit 150 | fi 151 | ./Application ./testcases/delete.conf 152 | fi 153 | 154 | echo "TEST 1: Delete 3 replicas of every key" 155 | 156 | delete_count=`grep -i "${DELETE_OPERATION}" dbg.log | wc -l` 157 | valid_delete_count=$(( ${delete_count} - 1 )) 158 | expected_count=$(( ${valid_delete_count} * ${RFPLUSONE} )) 159 | delete_success_count=`grep -i "${DELETE_SUCCESS}" dbg.log | wc -l` 160 | 161 | if [ "${delete_success_count}" -ne "${expected_count}" ] 162 | then 163 | DELETE_TEST1_STATUS="${FAILURE}" 164 | else 165 | keys="" 166 | keys=`grep -i "${DELETE_OPERATION}" dbg.log | cut -d" " -f7` 167 | for key in ${keys} 168 | do 169 | if [ $key != "${INVALID_KEY}" ] 170 | then 171 | key_delete_success_count=`grep -i "${DELETE_SUCCESS}" dbg.log | grep "${key}" | wc -l` 172 | if [ "${key_delete_success_count}" -ne "${RFPLUSONE}" ] 173 | then 174 | DELETE_TEST1_STATUS="${FAILURE}" 175 | break 176 | fi 177 | fi 178 | done 179 | fi 180 | 181 | echo "TEST 2: Attempt delete of an invalid key" 182 | 183 | delete_fail_count=`grep -i "${DELETE_FAILURE}" dbg.log | grep "${INVALID_KEY}" | wc -l` 184 | if [ "${delete_fail_count}" -ne 4 ] 185 | then 186 | DELETE_TEST2_STATUS="${FAILURE}" 187 | fi 188 | 189 | if [ "${DELETE_TEST1_STATUS}" -eq "${SUCCESS}" ] 190 | then 191 | DELETE_TEST1_SCORE=3 192 | fi 193 | 194 | if [ "${DELETE_TEST2_STATUS}" -eq "${SUCCESS}" ] 195 | then 196 | DELETE_TEST2_SCORE=4 197 | fi 198 | 199 | # Display score 200 | echo "TEST 1 SCORE..................: ${DELETE_TEST1_SCORE} / 3" 201 | echo "TEST 2 SCORE..................: ${DELETE_TEST2_SCORE} / 4" 202 | # Add to grade 203 | GRADE=$(( ${GRADE} + ${DELETE_TEST1_SCORE} )) 204 | GRADE=$(( ${GRADE} + ${DELETE_TEST2_SCORE} )) 205 | 206 | #echo "" 207 | #echo "############################" 208 | #echo " DELETE TEST ENDS" 209 | #echo "############################" 210 | #echo "" 211 | 212 | echo "" 213 | echo "############################" 214 | echo " READ TEST" 215 | echo "############################" 216 | echo "" 217 | 218 | READ_TEST1_STATUS="${FAILURE}" 219 | READ_TEST1_SCORE=0 220 | READ_TEST2_STATUS="${FAILURE}" 221 | READ_TEST2_SCORE=0 222 | READ_TEST3_PART1_STATUS="${FAILURE}" 223 | READ_TEST3_PART1_SCORE=0 224 | READ_TEST3_PART2_STATUS="${FAILURE}" 225 | READ_TEST3_PART2_SCORE=0 226 | READ_TEST4_STATUS="${FAILURE}" 227 | READ_TEST4_SCORE=0 228 | READ_TEST5_STATUS="${FAILURE}" 229 | READ_TEST5_SCORE=0 230 | 231 | if [ "${verbose}" -eq 0 ] 232 | then 233 | make clean > /dev/null 2>&1 234 | make > /dev/null 2>&1 235 | if [ $? -ne "${SUCCESS}" ] 236 | then 237 | echo "COMPILATION ERROR !!!" 238 | exit 239 | fi 240 | ./Application ./testcases/read.conf > /dev/null 2>&1 241 | else 242 | make clean 243 | make 244 | if [ $? -ne "${SUCCESS}" ] 245 | then 246 | echo "COMPILATION ERROR !!!" 247 | exit 248 | fi 249 | ./Application ./testcases/read.conf 250 | fi 251 | 252 | read_operations=`grep -i "${READ_OPERATION}" dbg.log | cut -d" " -f3 | tr -s ']' ' ' | tr -s '[' ' ' | sort` 253 | 254 | cnt=1 255 | for time in ${read_operations} 256 | do 257 | if [ ${cnt} -eq 1 ] 258 | then 259 | echo "TEST 1: Read a key. Check for correct value being read at least in quorum of replicas" 260 | read_op_test1_time="${time}" 261 | read_op_test1_key=`grep -i "${READ_OPERATION}" dbg.log | grep "${read_op_test1_time}" | cut -d" " -f7` 262 | read_op_test1_value=`grep -i "${READ_OPERATION}" dbg.log | grep "${read_op_test1_time}" | cut -d" " -f9` 263 | elif [ ${cnt} -eq 2 ] 264 | then 265 | echo "TEST 2: Read a key after failing a replica. Check for correct value being read at least in quorum of replicas" 266 | read_op_test2_time="${time}" 267 | read_op_test2_key=`grep -i "${READ_OPERATION}" dbg.log | grep "${read_op_test2_time}" | cut -d" " -f7` 268 | read_op_test2_value=`grep -i "${READ_OPERATION}" dbg.log | grep "${read_op_test2_time}" | cut -d" " -f9` 269 | elif [ ${cnt} -eq 3 ] 270 | then 271 | echo "TEST 3 PART 1: Read a key after failing two replicas. Read should fail" 272 | read_op_test3_part1_time="${time}" 273 | read_op_test3_part1_key=`grep -i "${READ_OPERATION}" dbg.log | grep "${read_op_test3_part1_time}" | cut -d" " -f7` 274 | read_op_test3_part1_value=`grep -i "${READ_OPERATION}" dbg.log | grep "${read_op_test3_part1_time}" | cut -d" " -f9` 275 | elif [ ${cnt} -eq 4 ] 276 | then 277 | echo "TEST 3 PART 2: Read the key after allowing stabilization protocol to kick in. Check for correct value being read at least in quorum of replicas" 278 | read_op_test3_part2_time="${time}" 279 | read_op_test3_part2_key=`grep -i "${READ_OPERATION}" dbg.log | grep "${read_op_test3_part2_time}" | cut -d" " -f7` 280 | read_op_test3_part2_value=`grep -i "${READ_OPERATION}" dbg.log | grep "${read_op_test3_part2_time}" | cut -d" " -f9` 281 | elif [ ${cnt} -eq 5 ] 282 | then 283 | echo "TEST 4: Read a key after failing a non-replica. Check for correct value being read at least in quorum of replicas" 284 | read_op_test4_time="${time}" 285 | read_op_test4_key="${read_op_test1_key}" 286 | read_op_test4_value="${read_op_test1_value}" 287 | elif [ ${cnt} -eq 6 ] 288 | then 289 | echo "TEST 5: Attempt read of an invalid key" 290 | read_op_test5_time="${time}" 291 | fi 292 | cnt=$(( ${cnt} + 1 )) 293 | done 294 | 295 | read_test1_success_count=0 296 | read_test2_success_count=0 297 | read_test3_part2_success_count=0 298 | read_test4_success_count=0 299 | 300 | read_successes=`grep -i "${READ_SUCCESS}" dbg.log | grep ${read_op_test1_key} | grep ${read_op_test1_value} 2>/dev/null` 301 | if [ "${read_successes}" ] 302 | then 303 | while read success 304 | do 305 | time_of_this_success=`echo "${success}" | cut -d" " -f2 | tr -s '[' ' ' | tr -s ']' ' '` 306 | if [ "${time_of_this_success}" -ge "${read_op_test1_time}" -a "${time_of_this_success}" -lt "${read_op_test2_time}" ] 307 | then 308 | read_test1_success_count=`expr ${read_test1_success_count} + 1` 309 | elif [ "${time_of_this_success}" -ge "${read_op_test2_time}" -a "${time_of_this_success}" -lt "${read_op_test3_part1_time}" ] 310 | then 311 | read_test2_success_count=`expr ${read_test2_success_count} + 1` 312 | elif [ "${time_of_this_success}" -ge "${read_op_test3_part2_time}" -a "${time_of_this_success}" -lt "${read_op_test4_time}" ] 313 | then 314 | read_test3_part2_success_count=`expr ${read_test3_part2_success_count} + 1` 315 | elif [ "${time_of_this_success}" -ge "${read_op_test4_time}" ] 316 | then 317 | read_test4_success_count=`expr ${read_test4_success_count} + 1` 318 | fi 319 | done <<<"${read_successes}" 320 | fi 321 | 322 | read_test3_part1_fail_count=0 323 | read_test5_fail_count=0 324 | 325 | read_fails=`grep -i "${READ_FAILURE}" dbg.log 2>/dev/null` 326 | if [ "${read_fails}" ] 327 | then 328 | while read fail 329 | do 330 | time_of_this_fail=`echo "${fail}" | cut -d" " -f2 | tr -s '[' ' ' | tr -s ']' ' '` 331 | if [ "${time_of_this_fail}" -ge "${read_op_test3_part1_time}" -a "${time_of_this_fail}" -lt "${read_op_test3_part2_time}" ] 332 | then 333 | actual_key=`echo "${fail}" | grep "${read_op_test3_part1_key}" | wc -l` 334 | if [ "${actual_key}" -eq 1 ] 335 | then 336 | read_test3_part1_fail_count=`expr ${read_test3_part1_fail_count} + 1` 337 | fi 338 | elif [ "${time_of_this_fail}" -ge "${read_op_test5_time}" ] 339 | then 340 | actual_key=`echo "${fail}" | grep "${INVALID_KEY}" | wc -l` 341 | if [ "${actual_key}" -eq 1 ] 342 | then 343 | read_test5_fail_count=`expr ${read_test5_fail_count} + 1` 344 | fi 345 | fi 346 | done <<<"${read_fails}" 347 | fi 348 | 349 | if [ "${read_test1_success_count}" -eq "${QUORUMPLUSONE}" -o "${read_test1_success_count}" -eq "${RFPLUSONE}" ] 350 | then 351 | READ_TEST1_STATUS="${SUCCESS}" 352 | fi 353 | if [ "${read_test2_success_count}" -eq "${QUORUMPLUSONE}" ] 354 | then 355 | READ_TEST2_STATUS="${SUCCESS}" 356 | fi 357 | if [ "${read_test3_part1_fail_count}" -eq 1 ] 358 | then 359 | READ_TEST3_PART1_STATUS="${SUCCESS}" 360 | fi 361 | if [ "${read_test3_part2_success_count}" -eq "${QUORUMPLUSONE}" -o "${read_test3_part2_success_count}" -eq "${RFPLUSONE}" ] 362 | then 363 | READ_TEST3_PART2_STATUS="${SUCCESS}" 364 | fi 365 | if [ "${read_test4_success_count}" -eq "${QUORUMPLUSONE}" -o "${read_test4_success_count}" -eq "${RFPLUSONE}" ] 366 | then 367 | READ_TEST4_STATUS="${SUCCESS}" 368 | fi 369 | if [ "${read_test5_fail_count}" -eq "${QUORUMPLUSONE}" -o "${read_test5_fail_count}" -eq "${RFPLUSONE}" ] 370 | then 371 | READ_TEST5_STATUS="${SUCCESS}" 372 | fi 373 | 374 | if [ "${READ_TEST1_STATUS}" -eq "${SUCCESS}" ] 375 | then 376 | READ_TEST1_SCORE=3 377 | fi 378 | if [ "${READ_TEST2_STATUS}" -eq "${SUCCESS}" ] 379 | then 380 | READ_TEST2_SCORE=9 381 | fi 382 | if [ "${READ_TEST3_PART1_STATUS}" -eq "${SUCCESS}" ] 383 | then 384 | READ_TEST3_PART1_SCORE=9 385 | fi 386 | if [ "${READ_TEST3_PART2_STATUS}" -eq "${SUCCESS}" ] 387 | then 388 | READ_TEST3_PART2_SCORE=10 389 | fi 390 | if [ "${READ_TEST4_STATUS}" -eq "${SUCCESS}" ] 391 | then 392 | READ_TEST4_SCORE=6 393 | fi 394 | if [ "${READ_TEST5_STATUS}" -eq "${SUCCESS}" ] 395 | then 396 | READ_TEST5_SCORE=3 397 | fi 398 | 399 | # Display score 400 | echo "TEST 1 SCORE..................: ${READ_TEST1_SCORE} / 3" 401 | echo "TEST 2 SCORE..................: ${READ_TEST2_SCORE} / 9" 402 | echo "TEST 3 PART 1 SCORE..................: ${READ_TEST3_PART1_SCORE} / 9" 403 | echo "TEST 3 PART 2 SCORE..................: ${READ_TEST3_PART2_SCORE} / 10" 404 | echo "TEST 4 SCORE..................: ${READ_TEST4_SCORE} / 6" 405 | echo "TEST 5 SCORE..................: ${READ_TEST5_SCORE} / 3" 406 | # Add to grade 407 | GRADE=`expr ${GRADE} + ${READ_TEST1_SCORE}` 408 | GRADE=`expr ${GRADE} + ${READ_TEST2_SCORE}` 409 | GRADE=`echo ${GRADE} ${READ_TEST3_PART1_SCORE} | awk '{print $1 + $2}'` 410 | GRADE=`echo ${GRADE} ${READ_TEST3_PART2_SCORE} | awk '{print $1 + $2}'` 411 | GRADE=`echo ${GRADE} ${READ_TEST4_SCORE} | awk '{print $1 + $2}'` 412 | GRADE=`echo ${GRADE} ${READ_TEST5_SCORE} | awk '{print $1 + $2}'` 413 | 414 | #echo "" 415 | #echo "############################" 416 | #echo " READ TEST ENDS" 417 | #echo "############################" 418 | #echo "" 419 | 420 | echo "" 421 | echo "############################" 422 | echo " UPDATE TEST" 423 | echo "############################" 424 | echo "" 425 | 426 | UPDATE_TEST1_STATUS="${FAILURE}" 427 | UPDATE_TEST1_SCORE=0 428 | UPDATE_TEST2_STATUS="${FAILURE}" 429 | UPDATE_TEST2_SCORE=0 430 | UPDATE_TEST3_PART1_STATUS="${FAILURE}" 431 | UPDATE_TEST3_PART1_SCORE=0 432 | UPDATE_TEST3_PART2_STATUS="${FAILURE}" 433 | UPDATE_TEST3_PART2_SCORE=0 434 | UPDATE_TEST4_STATUS="${FAILURE}" 435 | UPDATE_TEST4_SCORE=0 436 | UPDATE_TEST5_STATUS="${FAILURE}" 437 | UPDATE_TEST5_SCORE=0 438 | 439 | if [ "${verbose}" -eq 0 ] 440 | then 441 | make clean > /dev/null 2>&1 442 | make > /dev/null 2>&1 443 | if [ $? -ne "${SUCCESS}" ] 444 | then 445 | echo "COMPILATION ERROR !!!" 446 | exit 447 | fi 448 | ./Application ./testcases/update.conf > /dev/null 2>&1 449 | else 450 | make clean 451 | make 452 | if [ $? -ne "${SUCCESS}" ] 453 | then 454 | echo "COMPILATION ERROR !!!" 455 | exit 456 | fi 457 | ./Application ./testcases/update.conf 458 | fi 459 | 460 | update_operations=`grep -i "${UPDATE_OPERATION}" dbg.log | cut -d" " -f3 | tr -s ']' ' ' | tr -s '[' ' ' | sort` 461 | 462 | cnt=1 463 | for time in ${update_operations} 464 | do 465 | if [ ${cnt} -eq 1 ] 466 | then 467 | echo "TEST 1: Update a key. Check for correct value being updated at least in quorum of replicas" 468 | update_op_test1_time="${time}" 469 | update_op_test1_key=`grep -i "${UPDATE_OPERATION}" dbg.log | grep "${update_op_test1_time}" | cut -d" " -f7` 470 | update_op_test1_value=`grep -i "${UPDATE_OPERATION}" dbg.log | grep "${update_op_test1_time}" | cut -d" " -f9` 471 | elif [ ${cnt} -eq 2 ] 472 | then 473 | echo "TEST 2: Update a key after failing a replica. Check for correct value being updated at least in quorum of replicas" 474 | update_op_test2_time="${time}" 475 | update_op_test2_key=`grep -i "${UPDATE_OPERATION}" dbg.log | grep "${update_op_test2_time}" | cut -d" " -f7` 476 | update_op_test2_value=`grep -i "${UPDATE_OPERATION}" dbg.log | grep "${update_op_test2_time}" | cut -d" " -f9` 477 | elif [ ${cnt} -eq 3 ] 478 | then 479 | echo "TEST 3 PART 1: Update a key after failing two replicas. Update should fail" 480 | update_op_test3_part1_time="${time}" 481 | update_op_test3_part1_key=`grep -i "${UPDATE_OPERATION}" dbg.log | grep "${update_op_test3_part1_time}" | cut -d" " -f7` 482 | update_op_test3_part1_value=`grep -i "${UPDATE_OPERATION}" dbg.log | grep "${update_op_test3_part1_time}" | cut -d" " -f9` 483 | elif [ ${cnt} -eq 4 ] 484 | then 485 | echo "TEST 3 PART 2: Update the key after allowing stabilization protocol to kick in. Check for correct value being updated at least in quorum of replicas" 486 | update_op_test3_part2_time="${time}" 487 | update_op_test3_part2_key=`grep -i "${UPDATE_OPERATION}" dbg.log | grep "${update_op_test3_part2_time}" | cut -d" " -f7` 488 | update_op_test3_part2_value=`grep -i "${UPDATE_OPERATION}" dbg.log | grep "${update_op_test3_part2_time}" | cut -d" " -f9` 489 | elif [ ${cnt} -eq 5 ] 490 | then 491 | echo "TEST 4: Update a key after failing a non-replica. Check for correct value being updated at least in quorum of replicas" 492 | update_op_test4_time="${time}" 493 | update_op_test4_key="${update_op_test1_key}" 494 | update_op_test4_value="${update_op_test1_value}" 495 | elif [ ${cnt} -eq 6 ] 496 | then 497 | echo "TEST 5: Attempt update of an invalid key" 498 | update_op_test5_time="${time}" 499 | fi 500 | cnt=$(( ${cnt} + 1 )) 501 | done 502 | 503 | update_test1_success_count=0 504 | update_test2_success_count=0 505 | update_test3_part2_success_count=0 506 | update_test4_success_count=0 507 | 508 | update_successes=`grep -i "${UPDATE_SUCCESS}" dbg.log | grep ${update_op_test1_key} | grep ${update_op_test1_value} 2>/dev/null` 509 | if [ "${update_successes}" ] 510 | then 511 | while read success 512 | do 513 | time_of_this_success=`echo "${success}" | cut -d" " -f2 | tr -s '[' ' ' | tr -s ']' ' '` 514 | if [ "${time_of_this_success}" -ge "${update_op_test1_time}" -a "${time_of_this_success}" -lt "${update_op_test2_time}" ] 515 | then 516 | update_test1_success_count=`expr ${update_test1_success_count} + 1` 517 | elif [ "${time_of_this_success}" -ge "${update_op_test2_time}" -a "${time_of_this_success}" -lt "${update_op_test3_part1_time}" ] 518 | then 519 | update_test2_success_count=`expr ${update_test2_success_count} + 1` 520 | elif [ "${time_of_this_success}" -ge "${update_op_test3_part2_time}" -a "${time_of_this_success}" -lt "${update_op_test4_time}" ] 521 | then 522 | update_test3_part2_success_count=`expr ${update_test3_part2_success_count} + 1` 523 | elif [ "${time_of_this_success}" -ge "${update_op_test4_time}" ] 524 | then 525 | update_test4_success_count=`expr ${update_test4_success_count} + 1` 526 | fi 527 | done <<<"${update_successes}" 528 | fi 529 | 530 | update_test3_part1_fail_count=0 531 | update_test5_fail_count=0 532 | 533 | update_fails=`grep -i "${UPDATE_FAILURE}" dbg.log 2>/dev/null` 534 | if [ "${update_fails}" ] 535 | then 536 | while read fail 537 | do 538 | time_of_this_fail=`echo "${fail}" | cut -d" " -f2 | tr -s '[' ' ' | tr -s ']' ' '` 539 | if [ "${time_of_this_fail}" -ge "${update_op_test3_part1_time}" -a "${time_of_this_fail}" -lt "${update_op_test3_part2_time}" ] 540 | then 541 | actual_key=`echo "${fail}" | grep "${update_op_test3_part1_key}" | wc -l` 542 | if [ "${actual_key}" -eq 1 ] 543 | then 544 | update_test3_part1_fail_count=`expr ${update_test3_part1_fail_count} + 1` 545 | fi 546 | elif [ "${time_of_this_fail}" -ge "${update_op_test5_time}" ] 547 | then 548 | actual_key=`echo "${fail}" | grep "${INVALID_KEY}" | wc -l` 549 | if [ "${actual_key}" -eq 1 ] 550 | then 551 | update_test5_fail_count=`expr ${update_test5_fail_count} + 1` 552 | fi 553 | fi 554 | done <<<"${update_fails}" 555 | fi 556 | 557 | if [ "${update_test1_success_count}" -eq "${QUORUMPLUSONE}" -o "${update_test1_success_count}" -eq "${RFPLUSONE}" ] 558 | then 559 | UPDATE_TEST1_STATUS="${SUCCESS}" 560 | fi 561 | if [ "${update_test2_success_count}" -eq "${QUORUMPLUSONE}" ] 562 | then 563 | UPDATE_TEST2_STATUS="${SUCCESS}" 564 | fi 565 | if [ "${update_test3_part1_fail_count}" -eq 1 ] 566 | then 567 | UPDATE_TEST3_PART1_STATUS="${SUCCESS}" 568 | fi 569 | if [ "${update_test3_part2_success_count}" -eq "${QUORUMPLUSONE}" -o "${update_test3_part2_success_count}" -eq "${RFPLUSONE}" ] 570 | then 571 | UPDATE_TEST3_PART2_STATUS="${SUCCESS}" 572 | fi 573 | if [ "${update_test4_success_count}" -eq "${QUORUMPLUSONE}" -o "${update_test4_success_count}" -eq "${RFPLUSONE}" ] 574 | then 575 | UPDATE_TEST4_STATUS="${SUCCESS}" 576 | fi 577 | if [ "${update_test5_fail_count}" -eq "${QUORUMPLUSONE}" -o "${update_test5_fail_count}" -eq "${RFPLUSONE}" ] 578 | then 579 | UPDATE_TEST5_STATUS="${SUCCESS}" 580 | fi 581 | 582 | if [ "${UPDATE_TEST1_STATUS}" -eq "${SUCCESS}" ] 583 | then 584 | UPDATE_TEST1_SCORE=3 585 | fi 586 | if [ "${UPDATE_TEST2_STATUS}" -eq "${SUCCESS}" ] 587 | then 588 | UPDATE_TEST2_SCORE=9 589 | fi 590 | if [ "${UPDATE_TEST3_PART1_STATUS}" -eq "${SUCCESS}" ] 591 | then 592 | UPDATE_TEST3_PART1_SCORE=9 593 | fi 594 | if [ "${UPDATE_TEST3_PART2_STATUS}" -eq "${SUCCESS}" ] 595 | then 596 | UPDATE_TEST3_PART2_SCORE=10 597 | fi 598 | if [ "${UPDATE_TEST4_STATUS}" -eq "${SUCCESS}" ] 599 | then 600 | UPDATE_TEST4_SCORE=6 601 | fi 602 | if [ "${UPDATE_TEST5_STATUS}" -eq "${SUCCESS}" ] 603 | then 604 | UPDATE_TEST5_SCORE=3 605 | fi 606 | 607 | # Display score 608 | echo "TEST 1 SCORE..................: ${UPDATE_TEST1_SCORE} / 3" 609 | echo "TEST 2 SCORE..................: ${UPDATE_TEST2_SCORE} / 9" 610 | echo "TEST 3 PART 1 SCORE..................: ${UPDATE_TEST3_PART1_SCORE} / 9" 611 | echo "TEST 3 PART 2 SCORE..................: ${UPDATE_TEST3_PART2_SCORE} / 10" 612 | echo "TEST 4 SCORE..................: ${UPDATE_TEST4_SCORE} / 6" 613 | echo "TEST 5 SCORE..................: ${UPDATE_TEST5_SCORE} / 3" 614 | # Add to grade 615 | GRADE=`echo ${GRADE} ${UPDATE_TEST1_SCORE} | awk '{print $1 + $2}'` 616 | GRADE=`echo ${GRADE} ${UPDATE_TEST2_SCORE} | awk '{print $1 + $2}'` 617 | GRADE=`echo ${GRADE} ${UPDATE_TEST3_PART1_SCORE} | awk '{print $1 + $2}'` 618 | GRADE=`echo ${GRADE} ${UPDATE_TEST3_PART2_SCORE} | awk '{print $1 + $2}'` 619 | GRADE=`echo ${GRADE} ${UPDATE_TEST4_SCORE} | awk '{print $1 + $2}'` 620 | GRADE=`echo ${GRADE} ${UPDATE_TEST5_SCORE} | awk '{print $1 + $2}'` 621 | 622 | #echo "" 623 | #echo "############################" 624 | #echo " UPDATE TEST ENDS" 625 | #echo "############################" 626 | #echo "" 627 | 628 | echo "" 629 | echo "TOTAL GRADE: ${GRADE} / 90" 630 | echo "" 631 | -------------------------------------------------------------------------------- /Application.cpp: -------------------------------------------------------------------------------- 1 | /********************************** 2 | * FILE NAME: Application.cpp 3 | * 4 | * DESCRIPTION: Application layer class function definitions 5 | **********************************/ 6 | 7 | #include "Application.h" 8 | 9 | void handler(int sig) { 10 | void *array[10]; 11 | size_t size; 12 | 13 | // get void*'s for all entries on the stack 14 | size = backtrace(array, 10); 15 | 16 | // print out all the frames to stderr 17 | fprintf(stderr, "Error: signal %d:\n", sig); 18 | backtrace_symbols_fd(array, size, STDERR_FILENO); 19 | exit(1); 20 | } 21 | 22 | /********************************** 23 | * FUNCTION NAME: main 24 | * 25 | * DESCRIPTION: main function. Start from here 26 | **********************************/ 27 | int main(int argc, char *argv[]) { 28 | //signal(SIGSEGV, handler); 29 | if ( argc != ARGS_COUNT ) { 30 | cout<<"Configuration (i.e., *.conf) file File Required"<run(); 38 | // When done delete the application object 39 | delete(app); 40 | 41 | return SUCCESS; 42 | } 43 | 44 | /** 45 | * Constructor of the Application class 46 | */ 47 | Application::Application(char *infile) { 48 | int i; 49 | par = new Params(); 50 | srand (time(NULL)); 51 | par->setparams(infile); 52 | log = new Log(par); 53 | en = new EmulNet(par); 54 | en1 = new EmulNet(par); 55 | mp1 = (MP1Node **) malloc(par->EN_GPSZ * sizeof(MP1Node *)); 56 | mp2 = (MP2Node **) malloc(par->EN_GPSZ * sizeof(MP2Node *)); 57 | 58 | /* 59 | * Init all nodes 60 | */ 61 | for( i = 0; i < par->EN_GPSZ; i++ ) { 62 | Member *memberNode = new Member; 63 | memberNode->inited = false; 64 | Address *addressOfMemberNode = new Address(); 65 | Address joinaddr; 66 | joinaddr = getjoinaddr(); 67 | addressOfMemberNode = (Address *) en->ENinit(addressOfMemberNode, par->PORTNUM); 68 | mp1[i] = new MP1Node(memberNode, par, en, log, addressOfMemberNode); 69 | mp2[i] = new MP2Node(memberNode, par, en1, log, addressOfMemberNode); 70 | log->LOG(&(mp1[i]->getMemberNode()->addr), "APP"); 71 | log->LOG(&(mp2[i]->getMemberNode()->addr), "APP MP2"); 72 | delete addressOfMemberNode; 73 | } 74 | } 75 | 76 | /** 77 | * Destructor 78 | */ 79 | Application::~Application() { 80 | delete log; 81 | delete en; 82 | delete en1; 83 | for ( int i = 0; i < par->EN_GPSZ; i++ ) { 84 | delete mp1[i]; 85 | delete mp2[i]; 86 | } 87 | free(mp1); 88 | free(mp2); 89 | delete par; 90 | } 91 | 92 | /** 93 | * FUNCTION NAME: run 94 | * 95 | * DESCRIPTION: Main driver function of the Application layer 96 | */ 97 | int Application::run() 98 | { 99 | int i; 100 | int timeWhenAllNodesHaveJoined = 0; 101 | // boolean indicating if all nodes have joined 102 | bool allNodesJoined = false; 103 | srand(time(NULL)); 104 | 105 | // As time runs along 106 | for( par->globaltime = 0; par->globaltime < TOTAL_RUNNING_TIME; ++par->globaltime ) { 107 | // Run the membership protocol 108 | mp1Run(); 109 | 110 | // Wait for all nodes to join 111 | if ( par->allNodesJoined == nodeCount && !allNodesJoined ) { 112 | timeWhenAllNodesHaveJoined = par->getcurrtime(); 113 | allNodesJoined = true; 114 | } 115 | if ( par->getcurrtime() > timeWhenAllNodesHaveJoined + 50 ) { 116 | // Call the KV store functionalities 117 | mp2Run(); 118 | } 119 | // Fail some nodes 120 | //fail(); 121 | } 122 | 123 | // Clean up 124 | en->ENcleanup(); 125 | en1->ENcleanup(); 126 | 127 | for(i=0;i<=par->EN_GPSZ-1;i++) { 128 | mp1[i]->finishUpThisNode(); 129 | } 130 | 131 | return SUCCESS; 132 | } 133 | 134 | /** 135 | * FUNCTION NAME: mp1Run 136 | * 137 | * DESCRIPTION: This function performs all the membership protocol functionalities 138 | */ 139 | void Application::mp1Run() { 140 | int i; 141 | 142 | // For all the nodes in the system 143 | for( i = 0; i <= par->EN_GPSZ-1; i++) { 144 | 145 | /* 146 | * Receive messages from the network and queue them in the membership protocol queue 147 | */ 148 | if( par->getcurrtime() > (int)(par->STEP_RATE*i) && !(mp1[i]->getMemberNode()->bFailed) ) { 149 | // Receive messages from the network and queue them 150 | mp1[i]->recvLoop(); 151 | } 152 | 153 | } 154 | 155 | // For all the nodes in the system 156 | for( i = par->EN_GPSZ - 1; i >= 0; i-- ) { 157 | 158 | /* 159 | * Introduce nodes into the distributed system 160 | */ 161 | if( par->getcurrtime() == (int)(par->STEP_RATE*i) ) { 162 | // introduce the ith node into the system at time STEPRATE*i 163 | mp1[i]->nodeStart(JOINADDR, par->PORTNUM); 164 | cout<getMemberNode()->addr.getAddress() << endl; 165 | nodeCount += i; 166 | } 167 | 168 | /* 169 | * Handle all the messages in your queue and send heartbeats 170 | */ 171 | else if( par->getcurrtime() > (int)(par->STEP_RATE*i) && !(mp1[i]->getMemberNode()->bFailed) ) { 172 | // handle messages and send heartbeats 173 | mp1[i]->nodeLoop(); 174 | #ifdef DEBUGLOG 175 | if( (i == 0) && (par->globaltime % 500 == 0) ) { 176 | log->LOG(&mp1[i]->getMemberNode()->addr, "@@time=%d", par->getcurrtime()); 177 | } 178 | #endif 179 | } 180 | 181 | } 182 | } 183 | 184 | /** 185 | * FUNCTION NAME: mp2Run 186 | * 187 | * DESCRIPTION: This function performs all the key value store related functionalities 188 | * including: 189 | * 1) Ring operations 190 | * 2) CRUD operations 191 | */ 192 | void Application::mp2Run() { 193 | int i; 194 | 195 | // For all the nodes in the system 196 | for( i = 0; i <= par->EN_GPSZ-1; i++) { 197 | 198 | /* 199 | * 1) Update the ring 200 | * 2) Receive messages from the network and queue them in the KV store queue 201 | */ 202 | if ( par->getcurrtime() > (int)(par->STEP_RATE*i) && !mp2[i]->getMemberNode()->bFailed ) { 203 | if ( mp2[i]->getMemberNode()->inited && mp2[i]->getMemberNode()->inGroup ) { 204 | // Step 1 205 | mp2[i]->updateRing(); 206 | } 207 | // Step 2 208 | mp2[i]->recvLoop(); 209 | } 210 | } 211 | 212 | /** 213 | * Handle messages from the queue and update the DHT 214 | */ 215 | for ( i = par->EN_GPSZ-1; i >= 0; i-- ) { 216 | if ( par->getcurrtime() > (int)(par->STEP_RATE*i) && !mp2[i]->getMemberNode()->bFailed ) { 217 | mp2[i]->checkMessages(); 218 | } 219 | } 220 | 221 | /** 222 | * Insert a set of test key value pairs into the system 223 | */ 224 | if ( par->getcurrtime() == INSERT_TIME ) { 225 | insertTestKVPairs(); 226 | } 227 | 228 | /** 229 | * Test CRUD operations 230 | */ 231 | if ( par->getcurrtime() >= TEST_TIME ) { 232 | /************** 233 | * CREATE TEST 234 | **************/ 235 | /** 236 | * TEST 1: Checks if there are RF * NUMBER_OF_INSERTS CREATE SUCCESS message are in the log 237 | * 238 | */ 239 | if ( par->getcurrtime() == TEST_TIME && CREATE_TEST == par->CRUDTEST ) { 240 | cout<getcurrtime()<getcurrtime() == TEST_TIME && DELETE_TEST == par->CRUDTEST ) { 253 | deleteTest(); 254 | } // End of delete test 255 | 256 | /************* 257 | * READ TESTS 258 | *************/ 259 | /** 260 | * TEST 1: Read a key. Check for correct value being read in quorum of replicas 261 | * 262 | * Wait for some time after TEST 1 263 | * 264 | * TEST 2: Fail a single replica of a key. Check for correct value of the key 265 | * being read in quorum of replicas 266 | * 267 | * Wait for STABILIZE_TIME after TEST 2 (stabilization protocol should ensure at least 268 | * 3 replicas for all keys at all times) 269 | * 270 | * TEST 3 part 1: Fail two replicas of a key. Read the key and check for READ FAIL message in the log. 271 | * READ should fail because quorum replicas of the key are not up 272 | * 273 | * Wait for another STABILIZE_TIME after TEST 3 part 1 (stabilization protocol should ensure at least 274 | * 3 replicas for all keys at all times) 275 | * 276 | * TEST 3 part 2: Read the same key as TEST 3 part 1. Check for correct value of the key 277 | * being read in quorum of replicas 278 | * 279 | * Wait for some time after TEST 3 part 2 280 | * 281 | * TEST 4: Fail a non-replica. Check for correct value of the key 282 | * being read in quorum of replicas 283 | * 284 | * TEST 5: Read a non-existent key. Check for a READ FAIL message in the log 285 | * 286 | */ 287 | else if ( par->getcurrtime() >= TEST_TIME && READ_TEST == par->CRUDTEST ) { 288 | readTest(); 289 | } // end of read test 290 | 291 | /*************** 292 | * UPDATE TESTS 293 | ***************/ 294 | /** 295 | * TEST 1: Update a key. Check for correct new value being updated in quorum of replicas 296 | * 297 | * Wait for some time after TEST 1 298 | * 299 | * TEST 2: Fail a single replica of a key. Update the key. Check for correct new value of the key 300 | * being updated in quorum of replicas 301 | * 302 | * Wait for STABILIZE_TIME after TEST 2 (stabilization protocol should ensure at least 303 | * 3 replicas for all keys at all times) 304 | * 305 | * TEST 3 part 1: Fail two replicas of a key. Update the key and check for READ FAIL message in the log 306 | * UPDATE should fail because quorum replicas of the key are not up 307 | * 308 | * Wait for another STABILIZE_TIME after TEST 3 part 1 (stabilization protocol should ensure at least 309 | * 3 replicas for all keys at all times) 310 | * 311 | * TEST 3 part 2: Update the same key as TEST 3 part 1. Check for correct new value of the key 312 | * being update in quorum of replicas 313 | * 314 | * Wait for some time after TEST 3 part 2 315 | * 316 | * TEST 4: Fail a non-replica. Check for correct new value of the key 317 | * being updated in quorum of replicas 318 | * 319 | * TEST 5: Update a non-existent key. Check for a UPDATE FAIL message in the log 320 | * 321 | */ 322 | else if ( par->getcurrtime() >= TEST_TIME && UPDATE_TEST == par->CRUDTEST ) { 323 | updateTest(); 324 | } // End of update test 325 | 326 | } // end of if ( par->getcurrtime == TEST_TIME) 327 | } 328 | 329 | /** 330 | * FUNCTION NAME: fail 331 | * 332 | * DESCRIPTION: This function controls the failure of nodes 333 | * 334 | * Note: this is used only by MP1 335 | */ 336 | void Application::fail() { 337 | int i, removed; 338 | 339 | // fail half the members at time t=400 340 | if( par->DROP_MSG && par->getcurrtime() == 50 ) { 341 | par->dropmsg = 1; 342 | } 343 | 344 | if( par->SINGLE_FAILURE && par->getcurrtime() == 100 ) { 345 | removed = (rand() % par->EN_GPSZ); 346 | #ifdef DEBUGLOG 347 | log->LOG(&mp1[removed]->getMemberNode()->addr, "Node failed at time=%d", par->getcurrtime()); 348 | #endif 349 | mp1[removed]->getMemberNode()->bFailed = true; 350 | } 351 | else if( par->getcurrtime() == 100 ) { 352 | removed = rand() % par->EN_GPSZ/2; 353 | for ( i = removed; i < removed + par->EN_GPSZ/2; i++ ) { 354 | #ifdef DEBUGLOG 355 | log->LOG(&mp1[i]->getMemberNode()->addr, "Node failed at time = %d", par->getcurrtime()); 356 | #endif 357 | mp1[i]->getMemberNode()->bFailed = true; 358 | } 359 | } 360 | 361 | if( par->DROP_MSG && par->getcurrtime() == 300) { 362 | par->dropmsg=0; 363 | } 364 | 365 | } 366 | 367 | /** 368 | * FUNCTION NAME: getjoinaddr 369 | * 370 | * DESCRIPTION: This function returns the address of the coordinator 371 | */ 372 | Address Application::getjoinaddr(void){ 373 | //trace.funcEntry("Application::getjoinaddr"); 374 | Address joinaddr; 375 | joinaddr.init(); 376 | *(int *)(&(joinaddr.addr))=1; 377 | *(short *)(&(joinaddr.addr[4]))=0; 378 | //trace.funcExit("Application::getjoinaddr", SUCCESS); 379 | return joinaddr; 380 | } 381 | 382 | /** 383 | * FUNCTION NAME: findARandomNodeThatIsAlive 384 | * 385 | * DESCRTPTION: Finds a random node in the ring that is alive 386 | */ 387 | int Application::findARandomNodeThatIsAlive() { 388 | int number; 389 | do { 390 | number = (rand()%par->EN_GPSZ); 391 | }while (mp2[number]->getMemberNode()->bFailed); 392 | return number; 393 | } 394 | 395 | /** 396 | * FUNCTION NAME: initTestKVPairs 397 | * 398 | * DESCRIPTION: Init NUMBER_OF_INSERTS test KV pairs in the map 399 | */ 400 | void Application::initTestKVPairs() { 401 | srand(time(NULL)); 402 | int i; 403 | string key; 404 | key.clear(); 405 | testKVPairs.clear(); 406 | int alphanumLen = sizeof(alphanum) - 1; 407 | while ( testKVPairs.size() != NUMBER_OF_INSERTS ) { 408 | for ( i = 0; i < KEY_LENGTH; i++ ) { 409 | key.push_back(alphanum[rand()%alphanumLen]); 410 | } 411 | string value = "value" + to_string(rand()%NUMBER_OF_INSERTS); 412 | testKVPairs[key] = value; 413 | key.clear(); 414 | } 415 | } 416 | 417 | /** 418 | * FUNCTION NAME: insertTestKVPairs 419 | * 420 | * DESCRIPTION: This function inserts test KV pairs into the system 421 | */ 422 | void Application::insertTestKVPairs() { 423 | int number = 0; 424 | 425 | /* 426 | * Init a few test key value pairs 427 | */ 428 | initTestKVPairs(); 429 | 430 | for ( map::iterator it = testKVPairs.begin(); it != testKVPairs.end(); ++it ) { 431 | // Step 1. Find a node that is alive 432 | number = findARandomNodeThatIsAlive(); 433 | 434 | // Step 2. Issue a create operation 435 | log->LOG(&mp2[number]->getMemberNode()->addr, "CREATE OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), it->second.c_str(), par->getcurrtime()); 436 | mp2[number]->clientCreate(it->first, it->second); 437 | } 438 | 439 | cout<::iterator it = testKVPairs.begin(); 454 | for ( int i = 0; i < testKVPairs.size()/2; i++ ) { 455 | it++; 456 | 457 | // Step 1.a. Find a node that is alive 458 | number = findARandomNodeThatIsAlive(); 459 | 460 | // Step 1.b. Issue a delete operation 461 | log->LOG(&mp2[number]->getMemberNode()->addr, "DELETE OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), it->second.c_str(), par->getcurrtime()); 462 | mp2[number]->clientDelete(it->first); 463 | } 464 | 465 | /** 466 | * Test 2: Delete a non-existent key 467 | */ 468 | cout<LOG(&mp2[number]->getMemberNode()->addr, "DELETE OPERATION KEY: %s at time: %d", invalidKey.c_str(), par->getcurrtime()); 475 | mp2[number]->clientDelete(invalidKey); 476 | } 477 | 478 | /** 479 | * FUNCTION NAME: readTest 480 | * 481 | * DESCRIPTION: Test the read API of the KV store 482 | */ 483 | void Application::readTest() { 484 | 485 | // Step 0. Key to be read 486 | // This key is used for all read tests 487 | map::iterator it = testKVPairs.begin(); 488 | int number; 489 | vector replicas; 490 | int replicaIdToFail = TERTIARY; 491 | int nodeToFail; 492 | bool failedOneNode = false; 493 | 494 | /** 495 | * Test 1: Test if value of a single read operation is read correctly in quorum number of nodes 496 | */ 497 | if ( par->getcurrtime() == TEST_TIME ) { 498 | // Step 1.a. Find a node that is alive 499 | number = findARandomNodeThatIsAlive(); 500 | 501 | // Step 1.b Do a read operation 502 | cout<LOG(&mp2[number]->getMemberNode()->addr, "READ OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), it->second.c_str(), par->getcurrtime()); 504 | mp2[number]->clientRead(it->first); 505 | } 506 | 507 | /** end of test1 **/ 508 | 509 | /** 510 | * Test 2: FAIL ONE REPLICA. Test if value is read correctly in quorum number of nodes after ONE OF THE REPLICAS IS FAILED 511 | */ 512 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME) ) { 513 | // Step 2.a Find a node that is alive and assign it as number 514 | number = findARandomNodeThatIsAlive(); 515 | 516 | // Step 2.b Find the replicas of this key 517 | replicas.clear(); 518 | replicas = mp2[number]->findNodes(it->first); 519 | // if less than quorum replicas are found then exit 520 | if ( replicas.size() < (RF-1) ) { 521 | cout<LOG(&mp2[number]->getMemberNode()->addr, "Could not find at least quorum replicas for this key. Exiting!!! size of replicas vector: %d", replicas.size()); 523 | exit(1); 524 | } 525 | 526 | // Step 2.c Fail a replica 527 | for ( int i = 0; i < par->EN_GPSZ; i++ ) { 528 | if ( mp2[i]->getMemberNode()->addr.getAddress() == replicas.at(replicaIdToFail).getAddress()->getAddress() ) { 529 | if ( !mp2[i]->getMemberNode()->bFailed ) { 530 | nodeToFail = i; 531 | failedOneNode = true; 532 | break; 533 | } 534 | else { 535 | // Since we fail at most two nodes, one of the replicas must be alive 536 | if ( replicaIdToFail > 0 ) { 537 | replicaIdToFail--; 538 | } 539 | else { 540 | failedOneNode = false; 541 | } 542 | } 543 | } 544 | } 545 | if ( failedOneNode ) { 546 | log->LOG(&mp2[nodeToFail]->getMemberNode()->addr, "Node failed at time=%d", par->getcurrtime()); 547 | mp2[nodeToFail]->getMemberNode()->bFailed = true; 548 | mp1[nodeToFail]->getMemberNode()->bFailed = true; 549 | cout<LOG(&mp2[number]->getMemberNode()->addr, "Could not fail a node"); 554 | cout<<"Could not fail a node. Exiting!!!"; 555 | exit(1); 556 | } 557 | 558 | number = findARandomNodeThatIsAlive(); 559 | 560 | // Step 2.d Issue a read 561 | cout<LOG(&mp2[number]->getMemberNode()->addr, "READ OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), it->second.c_str(), par->getcurrtime()); 563 | mp2[number]->clientRead(it->first); 564 | 565 | failedOneNode = false; 566 | } 567 | 568 | /** end of test 2 **/ 569 | 570 | /** 571 | * Test 3 part 1: Fail two replicas. Test if value is read correctly in quorum number of nodes after TWO OF THE REPLICAS ARE FAILED 572 | */ 573 | // Wait for STABILIZE_TIME and fail two replicas 574 | if ( par->getcurrtime() >= (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME) ) { 575 | vector nodesToFail; 576 | nodesToFail.clear(); 577 | int count = 0; 578 | 579 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME) ) { 580 | // Step 3.a. Find a node that is alive 581 | number = findARandomNodeThatIsAlive(); 582 | 583 | // Get the keys replicas 584 | replicas.clear(); 585 | replicas = mp2[number]->findNodes(it->first); 586 | 587 | // Step 3.b. Fail two replicas 588 | //cout<<"REPLICAS SIZE: "< 2 ) { 590 | replicaIdToFail = TERTIARY; 591 | while ( count != 2 ) { 592 | int i = 0; 593 | while ( i != par->EN_GPSZ ) { 594 | if ( mp2[i]->getMemberNode()->addr.getAddress() == replicas.at(replicaIdToFail).getAddress()->getAddress() ) { 595 | if ( !mp2[i]->getMemberNode()->bFailed ) { 596 | nodesToFail.emplace_back(i); 597 | replicaIdToFail--; 598 | count++; 599 | break; 600 | } 601 | else { 602 | // Since we fail at most two nodes, one of the replicas must be alive 603 | if ( replicaIdToFail > 0 ) { 604 | replicaIdToFail--; 605 | } 606 | } 607 | } 608 | i++; 609 | } 610 | } 611 | } 612 | else { 613 | // If the code reaches here. Test your stabilization protocol 614 | cout<LOG(&mp2[nodesToFail.at(i)]->getMemberNode()->addr, "Node failed at time=%d", par->getcurrtime()); 621 | mp2[nodesToFail.at(i)]->getMemberNode()->bFailed = true; 622 | mp1[nodesToFail.at(i)]->getMemberNode()->bFailed = true; 623 | cout<LOG(&mp2[number]->getMemberNode()->addr, "Could not fail two nodes"); 629 | //cout<<"COUNT: " <LOG(&mp2[number]->getMemberNode()->addr, "READ OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), it->second.c_str(), par->getcurrtime()); 639 | // This read should fail since at least quorum nodes are not alive 640 | mp2[number]->clientRead(it->first); 641 | } 642 | 643 | /** 644 | * TEST 3 part 2: After failing two replicas and waiting for STABILIZE_TIME, issue a read 645 | */ 646 | // Step 3.d Wait for stabilization protocol to kick in 647 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME + STABILIZE_TIME) ) { 648 | number = findARandomNodeThatIsAlive(); 649 | // Step 3.e Issue a read 650 | cout<LOG(&mp2[number]->getMemberNode()->addr, "READ OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), it->second.c_str(), par->getcurrtime()); 652 | // This read should be successful 653 | mp2[number]->clientRead(it->first); 654 | } 655 | } 656 | 657 | /** end of test 3 **/ 658 | 659 | /** 660 | * Test 4: FAIL A NON-REPLICA. Test if value is read correctly in quorum number of nodes after a NON-REPLICA IS FAILED 661 | */ 662 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME + STABILIZE_TIME + LAST_FAIL_TIME ) ) { 663 | // Step 4.a. Find a node that is alive 664 | number = findARandomNodeThatIsAlive(); 665 | 666 | // Step 4.b Find a non - replica for this key 667 | replicas.clear(); 668 | replicas = mp2[number]->findNodes(it->first); 669 | for ( int i = 0; i < par->EN_GPSZ; i++ ) { 670 | if ( !mp2[i]->getMemberNode()->bFailed ) { 671 | if ( mp2[i]->getMemberNode()->addr.getAddress() != replicas.at(PRIMARY).getAddress()->getAddress() && 672 | mp2[i]->getMemberNode()->addr.getAddress() != replicas.at(SECONDARY).getAddress()->getAddress() && 673 | mp2[i]->getMemberNode()->addr.getAddress() != replicas.at(TERTIARY).getAddress()->getAddress() ) { 674 | // Step 4.c Fail a non-replica node 675 | log->LOG(&mp2[i]->getMemberNode()->addr, "Node failed at time=%d", par->getcurrtime()); 676 | mp2[i]->getMemberNode()->bFailed = true; 677 | mp1[i]->getMemberNode()->bFailed = true; 678 | failedOneNode = true; 679 | cout<LOG(&mp2[number]->getMemberNode()->addr, "Could not fail a node(non-replica)"); 687 | cout<<"Could not fail a node(non-replica). Exiting!!!"; 688 | exit(1); 689 | } 690 | 691 | number = findARandomNodeThatIsAlive(); 692 | 693 | // Step 4.d Issue a read operation 694 | cout<LOG(&mp2[number]->getMemberNode()->addr, "READ OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), it->second.c_str(), par->getcurrtime()); 696 | // This read should fail since at least quorum nodes are not alive 697 | mp2[number]->clientRead(it->first); 698 | } 699 | 700 | /** end of test 4 **/ 701 | 702 | /** 703 | * Test 5: Read a non-existent key. 704 | */ 705 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME + STABILIZE_TIME + LAST_FAIL_TIME ) ) { 706 | string invalidKey = "invalidKey"; 707 | 708 | // Step 5.a Find a node that is alive 709 | number = findARandomNodeThatIsAlive(); 710 | 711 | // Step 5.b Issue a read operation 712 | cout<LOG(&mp2[number]->getMemberNode()->addr, "READ OPERATION KEY: %s at time: %d", invalidKey.c_str(), par->getcurrtime()); 714 | // This read should fail since at least quorum nodes are not alive 715 | mp2[number]->clientRead(invalidKey); 716 | } 717 | 718 | /** end of test 5 **/ 719 | 720 | } 721 | 722 | /** 723 | * FUNCTION NAME: updateTest 724 | * 725 | * DECRIPTION: This tests the update API of the KV Store 726 | */ 727 | void Application::updateTest() { 728 | // Step 0. Key to be updated 729 | // This key is used for all update tests 730 | map::iterator it = testKVPairs.begin(); 731 | it++; 732 | string newValue = "newValue"; 733 | int number; 734 | vector replicas; 735 | int replicaIdToFail = TERTIARY; 736 | int nodeToFail; 737 | bool failedOneNode = false; 738 | 739 | /** 740 | * Test 1: Test if value is updated correctly in quorum number of nodes 741 | */ 742 | if ( par->getcurrtime() == TEST_TIME ) { 743 | // Step 1.a. Find a node that is alive 744 | number = findARandomNodeThatIsAlive(); 745 | 746 | // Step 1.b Do a update operation 747 | cout<LOG(&mp2[number]->getMemberNode()->addr, "UPDATE OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), newValue.c_str(), par->getcurrtime()); 749 | mp2[number]->clientUpdate(it->first, newValue); 750 | } 751 | 752 | /** end of test 1 **/ 753 | 754 | /** 755 | * Test 2: FAIL ONE REPLICA. Test if value is updated correctly in quorum number of nodes after ONE OF THE REPLICAS IS FAILED 756 | */ 757 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME) ) { 758 | // Step 2.a Find a node that is alive and assign it as number 759 | number = findARandomNodeThatIsAlive(); 760 | 761 | // Step 2.b Find the replicas of this key 762 | replicas.clear(); 763 | replicas = mp2[number]->findNodes(it->first); 764 | // if quorum replicas are not found then exit 765 | if ( replicas.size() < RF-1 ) { 766 | log->LOG(&mp2[number]->getMemberNode()->addr, "Could not find at least quorum replicas for this key. Exiting!!! size of replicas vector: %d", replicas.size()); 767 | cout<EN_GPSZ; i++ ) { 773 | if ( mp2[i]->getMemberNode()->addr.getAddress() == replicas.at(replicaIdToFail).getAddress()->getAddress() ) { 774 | if ( !mp2[i]->getMemberNode()->bFailed ) { 775 | nodeToFail = i; 776 | failedOneNode = true; 777 | break; 778 | } 779 | else { 780 | // Since we fail at most two nodes, one of the replicas must be alive 781 | if ( replicaIdToFail > 0 ) { 782 | replicaIdToFail--; 783 | } 784 | else { 785 | failedOneNode = false; 786 | } 787 | } 788 | } 789 | } 790 | if ( failedOneNode ) { 791 | log->LOG(&mp2[nodeToFail]->getMemberNode()->addr, "Node failed at time=%d", par->getcurrtime()); 792 | mp2[nodeToFail]->getMemberNode()->bFailed = true; 793 | mp1[nodeToFail]->getMemberNode()->bFailed = true; 794 | cout<LOG(&mp2[number]->getMemberNode()->addr, "Could not fail a node"); 799 | cout<<"Could not fail a node. Exiting!!!"; 800 | exit(1); 801 | } 802 | 803 | number = findARandomNodeThatIsAlive(); 804 | 805 | // Step 2.d Issue a update 806 | cout<LOG(&mp2[number]->getMemberNode()->addr, "UPDATE OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), newValue.c_str(), par->getcurrtime()); 808 | mp2[number]->clientUpdate(it->first, newValue); 809 | 810 | failedOneNode = false; 811 | } 812 | 813 | /** end of test 2 **/ 814 | 815 | /** 816 | * Test 3 part 1: Fail two replicas. Test if value is updated correctly in quorum number of nodes after TWO OF THE REPLICAS ARE FAILED 817 | */ 818 | if ( par->getcurrtime() >= (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME) ) { 819 | 820 | vector nodesToFail; 821 | nodesToFail.clear(); 822 | int count = 0; 823 | 824 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME) ) { 825 | // Step 3.a. Find a node that is alive 826 | number = findARandomNodeThatIsAlive(); 827 | 828 | // Get the keys replicas 829 | replicas.clear(); 830 | replicas = mp2[number]->findNodes(it->first); 831 | 832 | // Step 3.b. Fail two replicas 833 | if ( replicas.size() > 2 ) { 834 | replicaIdToFail = TERTIARY; 835 | while ( count != 2 ) { 836 | int i = 0; 837 | while ( i != par->EN_GPSZ ) { 838 | if ( mp2[i]->getMemberNode()->addr.getAddress() == replicas.at(replicaIdToFail).getAddress()->getAddress() ) { 839 | if ( !mp2[i]->getMemberNode()->bFailed ) { 840 | nodesToFail.emplace_back(i); 841 | replicaIdToFail--; 842 | count++; 843 | break; 844 | } 845 | else { 846 | // Since we fail at most two nodes, one of the replicas must be alive 847 | if ( replicaIdToFail > 0 ) { 848 | replicaIdToFail--; 849 | } 850 | } 851 | } 852 | i++; 853 | } 854 | } 855 | } 856 | else { 857 | // If the code reaches here. Test your stabilization protocol 858 | cout<LOG(&mp2[nodesToFail.at(i)]->getMemberNode()->addr, "Node failed at time=%d", par->getcurrtime()); 864 | mp2[nodesToFail.at(i)]->getMemberNode()->bFailed = true; 865 | mp1[nodesToFail.at(i)]->getMemberNode()->bFailed = true; 866 | cout<LOG(&mp2[number]->getMemberNode()->addr, "Could not fail two nodes"); 872 | cout<<"Could not fail two nodes. Exiting!!!"; 873 | exit(1); 874 | } 875 | 876 | number = findARandomNodeThatIsAlive(); 877 | 878 | // Step 3.c Issue an update 879 | cout<LOG(&mp2[number]->getMemberNode()->addr, "UPDATE OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), newValue.c_str(), par->getcurrtime()); 881 | // This update should fail since at least quorum nodes are not alive 882 | mp2[number]->clientUpdate(it->first, newValue); 883 | } 884 | 885 | /** 886 | * TEST 3 part 2: After failing two replicas and waiting for STABILIZE_TIME, issue an update 887 | */ 888 | // Step 3.d Wait for stabilization protocol to kick in 889 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME + STABILIZE_TIME) ) { 890 | number = findARandomNodeThatIsAlive(); 891 | // Step 3.e Issue a update 892 | cout<LOG(&mp2[number]->getMemberNode()->addr, "UPDATE OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), newValue.c_str(), par->getcurrtime()); 894 | // This update should be successful 895 | mp2[number]->clientUpdate(it->first, newValue); 896 | } 897 | } 898 | 899 | /** end of test 3 **/ 900 | 901 | /** 902 | * Test 4: FAIL A NON-REPLICA. Test if value is read correctly in quorum number of nodes after a NON-REPLICA IS FAILED 903 | */ 904 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME + STABILIZE_TIME + LAST_FAIL_TIME ) ) { 905 | // Step 4.a. Find a node that is alive 906 | number = findARandomNodeThatIsAlive(); 907 | 908 | // Step 4.b Find a non - replica for this key 909 | replicas.clear(); 910 | replicas = mp2[number]->findNodes(it->first); 911 | for ( int i = 0; i < par->EN_GPSZ; i++ ) { 912 | if ( !mp2[i]->getMemberNode()->bFailed ) { 913 | if ( mp2[i]->getMemberNode()->addr.getAddress() != replicas.at(PRIMARY).getAddress()->getAddress() && 914 | mp2[i]->getMemberNode()->addr.getAddress() != replicas.at(SECONDARY).getAddress()->getAddress() && 915 | mp2[i]->getMemberNode()->addr.getAddress() != replicas.at(TERTIARY).getAddress()->getAddress() ) { 916 | // Step 4.c Fail a non-replica node 917 | log->LOG(&mp2[i]->getMemberNode()->addr, "Node failed at time=%d", par->getcurrtime()); 918 | mp2[i]->getMemberNode()->bFailed = true; 919 | mp1[i]->getMemberNode()->bFailed = true; 920 | failedOneNode = true; 921 | cout<LOG(&mp2[number]->getMemberNode()->addr, "Could not fail a node(non-replica)"); 930 | cout<<"Could not fail a node(non-replica). Exiting!!!"; 931 | exit(1); 932 | } 933 | 934 | number = findARandomNodeThatIsAlive(); 935 | 936 | // Step 4.d Issue a update operation 937 | cout<LOG(&mp2[number]->getMemberNode()->addr, "UPDATE OPERATION KEY: %s VALUE: %s at time: %d", it->first.c_str(), newValue.c_str(), par->getcurrtime()); 939 | // This read should fail since at least quorum nodes are not alive 940 | mp2[number]->clientUpdate(it->first, newValue); 941 | } 942 | 943 | /** end of test 4 **/ 944 | 945 | /** 946 | * Test 5: Udpate a non-existent key. 947 | */ 948 | if ( par->getcurrtime() == (TEST_TIME + FIRST_FAIL_TIME + STABILIZE_TIME + STABILIZE_TIME + LAST_FAIL_TIME ) ) { 949 | string invalidKey = "invalidKey"; 950 | string invalidValue = "invalidValue"; 951 | 952 | // Step 5.a Find a node that is alive 953 | number = findARandomNodeThatIsAlive(); 954 | 955 | // Step 5.b Issue a read operation 956 | cout<LOG(&mp2[number]->getMemberNode()->addr, "UPDATE OPERATION KEY: %s VALUE: %s at time: %d", invalidKey.c_str(), invalidValue.c_str(), par->getcurrtime()); 958 | // This read should fail since at least quorum nodes are not alive 959 | mp2[number]->clientUpdate(invalidKey, invalidValue); 960 | } 961 | 962 | /** end of test 5 **/ 963 | 964 | } 965 | --------------------------------------------------------------------------------