In this project, you will be testing the Partition Tolerance of a NoSQL DB using the procedures described in the following article: https://www.infoq.com/articles/jepsen. In addition, you will be developing a Data Service API in Go and deploying the API on AWS.
- Select one CP and one AP NoSQL Database.
- For example: Mongo and Riak (Note: Other NoSQL DBs that can be configured in CP or AP mode are also allowed).
- For each Database:
- Set up your cluster as AWS EC2 Instances. (# of Nodes and Topology is open per your design)
- Set up the Experiments (i.e. Test Cases), run the Experiments and record the results for both AP and CP.
- For one of your NoSQL database, develop the Go API and integrate the API with the Team SaaS App from the Hackathon Project.
- Project must be use in a GitHub Repo (assigned by TA) to maintain source code, documentation and design diagrams.
- Repo will be maintain in: https://github.com/nguyensjsu (Links to an external site.)Links to an external site.
- Keep a Project Journal (as a markdown document) recording weekly progress, challenges, tests and test results.
- All documentation must be written in GitHub Markdown (including diagrams) and committed to GitHub.
Objective ---> Initial Research on the basic understanding and functionality of the project, studying the requriements, identifying the key aspects and figuring out the necessary technologies to be used.
- Studying NoSQL Databases
- Understanding CAP Theorem
- Understanding the requirements and architecture of the project through the article - https://www.infoq.com/articles/jepsen
- Researching on the NoSQL DB to be used for both CP and AP.
- Understanding Partition Tolerance and its pitfalls.
- Studying other related terminologies required for the project.
What is CAP Theorem?
ANS: States that it is impossible for a distributed data store to simultaneously provide more than two of the following three guarantees:-
- Consistency - Every read receives the most recent write or an error.
- Availability - Every request receives a response that is not an error.
- Partition Tolerance - The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes.
What are the requirements of the project that have been understood from the project?
Ans: We are supposed to create a cluster of NoSQL DBS wherein if one of the system is not able to communicate with the rest of the systems due to network failure then also our API should be able to read stale data. Thus showing high availability of our cluster (partition tolerant system).
What is partition tolerance and its pitfalls?
Ans: Partition tolerance in CAP means tolerance to a network partition. An example of NP is when two nodes cant talk to each other, but there are clients who are able to talk to either one of both of these nodes. An AP system is able to function during the network split, while being able to provide various forms of eventual consistency.
What are the benefits of using MongoDB (NoSQL) for this project?
Ans: MongoDB is easy to scale. Supports replication and high availability. Use internal memory for storing the working set, enabling faster access of data. Also, with right configuration of replica set, partition tolerance can be tested on MongoDB.
Objective ---> MongoDB cluster setup with replicaset configuration.
- Launch instance
- Select'Amazon Linux AMI' type instance.
- Instance Type: t2.micro
- Number of instances: 1
- Network: VPC cmpe281 (N.California Region)
- Subnet: Public
- Auto-assign Public IP: Enable
- Security group: MongoDB (open ports = 22, 27017)
- Key-value pair: cmpe281
- SSH into the MongoDB jumpbox using the keyValue pair.
(Use Putty for Windows - a free SSH client for Windows platform)
- sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
- echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list
- sudo apt-get update
- sudo apt-get install -y mongodb-org
- Generate your key file:
$ openssl rand -base64 741 > keyFile - Create and store your key file in the /opt/mongo directory:
$ sudo mkdir -p /opt/mongodb - Move the keyfile to /opt/mongo, and assign it the correct permissions:
$ sudo cp keyFile /opt/mongodb - Update the file ownership.
$ sudo chown mongodb:mongodb /opt/mongodb/keyFile
$ sudo chmod 0600 /opt/mongodb/keyFile
$ sudo nano /etc/mongod.conf
- remove or comment out bindIp: 127.0.0.1
replace with bindIp: 0.0.0.0 (binds on all ips) - Uncomment security section & add keyFile: /opt/mongodb/keyFile
- Uncomment replication section. Name Replica Set = cmpe281
$ sudo nano /etc/systemd/system/mongod.service
[Unit]
Description=High-performance, schema-free document-oriented database
After=network.target
[Service]
User=mongodb
ExecStart=/usr/bin/mongod --quiet --config /etc/mongod.conf
[Install]
WantedBy=multi-user.target
$ sudo systemctl enable mongod.service
$ sudo service mongod restart
$ sudo service mongod status
- Select the primary instance -> Actions -> Image -> Create Image
- Name = MongoDB AMI
- We will be setting up the MongoDB cluster in three regions, N. California, Oregon and Ohio.
- These MongoDB instances will communicate with each other using the VPC Peering technique.
- Launch Instance 'Ubuntu Server 16.04 LTS (HVM)' type.
- My AMIs: 'MongoDB AMI'
- Instance Type: t2.micro
- Number of instances: 3
- Network: VPC cmpe281
- Subnet: Private
- Auto-assign Public IP: Disable
- Security Group: MongoDB (22, 27017)
- Key-value pair: cmpe281
- Launch Instance 'Ubuntu Server 16.04 LTS (HVM)' type.
- My AMIs: 'MongoDB AMI'
- Instance Type: t2.micro
- Number of instances: 2
- Network: VPC cmpe281-oregon
- Subnet: Private
- Auto-assign Public IP: Disable
- Security Group: MongoDB (22, 27017)
- Key-value pair: cmpe281-oregon
- Launch Instance 'Ubuntu Server 16.04 LTS (HVM)' type.
- My AMIs: 'MongoDB AMI'
- Instance Type: t2.micro
- Number of instances: 1
- Network: VPC cmpe281-ohio
- Subnet: Private
- Auto-assign Public IP: Disable
- Security Group: MongoDB (22, 27017)
- Key-value pair: cmpe281-ohio
- To begin with, one primary and two secondary nodes will be in N. California Region tow other secondary nodes will be in the Oregon region and one secondary node in the Ohio region.
- SSH into the primary instance via the MongoDB jumpbox.
- Connecting to private instance from a public instance:
- SSH into the Public instance (jumpbox in this case).
- Get .pem file into the root folder from local. (Use Winscp for windows).
- Make sure private instance has port 22 opened.
- sudo ssh -i cmpe281.pem ec2-user@
$ mongo
$ rs.initiate()
$ rs.status()
$ mongo
$ use admin
$ db.createUser( {
user: "admin",
pwd: "cmpe281",
roles: [{ role: "root", db: "admin" }]
});
$ mongo
$ use admin
$ db.auth("admin","cmpe281")
$ rs.initiate()
$ rs.add("10.0.1.9") // secondary1
$ rs.add("10.0.1.84") // secondary2
$ rs.add("11.0.1.237") // secondary3
$ rs.add("11.0.1.54") // secondary4
$ rs.add("12.0.2.171") // secondary5
$ rs.status()
Objective ---> Riak cluster setup.
- AMI: Riak KV 2.2 Series
- Instance Type: t2.micro
- VPC: cmpe281
- Network: private subnet
- Auto Public IP: no
- Security Group: riak-cluster
- SG Open Ports: (see below)
- Key Pair: cmpe281-us-west-1
- Riak Cluster Security Group (Open Ports): 22(SSH), 8087 (Riak Protocol Buffers Interface), 8098 (Riak HTTP Interface).
- In order to allow communication between the Riak instances, need to add additional rules within this security group.
- The additional rules are - 4369, 6000-7999, 8099, 9080 (Set the source to the current security group).
- AMI: Amazon Linux AMI 2018.03.0 (HVM)
- Instance Type: t2.micro
- VPC: cmpe281
- Network: public subnet
- Auto Public IP: yes
- Security Group: cmpe281-dmz
- SG Open Ports: 22, 80, 443
- Key Pair: cmpe281-us-west-1
- SSH into the Jump Box.
- SSH into the Riak instance via the Jump Box. (Connecting to private instance from a public instance).
- sudo riak start (Run the command on all instances)
- sudo riak-admin cluster join riak@<ip.of.first.node> (Run the command in all the other instances)
- sudo riak-admin cluster plan
- sudo riak-admin cluster commit
- sudo riak-admin member_status
- sudo riak-admin cluster status
- The Riak cluster is now running on the AWS.
- sudo riak-admin bucket-type create subjects
- sudo riak-admin bucket-type activate subjects
- Since all our 5 Riak Nodes are in the private subnet, we will need Kong in order to access those instances.
- sudo yum update -y
- sudo yum install -y docker
- sudo service docker start
- sudo usermod -a -G docker ec2-user
$ sudo docker network create --driver bridge kong
$ sudo docker network ls
$ sudo docker run -d --name kong-database --network kong -p 9402:9402 cassandra:2.2
$ sudo docker run -d --name kong1 --network kong -e "KONG_DATABASE=cassandra" -e "KONG_CASSANDRA_CONTACT_POINTS=kong-db" -e "KONG_PG_HOST=kong-db" -p 8000:8000 -p 8443:8443 -p 8001:8001 -p 7946:7946 -p 7946:7946/udp kong:0.9.9
$ sudo docker ps --all --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}\t"
- Add a security group named "Kong" with open ports 8000 and 8001 to configure Kong (via Postman)
** Repeat the above steps for the remaining 4 nodes as well. (Replace the Private IP in the upstream URL)
GET http://13.56.164.9:8000/node1/ping
GET http://13.56.164.9:8000/node2/ping
GET http://13.56.164.9:8000/node3/ping
GET http://13.56.164.9:8000/node4/ping
GET http://13.56.164.9:8000/node5/ping
GET http://13.56.164.9:8000/node1/types/subjects/buckets/cmpe281/props
Objective ---> CRUD operations and GO API setup.
- Understand the GO API functionality.
- Initial setup to run goapi
- Method for connecting mongodb using mgo
- Implementing basic CRUD operations for Mongodb in GO
Kindly refer to the source code for CRUD API implementaion.
Objective ---> Testing the CP and AP properties and recording a video for the same.
$ rs.slaveOk() // on all secondary nodes
- Enter some dummy data on the primary and check if that data is replicated on the secondary nodes in all the three regions.
$ use teams
$ db.users.save( {username:"Nitish"} )
$ db.users.find()
EXPECTED OUTPUT - Should be able to read data inserted in primary on all the secondaries.
ACTUAL OUTPUT- Able to read data on all secondaries.
$ Test passed.
- Create a network partition between the primary instance and any one of the other secondary instances.
- In order to achieve this, we can delete the VPC peering connection or just delete the CIDR block of the other VPC from the route table between two regions (eg: California and Ohio in this case).
EXPECTED OUTPUT - Should be able to read updated data.
ACTUAL OUTPUT- Updated data is being read.
$ Test passed.
- Create a network partition between the seondary node from Ohio and both other regions i.e. Oregon and California.
- In this way, we are isolating the secondary instance in Ohio completely.
- To achieve this, we can delete the CIDR blocks of both the regions (Oregon and N. California) from the Ohio route tables and also delete the CIDR block of the Ohio region from the route tables of Oregon and N. California regions respectively.
EXPECTED OUTPUT - Should be able to read stale data.
ACTUAL OUTPUT- Stale data is being read.
$ Test passed.
** Add the CIDR blocks back to the route tables of the VPCs, thus enabling communication between them.
- Step down the primary for 12 seconds using the command:
$ rs.stepDown(12);
EXPECTED OUTPUT - Should elect new leader after primary is down.
ACTUAL OUTPUT - New leader elected after primary went down.
$ Test passed.
- Testing done via Postman.
Create Node1 Key:
POST http://13.56.164.9:8000/node1/types/teams/buckets/epl/keys/liverpool
Body:
{Manager: "Jurgen Klopp"}
Check for Replication:
GET http://13.56.164.9:8000/node1/types/teams/buckets/epl/keys/liverpool
GET http://13.56.164.9:8000/node2/types/teams/buckets/epl/keys/liverpool
GET http://13.56.164.9:8000/node3/types/teams/buckets/epl/keys/liverpool
GET http://13.56.164.9:8000/node4/types/teams/buckets/epl/keys/liverpool
GET http://13.56.164.9:8000/node5/types/teams/buckets/epl/keys/liverpool
EXPECTED OUTPUT - Should be able to read data inserted in node1 on all the other nodes.
ACTUAL OUTPUT- Able to read data on all the other nodes.
$ Test passed.
- Create Network Partition:
Block Nodes 1,2 and 3 on Node 4 and 5 with iptables command and vice-versa
$ sudo iptables -I INPUT -s 10.0.1.112 -j DROP
$ sudo iptables -I INPUT -s 10.0.1.186 -j DROP
$ sudo iptables -I INPUT -s 10.0.1.71 -j DROP
$ sudo iptables -I INPUT -s 10.0.1.79 -j DROP
$ sudo iptables -I INPUT -s 10.0.1.133 -j DROP
Create new Node1 Key:
POST http://13.56.164.9:8000/node1/types/teams/buckets/epl/keys/arsenal
Body: {Manager: "Unai Emery"}
Check for Replication:
GET http://13.56.164.9:8000/node1/types/teams/buckets/epl/keys/arsenal
GET http://13.56.164.9:8000/node2/types/teams/buckets/epl/keys/arsenal
GET http://13.56.164.9:8000/node3/types/teams/buckets/epl/keys/arsenal
GET http://13.56.164.9:8000/node4/types/teams/buckets/epl/keys/arsenal
GET http://13.56.164.9:8000/node5/types/teams/buckets/epl/keys/arsenal
EXPECTED OUTPUT - Should be not be able to read data inserted in node1 on partitioned nodes 4 and 5.
ACTUAL OUTPUT- Not able to read data on nodes 4 and 5. Able to read data only on nodes 1, 2 and 3.
$ Test passed.
- update data from node 1
- unblock traffic
- read data from all nodes
- Delete Network Partition:
Unblock the traffic from Nodes 1,2, and 3 and vice-versa.
$ sudo iptables -D INPUT -s 10.0.1.112 -j DROP
$ sudo iptables -D INPUT -s 10.0.1.186 -j DROP
$ sudo iptables -D INPUT -s 10.0.1.71 -j DROP
$ sudo iptables -D INPUT -s 10.0.1.79 -j DROP
$ sudo iptables -D INPUT -s 10.0.1.133 -j DROP
Create new Node1 Key:
POST http://13.56.164.9:8000/node1/types/teams/buckets/epl/keys/mancity
Body:
{Manager: "Pep Guardiola"}
Check for Replication:
GET http://13.56.164.9:8000/node1/types/teams/buckets/epl/keys/mancity
GET http://13.56.164.9:8000/node2/types/teams/buckets/epl/keys/mancity
GET http://13.56.164.9:8000/node3/types/teams/buckets/epl/keys/mancity
GET http://13.56.164.9:8000/node4/types/teams/buckets/epl/keys/mancity
GET http://13.56.164.9:8000/node5/types/teams/buckets/epl/keys/mancity
EXPECTED OUTPUT - Should be able to read the updated data inserted in node1 on nodes 4 and 5 after removing the partition.
ACTUAL OUTPUT- Able to read data on nodes 4 and 5 after removing the partition.
$ Test passed.
- Timestamp based resolution --> the value that is written later, will be kept.
- update with different data on two different nodes, say 1 and 4 for the same key, say tottenham.
- update on 1 first.
- update on 4 later.
- read data for all nodes to see which data is fetched.
Create new Node1 Key:
POST http://13.56.164.9:8000/node1/types/teams/buckets/epl/keys/tottenham
Body:
{Manager: "Mario Pocchetino"}
Create new Node4 Key:
POST http://13.56.164.9:8000/node4/types/teams/buckets/epl/keys/tottenham
Body: {Captain: "Harry Kane"}
Check for Replication:
GET http://13.56.164.9:8000/node1/types/teams/buckets/epl/keys/tottenham
GET http://13.56.164.9:8000/node2/types/teams/buckets/epl/keys/tottenham
GET http://13.56.164.9:8000/node3/types/teams/buckets/epl/keys/tottenham
GET http://13.56.164.9:8000/node4/types/teams/buckets/epl/keys/tottenham
GET http://13.56.164.9:8000/node5/types/teams/buckets/epl/keys/tottenham
EXPECTED OUTPUT - Should be able to read the latest data inserted.
ACTUAL OUTPUT- Able to read only the latest data on all the nodes.
$ Test passed.