I am trying to create a Laravel 4 chat app that uses normal-polling. So far it's working fine but when I test it out in two browsers with two different accounts, I found that when I try to send a message from each client(browser) at the same time, client B receives client A's message but client A does not receive client B's msg. Refresh would of course, load client B's msg in client A. How do I solve this?
The reason why I use normal-polling instead of long-polling is because of concurrent connections problem whereby some legacy browsers only limit to 2 concurrent connections. This chat app is planned to accomodate maximum 25 users in a single chat session. I also can't use web sockets due to legacy browser issues. Please correct me if I'm wrong.
p.s. bonus points for better ways to implement this! Thank you!
Here is my client(Javascript) code that updates message.
updatemessages(){
...
$.ajax({
type: "POST",
url: 'create/populate',
data: {
'from': lastMessages,
'conv_id':conv_id
},
success: function(messages) {
$.each(messages, function() {
appendMessage(this);
});
},
error: function(xhr,textStatus,errorThrown) {
console.log(xhr.responseText);
console.log('something went wrong!');
$(this).abort();
},
complete: function() {
window.setTimeout(updateMessages, 2000);
},
dataType: 'json'
});
}
updatemessages();
This is my server code (controller) that receives the message, compares it to the latest message in my db and if found latest message, retrieves it.
public function index()
{
$timestamps = Input::get('from'); //get timestamp of AJAX sent message
$conv_id = Input::get('conv_id'); //get conversation id of AJAX sent conv_id
$allMessages = Messages::with('User')->whereIn('conv_id',$conv_id);
if(is_null($timestamps)){
$messages = Messages::with('User')->whereIn('conv_id',$conv_id)->orderBy('created_at','desc');
}else{
asort($timestamps);
$messages = $allMessages->where('created_at','>',end($timestamps));
}
return $messages->get()->reverse();
}
This is another code that stores the new message into the database.
public function store()
{
$body = Input::get('message');
$user_id = Input::get('user_id');
$conversation = null;
$conv_id = Input::get('conv_id');
return Messages::create(array('body'=>$body,'conv_id'=>$conv_id,'user_id'=>$user_id));
}
Carbon's answer did solve the problem just a little bit but the core problem was that I was using timestamp values with precision up to only seconds instead of a precision up to milliseconds. This was evident in
$messages = $allMessages->where('created_at','>',end($timestamps));
As you can see, that's the main reason why when A and B send message at the same time, they were not received because A's message timestamp is the same as B's message timestamp. So I created another column called milliseconds and everytime I create a new message, I would run
$micro = round(microtime(true) * 1000);
and save the new message like so :
Messages::create(array(...other message datas...,'microseconds'=>$micro));
Then in my load message function in the controller, instead of comparing with the timestamp of the last message that appear in the view, I used Carbon's answer to compare with the time of last polled.
$currentPoll = round(microtime(true) * 1000); //get time in microseconds of time of current poll
$lastPoll = Session::get('lastPoll');//get time of previous poll
$messages = $allMessages->where('microseconds','>',$lastPoll)->orderBy('microseconds','desc');
Session::put('lastPoll',$currentPoll);// update the lastPoll variable to be used in the next request
Finally, in my javascript, I removed any functions that append the message to the view except the one in updateMessages function because only updateMessages is allowed to append the messages. This is to prevent my messages from displaying twice. True, when I send my message, my message does not appear straight away on my view but this is much better than not receiving any messages at all.
and voila~! My problem is solved...