rexxars / sse-channel

Server-Sent Events "channel" where all messages are broadcasted to all connected clients, history is maintained automatically and server attempts to keep clients alive by sending "keep-alive" packets automatically.
MIT License
111 stars 11 forks source link

Auto Refresh Issue #16

Closed GoChartingAdmin closed 7 years ago

GoChartingAdmin commented 7 years ago

Hi,

I am new to the concept of SSE and hence please excuse my naiveness. I have used your package and it works like a charm. Here is the issue I am facing and would like some guidance.

I am creating a stock market watchlist in my app where the server is repeatedly downloading the ticker price data from the database and sending it to all the client using your package. The front end uses ReactJS which is somewhat irrelevant in this context

client.js

    componentDidMount(){
     if (!!window.EventSource) {
         this.source = new EventSource('http://localhost:3000/channel/liveFeed?');
         this.source.addEventListener("watchlist", function(e) {
             var { feed } = JSON.parse(e.data);
             var quotefeed = feed.map((each,idx)=>{
                                                                                             return(
            {
                                                             ticker:each['Symbol'],
                                 time:each['Time of Last Trade'],
                                 open:each['Opening Price'],
                                 high:each['High Price'],
                                 low:each['Low Price'],
                                 close:each['Close Price'],
                                 volume:each['Volume'],
                                 lastClose:each['Closing Price Previous Day'],
                                 change:parseInt(each['Close Price']) - parseInt(each['Closing Price Previous Day']),
                                 percentChange:Math.round((parseInt(each['Close Price']) - parseInt(each['Closing Price Previous Day']))/parseInt(each['Closing Price Previous Day'])*100)/100
}
                                                                                             )
                                                                                         })
             this.setState({livePriceFeed:quotefeed});
         }.bind(this), false);

         this.source.addEventListener("open", function(e) {
             console.log("OPENED");
         }.bind(this), false);

         this.source.addEventListener("error", function(e) {
             console.log("ERROR");
             if (e.readyState == EventSource.CLOSED) {
                 console.log("CLOSED");
             }
         }.bind(this), false);
     }
 }

And server-side uses your example using express with small tweak to the data-provider.js

const priceFeeder = () => {
  logger.info('I m database');
  pool.getConnection((err,connection)=>{
    if (err) {
      logger.info('error when connecting to db:', err);
      connection.end(); 
    };
    connection.query('SELECT * FROM DataLake', function(err, rows, fields)
    {
      if (err) {
        logger.info('error when connecting to db:', err);
        connection.end(); 
      };
      logger.info("Rows:" + rows.length);
      emitter.emit('liveFeed', {
            id: (new Date()).toISOString(),
            event: 'watchlist',
            feed: rows
        });
      connection.release();
    });
  })
}

setTimeout(priceFeeder, 5000);
setInterval(priceFeeder, 60000);

channels.js

const liveFeedChannel = new SseChannel({
    retryTimeout: 250,
    cors: { origins: ['*'] },
    pingInterval: 60 * 1000,
    jsonEncode: true,
    historySize: 5
});

dataProvider.on('liveFeed', function(randomData) {
    liveFeedChannel.send({ event:randomData.event,data:randomData,id:randomData.id});
});

As you can see, the idea is to first trigger priceFeeder 5000ms after the server starts and then periodically after every 1 minute. This works per expectation but the issue is with the client side.

When the client starts and establishes the SSE connection using the url 'http://localhost:3000/channel/liveFeed?', it starts getting the feed from the server only after the first data refresh on the server side post client launch. So, for ~ 1minute, it shows no data and then starts refreshing as usual.

Now if I change the url to 'http://localhost:3000/channel/liveFeed?lastEventId=0', the client gets all the data from the start the moment the client launches (which is great) but it does not refresh every time new data is sent by the server (auto refresh does not work anymore).

The client should see some data the moment it launches and then get refreshed every 1 minute when new data is sent by the server through SSE. This is the base case and I do have some small complexities which I can resolve once this base setup is up and running.

Please help me if this is something can be achieved using your package/

Regards SD

debsush commented 7 years ago

Can you try to create first eventsource with lastEventID = 0 (to get the initial dump) and on receipt of the message close it. And then again establish a second eventsource without the lastEventID to get the usual periodic refresh?

rexxars commented 7 years ago

I'm kind of swamped right now, so I don't have time to dive into this fully, but if you're using IDs, they should be numeric. Either use a counter for the IDs, or use Date.now() instead of (new Date()).toISOString()

rexxars commented 7 years ago

This doesn't seem like a bug, but an implementation problem. If you have a more specific bug to report, please comment. Closing.