CHASM

Crytpograph-Hash-Algorithm-Secured Mirroring

The Refractions of Design: Part III

Both upstream and downstream can have multiple connections concurrently, at a high level this is accomplished using async calls. Each connection instantiates a connection chain, which is functionally a state machine where each state is a message in the protocol. A message framework provides the structure for sending and receiving. It consists of two classes send_message and recv_message that take a state object, either a send_state or recv_state, respectively. The state object handles preparing and processing the message by implementing the operations used by send_message and recv_message.

class send_state
{
    ...
    // send operations
    virtual void prepare(connection_ptr conn, chasm::buffer& msg)
            throw (std::bad_alloc);
    virtual recv_state_ptr next_state() const;
    ...
};

Although briefly more than one state per connection will exist, usually there will only be one at any given point in time. The ephemeral nature of the states is accomplished through pointers and callbacks. A state is instantiated as a shared pointer and then started, shortly after that an async call will be made that will unwind the stack and the calling object will be destroyed. Callbacks provide a mechanism for keeping objects alive. The state becomes the callback through the use of bind and the this pointer. Following is a snippet of code demonstrating the concepts used:

void v0 :: send_message :: start()
{
    ...
    m_state_obj->prepare(m_conn, m_msg);
    ...
    send();
}

void v0 :: send_message :: send() const
{
    boost::asio::async_write(*m_conn->_socket, boost::asio::buffer(m_msg),
                             boost::bind(&v0::send_message::goto_next_state,
                                         shared_from_this(),
                                         boost::asio::placeholders::error));
}

void v0 :: send_message :: goto_next_state(...) const
{
    ...
    v0::recv_state_ptr state_obj = m_state_obj->next_state();
    if (state_obj.get())
    {
        boost::shared_ptr<v0::recv_message>
            rm(new v0::recv_message(m_conn, state_obj));
        rm->start();
    }
    ...
}

...

-mfm

The Refractions of Design: Part II

I would like to digress a bit and talk about the whole design of the CHASM peer-to-peer protocol, eventually leading to design specifics with code samples. The material in this post is for a technical document I am writing, so it may come off as dry and boring to most. For those whose native tongue is C or C++, the new direction of this thread will have its redeeming qualities in the end.

CHASM Peer-to-Peer Design

The Boost.Asio ("asio") library is used for communicating over the network, all calls are made using asynchronous variants. Asynchronous calls were deliberately chosen to provide a level of concurrency within the daemon without the need for multiple threads or forking. Two functions are primarily used: async_read and async_write.

The chasm-p2p daemon is composed of two central parts, the downstream client ("downstream") and upstream server ("upstream"). Downstream uses a timer object to periodically check for manifest revisions and will make connections to one or more upstream nodes when necessary. Both of these components follow a similar structure utilizing the pimpl idiom for separation of interface and implementation. The public interface of each is defined in a downstream and upstream class with the implementation being in downstream::priv and upstream::priv, respectively.

A protocol namespace is used along with a unique version number namespace. The version number depends on the official peer-to-peer protocol in use and will be utilized for protocol version negotiation between upstream and downstream. Specifics of this functionality are yet to be determined.

...

-mfm

chasm-p2p(8)

NAME

chasm-p2p - CHASM peer-to-peer server and client

SYNOPSIS

chasm-p2p  [--no-daemon | -f] [--host | -H] [--help | -h]
           [--port | -p] [--timer | -t] [--version | -V]
           [--verbose | -v]
           [COMMAND]

DESCRIPTION

The chasm-p2p daemon is responsible for the sending and receiving of blobs. It consists of two major modes called upstream and downstream. The normal function is to be in both modes simultaneously.

Upstream is a server, it does the sending of blobs. Once a connection is established, a manifest revision request will be read and blobs will be transferred accordingly.

Downstream is a client that receives blobs. It uses a timer that notifies it to periodically check for manifest revisions. If a revision exists, a connection to one or more upstream servers will be made.

Both upstream and downstream are not responsible for determining what blobs are sent or received, the Pool-Manifest Oracle is in charge of that. The actual storage of the blobs is the job of the Pool Manager. chasm-p2p is solely responsible for the transfer of blobs and is used as an intermediary for the negotiation. The protocol version in use specifies the extent of each components role.

The Refractions of Design: Part I

"...the very best designers produce structures that are faster, smaller, simpler, cleaner, and produced with less effort."

Frederick P. Brooks Jr., The Mythical Man-Month

I did not know the real project was not in the code but in the design. A program that is well designed is not merely an accident: it is an act of human thought that transforms an idea into a tangible structure. I do not consider myself a great designer. In fact, I am not sure if I would know great design if I saw it. My goal is to learn what great design is, to strive for this ideal that Frederick P. Brooks Jr. speaks of. Thankfully, I have help in achieving this goal.

Not being experienced in design, I have had to learn numerous design patterns, patterns I previously knew only by name. Learning these, however, has not been a magical solution by any means. With a plethora of possible designs and my having only an abstract understanding of each, this has only added to my inability to decide on any one design. In the end, I did settle on a primitive design that has proven a good jumping-off point.

So, the design of the CHASM peer-to-peer protocol is now going through its second iteration in code -- many more never made it past my white-board. It has been an invaluable learning experience to receive feedback on the design from my mentors Ben Boeckel and Rob Escriva. After much discussion with them, I will implement a new design to reduce redundancy and temporary-state variables. This will increase clarity and provide a cleaner design.

In Part II, I will discuss the design in greater detail and provide a code snippet.

-mfm

Copyright © 2010 Robert Escriva ¦ Powered by Firmant