/Distributed_Event_Purchase_System

Strong consistency distributed RESTful API service

Primary LanguageJava

Strong Consistency Event Purchase RESTful API Service

Introduction

This is an event create/purchase RESTful API Service. Client can create user, create event, purchase tickets, and transfer tickets. Focus on the replication features of the event service which is a replicated, fault tolerant service.

The design of my service and the demonstration of the testing framework are recorded here: Youtube

Overview

The architecture of the service looks like this:

Project4 Architecture

Features

Fault Tolerance

In a system with N data storage servers, you will tolerate the failure of up to N-1 nodes. As long as one data storage server is available, a client request will succeed.

Read-My-Writes Consistency

A front end will not receive data older than it has seen before if fresher data is available. If, for example, a client posts a message and then performs a read, the response the client receives must include the most recent messages unless all data storage servers storing the newest data have failed.

Membership

The event services will maintain the membership of all the services, including: primary/secondary event service, primary user service, and front end service. Event services will gossip with each other to get the service list, and to add itself to each other's list. Front end services will greet with the primary service to add itself to primary's list. The primary event/user service will be configured when the service starts. Once event service detcets a service is unreachable, it will remove it from its list.

Bully Election

During the gossip between secondaries, start an election when detecting primary is down. Send GET request to other event services with higher rank address. If no reply, send POST request to announce there is a new primary. If received a response, it means that there is an outstanding service, wait for announcement. Each node will only send one election request in one round of the election, unless it timeout to wait for the announcement.

Service States

The event service has three states: primary, secondary, and candidate. The service will start with primary or secondary state by configuration. A secondary will trun into candidate state when it detects the primary is down or when it receives a election request. A candidate will turn into primary if there is no outstanding node, and it will turn into secondary when receives an announcement.

Committed Log

A synchronized data structure to maintain the logs of committed request. It contains with Universally Unique IDentifier, Lamport Timestamps, and the committed data. The purpose is to avoid duplicate data, and to maintain the order of replications.

Replication

When a front end service receives a write request, it will assign the request with an uuid and pass it to the primary event service. The primary event service will start the write operation, and right after it finished, it will assign the request with a Lamport Timestamp, commit to log, and pass it to the secondary event service. If the primary fails during replication, the front end will hold the request and retry it when a new primary comes up. If a new primary has already committed the write with the same uuid, it will ignore it and pass it with the timestamp it committed to the secondary event service. If a secondary receives a write request with the uuid it already committed, it will match with its timestamp. If the uuid and the timestamp don't match, it will request a full copy from the primary to overwrite the data. Full backup from primary will only happen when new secondary comes up or the above situation.

API

Front End Service

GET /events

Responses:

CodeDescription
200Event Details
[
	{
		"eventid": 0, 
		"eventname": "string", 
		"userid": 0,		
		"avail": 0, 
		"purchased": 0
	} 
]
	
400No events found
POST /events/create

Body:

{
	"userid": 0,
	"eventname": "string",
	"numtickets": 0
}

Responses:

CodeDescription
200Event created
{
	"eventid": 0
}	
400Event unsuccessfully created
GET /events/{eventid}

Responses:

CodeDescription
200Event Details
{
	"eventid": 0, 
	"eventname": "string", 
	"userid": 0,		
	"avail": 0, 
	"purchased": 0
}
400Event not found
POST /events/{eventid}/purchase/{userid} Body:
{
	"tickets": 0
}

Responses:

CodeDescription
200Tickets purchased
400Tickets could not be purchased
POST /users/create

Body:

{
	"username": "string"
}

Responses:

CodeDescription
200User created
{
	"userid": 0
}	
400User could not be created
GET /users/{userid}

Responses:

CodeDescription
200User Details
{
	"userid": 0,
	"username": "string",
	"tickets": [
		{
			"eventid": 0, 
			"eventname": "string", 
			"userid": 0,		
			"avail": 0, 
			"purchased": 0
		}
	]	
}
400User not found
POST /users/{userid}/tickets/transfer

Body:

{
	"eventid": 0,
	"tickets": 0,
	"targetuser": 0
}

