Entries in architecture
(2)

In the previous blog entry titled
"Introduction into QDS architecture"
I gave a short overview of the overall QDS design. Today I'll explain the basics of QDS transport and networking architecture.

QDS transport layer uses quite non-standard design. In a typical enterprise application that is built in layers, the presentation
layer is built on top of a business logic layer, which is built on top of transport, communication, and storage layers. That is, logic
typically depends on the transport layer. In QDS this dependency is inverted. Transport layer is built on top of QDS core logic
and depends on it.

The are various connectors that transfer data over the network. Connectors may come in different flavors depending on the
underlying protocols, means of establishing connection, or technologies they use: server or client, synchronous or asynchronous, unicast or multicast, TCP/IP, UDP, HTTP, etc.
There's even a file connector that does not actually work over the network, but reads pre-recorded messages from a file.

Each connector is responsible for establishing connections, and delivery/reception of
the underlying messages over the network. Typically, there are two different roles in the communication. On one side data provider
receives subscription and sends data while communicating with the data receiver on the other side. Connector on the data provider
side creates agent in the QDS core to manage subscription and filter data that pertains to this subscription. Connector on the
data receiver side creates distributor in the QDS core to feed the incoming data into the core and collect subscription from the core.
This data will be ultimately delivered for processing to the core agents in the data receiver node.

The actual connector code works with agents or distributors though a special adapter whose purpose is to convert internal QDS core
API into the message-based API that is suitable for network communication. Thus, there are agent and distributor adapters.
For example, a combination of ServerSocketConnector with AgentAdapter creates a data provider node that listens
for incoming TCP/IP connections. On the other side, ClientSocketConnector with DistributorAdapter creates a data
receiver that can connect to the data provider via TCP/IP protocol. However, while data receiver node typically establishes connection
to the data provider, it does not have to be always this way. The transport architecture is fully flexible from deployment perspective,
so that, for example, data providers can establish connection to a central data receiver node instead.

This flexible transport and networking architecture makes it easy to deploy a multiplexor node.

QDS multiplexor node contains QDS core and at least two connectors — one for agents and one for distributors. Multiplexor
works as data receiver and data provider at the same time. It collects subscription from multiple data receivers and collectors
data from multiple data providers, delivering this data to each data receiver based on their subscription.

dxFeed Market Data services are built on top of QDS core — the open source, high-performance messaging solution that
was designed from the ground up to deliver millions of quotes on hundreds of thousands financial instruments to virtually
unlimited number of destinations in a scalable way, while supporting fully individual subscription for each recipient.

The QDS core is an integral part of dxFeed API, hidden behind top-level easy-to-use object-oriented API of dxFeed.

The key function that is being performed by QDS core is message multiplexing. It collects messages from a number of
data sources that are called distributors in QDS parlance, and forwards those messages to data consumers,
that are called agents. Each agent has its own subscription to the data items that it is interested in. This
subscription is collected by QDS core into a total subscription that is made available to distributors.
QDS core is also known as collector.

The key to QDS core performance is very careful memory management that is incorporated into every aspect of its design.
Modern computer architectures are notoriously known for multi-level cache hierarchies with a relatively slow main memory
compared to the main CPU speed. The amount of memory consumed to encode the data and memory locality are very
important for ultimate performance. QDS core address memory consumption and memory locality aspects with several
design decisions.

First of all, data items are grouped into data records. Those data fields that typically change together
are grouped together. A trade on the exchanges changes last trade price, last trade size, etc, so all those fields are
grouped into a trade record. Best bid and best offer information are grouped into a quote record, and so on.
Data records are not atomic, they are still composed from the number of individual fields. However, data records are atomic
units of subscription and atomic units of data update. By tracking subscription and data update on the per record level,
instead of per field level, the amount of bookkeeping is reduced multiple-fold, with a corresponding reduction in memory
consumption for all the data structures that QDS core has to maintain.

Second, is the compact representation of most numerical values. QDS uses custom-designed decimal format that
is build for the specific purpose of representing prices of financial instruments. This domain-specific representation is
based on the observation that financial instruments are traded mostly in decimals with a limited number of significant
digits. Volatility of financial instruments is mostly scale-free, so exchanges tend to limit price precision of
expensive instruments, but allow more digits after decimal point for cheaper ones. Those observations had allowed to
design a decimal representation in QDS that takes only 4 bytes per price.

Third, is the use of data buffers to transmit data from distributors to QDS core and to agents.
While it is easier to design a system where each data item is be represented by an object and transfer collections
of those objects around, this kind of design results in a poor memory locality and poor performance. QDS follows
a different approach. Data transfers are performed via contiguous regions of memory also known as data buffers.
Data buffers naturally map into data packages that has to be sent over the network, thus the process of
serializing and deserializing data for network transfer requires only sequential access to memory — the fastest
possible kind of memory access pattern.

Last, but not the least, is that all data transfers in QDS are garbage-free. Having dispensed with
individual small objects to represent data items, it is easy to reuse data buffers. The overall result is that
there are no object allocations in the normal data transfer path from distributors to agents. It means no
garbage collection pauses and enables QDS to transfer millions of records per second.

This design for performance comes with the price of complex QDS API. That is why, for the end-users of dxFeed quote services,
there is a layer on top of QDS API that is called dxFeed API. It creates Trade, Quote, and other event objects that
have easy-to-use API, where values are represented with double precision floating point numbers and can be directly used
via regular getter methods. This layer makes it easier to evolve the actual encoding of market data in QDS,
tracing the changing trends in market data, while maintaining compatibility with existing code that uses dxFeed API.
QDS records and their decimal-enconded fields serve as a low-level representation of market data,
while dxFeed API provides a high-level API to it.

Devexperts, its data providers, the financial exchanges and each of their affiliates (A) expressly disclaim the accuracy, adequacy, or completeness of any data and (B) shall not be liable for any errors, omissions or other defects in, delays or interruptions in such data, or for any actions taken in reliance thereon. Neither Devexperts nor any of its information providers will be liable for any damages relating to your use of the information provided herein. Either Devexperts or its third party data or content providers have exclusive proprietary rights in the data and information provided. Market data limited displays are delayed at least 15 min.
You agree not to copy, modify, reformat, download, store, reproduce, reprocess, transmit or redistribute any data or information found herein or use any such data or information in a commercial enterprise without obtaining prior written consent. All data and information is provided «as is».