Class SqlMessageStore

  • All Implemented Interfaces:
    IEMessageStore

    public final class SqlMessageStore
    extends Object
    implements IEMessageStore
    This class implements IEMessageStore providing the ability to store events to and retrieve events from a Java SQL connection. An application using this class is responsible for:
    • Defining the stored notification message key.
    • Providing an IInsertGenerator instance used to generate SQL statement for storing eBus notification message (as defined by message key) or PublishStatusEvent into target database.
    • Providing an IRetrieveGenerator instance used to generate SQL statement for retrieving eBus notification messages or PublishStatusEvent from target database based on message key and interval.
    • Providing an IMessageDecoder instance used to translate ResultSet into eBus notification message (either store's target message key or PublishStatusEvent).

    Note: application is responsible for inserting, retrieving, and decoding both the message type defined by the message key and PublishStatusEvent messages. Also note that the notification message type must be inserted and retrieved using a single SQL statement. Notification types too complex for this requirement cannot use SqlMessageStore.

    There is a one-to-one mapping between an SqlMessageStore and eBus notification message key. But the application developer is free to design SQL tables in any way deemed best. Most likely this means that there will be a single table for a given eBus notification message class containing the message subject. This allows a single SqlMessageStore instance to be shared among multiple EHistoricPublishFeeds. In this case, it is the application's responsibility to provide thread-safe message store and retrieval.

    Simply put, the application is responsible for interfacing SqlMessageStore with the application database, translating eBus notification messages between database and Java.

    Example

    The following code is used to insert and retrieve a TopOfBookMessage notification into and from an SQL table. Notification message definition (note: see ENotificationMessage and EMessage for inherited message fields):

    public final class TopOfBookMessage extends ENotificationMessage {
        public final PriceSize bid;
        public final PriceSize ask;
        public final PriceType priceType; // enum specifying if this is the opening, closing, low, high, or latest price.
    
        // Builder class not shown.
    }

    PriceSize definition:

    public final class PriceSize extends EField {
        public final Decimal6f price; // uses decimal4j for prices.
        public final int size;
        public final Trend trend; // enum specifying whether this price is up or down in relation to previous message.
    
        // Builder class not shown.
    }

    The following SQL data definitions used to store the top-of-book message are in Postgresql:

    CREATE TABLE top_of_book (
      ticker_symbol      varchar(50) NOT NULL,
      publisher_id       bigint NOT NULL,
      publisher_position integer NOT NULL,
      bid                price_size NULL,
      ask                price_size NULL,
      price_type         price_type NOT NULL,
      PRIMARY KEY (
        message_timestamp,
        publisher_id,
        publisher_position
      )
    );
    
    -- Table indices not shown.
    
    CREATE TYPE price_Size AS (
      price       NUMERIC(15, 6),
      size        integer,
      price_trend trend
    );
    
    CREATE TYPE price_type AS ENUM ( ... );
    CREATE TYPE trend AS ENUM ( ... );
    

    The PublishStatusEvent messages are stored in the table:

    CREATE TABLE publish_status_event (
      subject            varchar(500) NOT NULL,
      message_timestamp  timestamp NOT NULL,
      publisher_id       bigint NOT NULL,
      publisher_position integer NOT NULL,
      message_key        message_key NOT NULL,
      feed_state         feed_state NOT NULL,
      PRIMARY KEY (
        message_timestamp,
        publisher_id,
        publisher_position
      )
    );
    
    -- Table indices not shown.
    
    CREATE TYPE message_key AS (
      message_class   varchar(500),
      message_subject varchar(500)
    );
    
    CREATE TYPE feed_state AS ENUM (
      'UNKNOWN',
      'DOWN',
      'UP'
    );

    IInsertGenerator.insertStatement(ENotificationMessage) implementation returns the following insert statement for a top-of-book message:

    INSERT INTO top_of_book VALUES ('ACME', '2024-01-06 07:30:53.113', 2001, 1, ROW (12.076400, 1200, 'NA'::trend), ROW (12.076700, 600, 'NA'::trend), 'LATEST'::price_type)

    The insert statement for a PublishStatusEvent is:

    INSERT INTO publish_status_event VALUES ('/marketdata/ACME', '2024-01-06 07:30:54.547', 2001, 15, ROW ('com.lightspeedmd.com', '/marketdata/ACME', 'DOWN'::feed_state)

    IRetrieveGenerator.retrieveStatement(EMessageKey, EInterval) for top-of-book messages is:

    SELECT ticker_symbol, message_timestamp, publisher_id, publisher_position, (bid).price, (bid).size, (bid).price_trend, (ask).price, (ask).size, (ask).price_trend, price_type FROM top_of_book WHERE message_timestamp >= '2024-01-06 07:47:43.52010225' AND message_timestamp < '2024-01-06 07:47:44.18792075'

    The ResultSet to top-of-book message code is:

    // Note: result set columns start at index 1.
    final String tickerSymbol = rs.getString(1);
    final Instant timestamp = (rs.getTimestamp(2)).toInstant();
    final long publisherId = rs.getLong(3);
    final int position = rs.getInt(4);
    final Decimal6f bidPrice = Decimal6f.valueOf(rs.getBigDecimal(5));
    final int bidSize = rs.getInt(6);
    final Trend bidTrend = Trend.valueOf(rs.getString(7));
    final Decimal6f askPrice = Decimal6f.valueOf(rs.getBigDecimal(8));
    final int askSize = rs.getInt(9);
    final Trend askTrend = Trend.valueOf(rs.getString(10));
    final PriceType priceType = PriceType.valueOf(rs.getString(11));
    final PriceSize bid = (PriceSize.builder()).price(bidPrice)
                                               .size(bidSize)
                                               .trend(bidTrend)
                                               .build();
    final PriceSize ask = (PriceSize.builder()).price(askPrice)
                                               .size(askSize)
                                               .trend(askTrend)
                                               .build();
    final TopOfBookMessage.Builder builder = TopOfBookMessage.builder();
    
    builder.subject(tickerSymbol)
           .timestamp(timestamp)
           .publisherId(publisherId)
           .position(position)
           .bid(bid)
           .ask(ask)
           .priceType(priceType)
           .build());
    Author:
    Charles W. Rapp
    See Also:
    InMemoryMessageStore
    • Method Detail

      • isOpen

        public boolean isOpen()
        Description copied from interface: IEMessageStore
        Returns true if this message store is open and ready to store or retrieve notification messages.
        Specified by:
        isOpen in interface IEMessageStore
        Returns:
        true if message store is open.
      • key

        public EMessageKey key()
        Description copied from interface: IEMessageStore
        Returns message key associated with message store.
        Specified by:
        key in interface IEMessageStore
        Returns:
        message store's associated key.
      • retrieve

        public Collection<ENotificationMessage> retrieve​(EInterval interval)
        Description copied from interface: IEMessageStore
        Retrieves historic messages as defined by given date/time interval. Returned collection may be empty but never null.

        Please note that implementation are not required to validate interval since this is performed by EHistoricPublishFeed. That said, such validation is encourage if store is used outside of eBus historic feed framework.

        Specified by:
        retrieve in interface IEMessageStore
        Parameters:
        interval - date/time interval.
        Returns:
        iterator over historic messages. Does not return null but may return iterator where Iterator.hasNext() returns false.
        See Also:
        IEMessageStore.store(ENotificationMessage)
      • insertCount

        public int insertCount()
        Returns number of events inserted into message store since message store opening.
        Returns:
        return event insertion count.