Responses:

CodeDescription
200Event tickets transferred
400Tickets could not be transferred
GET /greet

Responses:

CodeDescription
200Front End Service is running
POST /election

Body:

{
	"port": 0
}

Responses:

CodeDescription
200New primary event service has been configured

Event Service

POST /create

Body:

{
	"userid": 0,
	"eventname": "string",
	"numtickets": 0
}

Responses:

CodeDescription
200Event created
{
	"eventid": 0
}	
400Event unsuccessfully created
GET /list

Responses:

CodeDescription
200List of events
[
	{
		"eventid": 0, 
		"eventname": "string", 
		"userid": 0,		
		"avail": 0, 
		"purchased": 0
	}
]	
GET /{eventid}

Responses:

CodeDescription
200Event details
{
	"eventid": 0, 
	"eventname": "string", 
	"userid": 0,		
	"avail": 0, 
	"purchased": 0
}
400Event not found
POST /purchase/{eventid}

Body:

{
	"userid": 0,
	"eventid": 0,
	"tickets": 0
}

Responses:

CodeDescription
200Event tickets purchased
400Tickets could not be purchased
POST /greet/event

Body:

{
	"port": 0,
}

Responses:

CodeDescription
200Service list
[
	{
		"service": "event",
		"address": "10.0.1.9:4599",
		"primary": true
	},
	{
		"service": "frontend",
		"address": "10.0.1.5:4560",
		"primary": false
	}
]
400Service unreachable
POST /greet/frontend

Body:

{
	"port": 0,
}

Responses:

CodeDescription
200Service list
[
	{
		"service": "event",
		"address": "10.0.1.9:4599",
		"primary": true
	},
	{
		"service": "frontend",
		"address": "10.0.1.5:4560",
		"primary": false
	}
]
400Service unreachable
GET /election

Responses:

CodeDescription
200A service with higher rank is running
POST /election

Body:

{
	"port": 0,
}

Responses:

CodeDescription
200New primary event service has been configured
400Service unreachable

User Service

POST /create

Body:

{
	"username": "string"
}

Responses:

CodeDescription
200User created
{
	"userid": 0
}	
400User unsuccessfully created
GET /{userid}

Responses:

CodeDescription
200User details
{
	"userid": 0,
	"username": "string",
	"tickets": [
		{
			"eventid": 0
		}
	]
}
400User not found
POST /{userid}/tickets/add

Body:

{
	"eventid": 0,
	"tickets": 0
}

Responses:

CodeDescription
200Event tickets added
400Tickets could not be added
POST /{userid}/tickets/transfer

Body:

{
	"eventid": 0,
	"tickets": 0,
	"targetuser": 0
}

Responses:

CodeDescription
200Event tickets transfered
400Tickets could not be transfered

Program and testing framework configuration

Start Event Service (Primary)
$ java -cp project4.jar EventService.EventServiceDriver -port <port> -primaryEvent this - primaryUser <address_of_primary_user>
Start Event Service (Secondary)
$ java -cp project4.jar EventService.EventServiceDriver -port <port> -primaryEvent <address_of_primary_event> - primaryUser <address_of_primary_user>
Start Front End Service
$ java -cp project4.jar FrontEndService.FrontEndDriver -port <port> -primaryEvent <address_of_primary_event> - primaryUser <address_of_primary_user>
Test for write and read
$ python test_wr.py <choose_0_to_2_for_different_eventname> <address_of_front_end>
Read directly from backend
$ python3 test_read_backend.py <address_of_event_service>
Test for election and replication of secondaries with different version data
$ python3 test_diff_ver.py <address_of_front_end>
Test for concurrent writes
$ java -cp project4.jar Usage.ConcurrentTest <0_for_create_1_for_purchase> <address_of_front_end> <times_of_test>

References

Acknowledgment

This is a course project, not using for any commercial purpose.

Author and contributors

  • Brian Sung - Graduate student in department of Computer Science at University of San Francisco - LinkedIn
  • Dr. Rollins - Professor in department of Computer Science at University of San Francisco - page