StellarSQL is a relational DBMS (RDBMS). Usually, RDBMS is 2-tier client/server architecture, which includes a server and a client. A client might be any devices which wants to access databases, and a server is running a DBMS for handling requests from clients.

There must be a protocol between client and server, in order to communication and data transmission. MySQL and PostgreSQL apply message stream communication as their protocol. MySQL and PostgreSQL do talk about protocol on their document:

Of course, you can also use HTTP protocol. Firebase is a NoSQL cloud database, and clients send HTTP requests for querying data. However HTTP is somehow too “fat” for the purpose of data querying, so message stream might be suitable in the case of DBMS.

Now, we can start to develop a little about Tokio.rs for StellarSQL, which we talked about on yesterday.

Create server

Based on the reason above, I would also use message stream communication as the protocol for StellarSQL.

I will think about the detail of the protocol in the following days. But now, we can implement the part of TCP stream by using Tokio.rs first.

We create a server first:

!FILENAME src/main.rs

    let addr = format!("127.0.0.1:{}", port).parse().unwrap();

    // Bind a TCP listener to the socket address.
    // Note that this is the Tokio TcpListener, which is fully async.
    let listener = TcpListener::bind(&addr).unwrap();

    // The server task asynchronously iterates over and processes each
    // incoming connection.
    let server = listener
        .incoming()
        .for_each(move |socket| {
            // Spawn a task to process the connection
            // TODO process()
            Ok(())
        }).map_err(|err| {
            println!("accept error = {:?}", err);
        });

    tokio::run(server);
    println!("StellarSQL running on {} port", port);

Continue to the day before yesterday, we let address use the port from arguments or configuration. So the server will serve at 127.0.0.1:PORT.

Then we let server listen to any sockets input from connections. For servers or DBMS, it is always that clients ask requests and servers answer responses. So, you could see there is a listener that will handles any sockets, which I remain it as TODO.

Finally, we run the server with tokio::run(server). According to the document, the Tokio is a pre-configured “out of the box” runtime for building asynchronous applications. It includes both a reactor and a task scheduler. This means applications are multi-threaded by default. In this case, we just use the default setting by Tokio.

Message

I also define the Message for message streaming. The Message struct is really basic and straightforward now. I will finish it and define the protocol in the following days. Then, we could use it for client/server communication later.

pub struct Message {
    /// The TCP socket.
    socket: TcpStream,

    /// Buffer used when reading from the socket. Data is not returned from this
    /// buffer until an entire message has been read.
    rd: BytesMut,

    /// Buffer used to stage data before writing it to the socket.
    wr: BytesMut,
}

impl Message {
    /// Create a new `Message` codec backed by the socket
    fn new(socket: TcpStream) -> Self {
        Message {
            socket,
            rd: BytesMut::new(),
            wr: BytesMut::new(),
        }
    }
}