Server Side Events and Polling Database for new records with Lucee/Coldfusion

Viewed 79

I'm struggling with the logic for sending realtime updates to a single client with Server Side Events. I can send and display the events fine, but I cant get my head around how to trigger the event from the server. Currently I have a database which logs chat, I want to send the new messages as they are received (or as close as) to a single Admin page so they can be monitored - so server load shouldnt be an issue as generally only one or two clients. I just cant figure out how to effectively trigger the update when new messages are recieved.

<cfcontent type="text/event-stream">

#get the last chat ID on inital page load
<CFquery datasource="chat" name="topmesg" >
SELECT max(id) as topid FROM chats where  DATE_SUB(CURDATE(),INTERVAL 1 DAY) <= timestamp;
</cfquery>

<cfset topid="#topmesg.topid#">

<cfsetting requesttimeout="60">

#Function for Server Side Event push (working fine)
<cffunction name="sendData">

<cfoutput>data: i: topid:#topid# <b>#author#</b>  | #platform# | id:#id#  #dateTimeFormat(Dateadd("h",1,mesg.timestamp), "ddd dd mmm - h:nn a")# #Chr(10)#</cfoutput>
<cfoutput>#Chr(10)#</cfoutput>
<cfflush>
</cffunction>


<cfloop> < to run this process every 1 second so that new chat messages are pushed to the monitoring page
#get the new chat messages since topid
<CFquery datasource="chat" name="mesg">
SELECT * FROM chats  where id > #topid# order by id asc;
</cfquery>
<cfloop query="mesg">
    <cfset sendData()>
    #increment last id to check
    <cfset topid=#id#>
    #to slow down the output
    <cfthread action="sleep" duration="500"/>
</cfloop>
    #This is the rate at which the main loop is run to check DB every second  
    <cfthread action="sleep" duration="1000"/>
<cfflush>
</cfloop>

And here is the page that calls it:

<html>
    <head>
    </head>
<body>
<div id="result"></div>

<script>
var source = new EventSource('event.cfm');

source.addEventListener('message', function(e){

    document.body.innerHTML = e.data + "<br>" +document.body.innerHTML;

});

</script>

</body>
</html>
1 Answers

I think this might be where you are going wrong: "I just cant figure out how to effectively trigger the update when new messages are recieved". You don't need to trigger anything. The client handles that, that's the entire thing about this feature.

Just have a server-side response that sits there streaming the latest messages. In a loop. Forever. If the server-side response ends, then the client will re-poll it anyhow.

I had a look at all this stuff because of this question, and jotted down some notes at JS: Server-sent events, but some portable repro code that demonstrates what I say above would be:

<!-- test.html -->

<div id="result"></div>

<script>
var source = new EventSource('testInLoop.cfm');

source.addEventListener('message', function(e){
    document.body.innerHTML += `${e.data}<br>`
});
</script>
<cfscript>
// testInLoop.cfm

header name="Content-Type" value="text/event-stream";

requestStartedAt = now()
for (i=1; i <= 10; i++) {
    data = {
        "uuid" = createUuid(),
        "now" = now().timeFormat("HH:mm:ss"),
        "requestStartedAt" = requestStartedAt.timeFormat("HH:mm:ss")
    }
    writeOutput("event: message" & chr(10))
    writeOutput('data: #serializeJson(data)#' & chr(10) & chr(10))
    flush;
    sleep(1000)
}
writeOutput("event: message" & chr(10))
writeOutput('data: #serializeJson({"complete": true})#' & chr(10) & chr(10))
flush;
</cfscript>

You will note that even after the loop finishes and the request ends... The client re-polls again to keep the event stream running.

If this is not answering the question, can you please clarify the question, and possibly swap out the code you have for something more portable (as per my comment against your question).

Related