Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java / JavaSE6

Distributed Database Management System

4.50/5 (4 votes)
5 Dec 2008CPOL7 min read 48.3K   2.7K  
A distributed database management system, having a main server-LookupServer, several DatabaseServers and Clients, using java.nio for communicating.

Introduction

This article describes how a distributed database management system was implemented. The system contains main server-LookupServer, several DatabaseServers and Clients. Using the classes available, applications can be created with any number of DataBaseServers and Clients.

The application uses sockets from java.nio for communicating:

  • LookupServer<->Databaseservers
  • Databaseservers<->Databaseservers
  • Clients<->Databaseservers  

Background

The basic concepts needed to understand this code are about sockets. A socket is an end-point of a bidirectional process-to-process communication across an IP based network. A socket provides an interface between a process, thread or application and the TCP/IP protocol stack provided by the operating system. Sockets can be blocking and nonblocking. A blocking socket, as the name says, will block when sending or receiving a message. A non blocking socket, when wishing to receive a message will check if the message is available; if so, it will read it, otherwise it will continue doing something else.

Using the Code

In order to compile and run the code, I used ant. In the build.xml file, I defined targets for compiling all the source code and producing a jar file. In order to run the application for a certain test, I introduced a runTest1 target.

Let me explain the main classes contained in the project.

1. Client 

The Client class defines a transaction in the database. All the actions of a transaction must keep the database’s integrity. The changes made by a client will only be available to that particular client until it commits the data. Each transaction will end with a commit or rollback command. Choosing to commit will save the changes made by the transaction making them available to other clients (transactions), while choosing rollback will cancel all the changes made till that point.

The behavior of a client is defined by the following pattern. First it will connect to a database server, which will assign it a unique TID (transaction id). After receiving the TID, the client can proceed in using the object database available. Thus, it will send several requests to identify the values of certain objects or to modify them. When the transaction is finished, the client will either commit the data or rollback.

The Client class implements TransactionInterface, which defines the following functions:

Java
public String begin(String DatabaseServerIP, int DatabaseServerPort)
public int getValue(String objName);
public void setValue(String objName, int value);
public void commit();
public void rollback();

  1. begin: The client will connect to the database server defined by the parameters: DatabaseServerIP and DatabaseServerPort.
  2. getValue: The client will request the value of a certain object from the DBServer. The object need not be stored on that server, it will be up to it to find the object’s location and provide the client with its value.
  3. setValue: The client will announce to the server that it wishes to change the value of a certain object. The changes will not be seen by other DBServers and clients until the client chooses to commit the new data.
  4. commit: The client will send a request to the DBServer to commit all the data that was worked on, then it will disconnect from the server. It is the database server’s job to announce to all the other database servers about the changes made.
  5. rollback: The client will announce to the server that it wishes to cancel all changes made, then it will disconnect from the server.

2. DatabaseServer

There are several database servers, each storing a number of objects. An object is defined by a name which is unique within a group of database servers, and its values. The class used to define such an object is DatabaseObject which I shall discuss later. A set of such database servers will connect to a lookup server, that will have intelligence about all the existing data servers and their stored objects.

The DatabaseServer class extends Thread and implements DatabaseServerInterface, which defines the following functions:

Java
public void startServer(String myIP, int myPort, String LookupServerIP,
 int LookupServerPort);
public void addObject(String objName, int initialValue);

  1. startServer: This function will open a connection on which it will listen for requests from clients or other DBServers. It will also store the Lookup server information (its IP and port given as parameters) for future information requests it will make.
  2. addObject: The DBServer will store information about a certain object (its name and value) and it will announce the LookupServer about its existence.
  3. stopServer: The DBServer will wait a predefined time for the current tasks to finish, then it will close all connections.

Each database server is a thread and it will run in the background after it is started, awaiting requests. In order to process several incoming requests, a database server uses a pool of threads. Each request received will be assigned to a thread in the pool, if one is available; it will wait for an available one otherwise. A task assigned to a thread in the pool is defined by the internal class HandlerData. According to the type of message received, it will accept a connection or read, interpret data and send an answer.

3. LookupServer

The Lookup server is the central server, in charge of managing the other servers and offering them information about each other. Multiple Lookup servers could be defined as well, but each will be responsible for its own set of Database Servers.

The LookupServer Class extends the Thread class and implements the LookupServerInterface which defines the following functions:

Java
public void startServer(String myIP, int myPort);
public void stopServer();
  1. startServer: This function will open a connection on which the server will listen for requests from DBServers.
  2. stopServer: The server will wait a predefined time for the current tasks to finish, then it will close all connections.

Just like the DBServers the lookup server can answer multiple requests. For this, it uses a pool of threads. Once a request is received, a thread from the pool is assigned to complete the task of processing it. The task assigned to this thread is defined by the internal class HandlerLookup. According to the type of message received, it will accept a connection or read, interpret data and send an answer if one is required.

4. DatabaseObject

The DatabaseObject class defines an object stored on a database server. The important information it stores consists of the object name and its value.

5. TestScenario1

TestScenario1 is a test which shows the basic features of the classes implemented. It defines a lookup server, two database servers and three clients and checks the database integrity and the validity of the client operations. 

Points of Interest 

The present project offers a system using distributed databases of simple objects, with good communications among the parts involved. However it leaves room for improvement. The most important one is that the DBServer should inform the lookup server about exiting in order for the lookup to remove the entries about it from its own database. Another improvement would be defining the protocol between the separate entities (clients, dbservers and lookupserver) within a separate class making it more flexible.

An important problem I should mention here involves the use of the selector (when using nonblocking sockets). In this project, the fact that multiple threads act on the same data, synchronization is vital. Most importantly when using a selector, the main thread will block on the select method so no one else could add a new socket channel to the selector until an incoming connection releases it. Even then, it cannot be sure that the thread trying to add a new socket channel to the selector will get the chance to do so before the main thread blocks on the select method again. To solve this problem, I used synchronization on an object: sinc. When a worker thread wishes to register a new channel to the selector, it first takes ownership of the object sinc then "wakes up" the selector. After registering the channel, it releases the object sinc.

Java
synchronized(sinc)
{
    this.selector.wakeup();
    sChannel.register(this.selector, SelectionKey.OP_READ);
}

In the mean time, the main thread will exit from the select function with a return code 0. The code returned is tested and in case it is 0, an attempt to take ownership of sinc is made. Until the worker thread finishes, the main thread will not be able to enter the synchronized section, thus allowing registering a channel without interference.

Java
while (true) 
{
	try
	{
	// Wait for an event
	a = selector.select();				
	if(a == 0)
	{
	        synchronized(sinc)
		{}
		continue;
	}
        //...
        }
        //...
} 

References and Thanks 

This project was originally developed as an assignment for the Languages for Distributed Programming course of the University Politehnica of Bucharest. Special thanks to the teaching assistant who proposed this assignment as it has been one of the most complex and interesting assignments given.

History

  • 5th December, 2008: Initial post

About Myself 

I am currently a student in my final year at the University Politehnica of Bucharest. My main interest involves distributed systems and parallel architectures but I enjoy being involved in other challenging projects as well. In my spare time, I write on a blog about interfete web (web interfaces).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)