Firebase onDisconnect when turning WIFI off - firebase

I noticed when I set up an onDisconnect(), hit my application on a different computer, and turn the WIFI off, my db is not updated; however, it is updated when I turn the WIFI back on. This worries me because I am building an application with expected mobile users and I want to gracefully handle temporary connection drops.
On the otherhand, /.info/connected knows about the disconnection and connection immediately.
Can anyone explain why this is happening and if there is a way to prevent the disconnect from happening once connection is reestablished?
Updated code:
var connectedRef, userRef;
connectedRef = new Firebase('https://{fb}/.info/connected');
userRef = new Firebase('https://{fb}/users/myUser');
connectedRef.on('value', function (snap) {
if (snap.val()) {
userRef.update({ online: true });
userRef.onDisconnect().update({ online: false }, function () {
console.log('Turn the Wi-Fi off after seeing this log.');
});
}
});
Result: The db does not set online to false when I turn the Wi-Fi off, unless I wait about 1 minute. The db does set online to false when I turn the Wi-Fi back on.

Turning off your wifi does not close the sockets in an efficient manner. Thus, the server has to wait for the socket to time out before it can fire onDisconnect. Since this is an entirely server-side process, the only possible outcomes are:
1) The user isn't allowed to perform the onDisconnect op (indicated in the callback immediately upon establishing the onDisconnect)
2) The event will fire when the socket times out or disconnects (the length of time is completely up to the browser/server negotiation (1 minute is not unreasonable)
3) Some data changes in Firebase between the time of establishing onDisconnect and the event firing that makes it invalid (the security rules won't allow it because the op is no longer valid)
To see your onDisconnect() fire a bit faster, try using goOffline(), which I believe will properly close the socket connections.

Related

Firebase OnDisconnect Event

so the question is about onDisconnect in firebase, I am implementing a chatapp like whatsapp in flutter and using Firebase, stuck at this point the onDisconnect event takes 2-3 mins to fire when you have no internet access:
I know it takes that lot of time, because it is waiting the sockets to timeout to nominate the user has completely disconnected
I tried using goOffline but with no luck, at least I wanted to make onDisconnect event to be fired faster.
I have a workaround by making a Timer in flutter and every X period I update with timestamp indicating that this user is alive, but the question is who will trigger the offline status for this user when he is offline? a cloud function, or another online connected user? Since, this user didn't respond in X period so we need to update his status to offline.
Is there any workaround for this issue?
Thanks in advance.
sample code I am using:
in the initState() I am listening to /.info/connected:
FirebaseDatabase.instance
.reference()
.child('/.info/connected')
.onValue
.listen((data) {
print('bool value');
print(data.snapshot.value);
if (data.snapshot.value == false) {
//user dc
print('user disconnected');
FirebaseDatabase.instance.goOffline().then((value) => print("offline"));
} else {
print('user reconnected');
FirebaseDatabase.instance.goOnline().then((value) => print("user online again"));
FirebaseDatabase.instance.reference().child('uid').onDisconnect().set({'status': 'offline'}).then((value) => print('here I am '));
}
});
If the server-side onDisconnect does not for your use-case, the common approach is indeed to implement your own mechanism. Your proposed keep-alive approach still needs an arbiter to determine when a connection is dead. That is definitely possible, but would typically require a Cloud Function or more permanent listener.
Instead of that, I'd usually add a lastSeenTimestamp for each user/connection, and then have the clients that read those timestamps for everyone determine how fresh they want the connection to be before considering a user stale.
You could still use onDisconnect in that case to clean up the data, but you're not longer tied to the socket timeout interval of the server to mark the user as inactive in your clients.

firebase onDisconnect() is not fired when user lose connection

Hello React native community, I'm trying to use onDisconnect() in firebase but the problem is that the void isn't getting fired when the network loses its connection but it is working if I close the app or when the app crashes.
This code is working if Wi-Fi is on but it's not working at all if Wi-Fi is off..
firebase.database().ref('users/test/connected').onDisconnect().set(false)
Any ideas?
You can combine disconnect operations with connection status monitoring and server time stamps to build a user connection status system. On this system, each user stores data in a specific database location to alert the real-time database client to online. The client sets this location to true when it comes online and to time stamp when it disconnects. This timestamp indicates the last time the user was online.
Apps have a disconnect operation ahead of the user's online display, so that there is no contention if the client loses network connectivity before the two commands are sent to the server.
// since I can connect from multiple devices or browser tabs, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
var myConnectionsRef = firebase.database().ref('users/test/connections');
// stores the timestamp of my last disconnect (the last time I was seen online)
var lastOnlineRef = firebase.database().ref('users/test/lastOnline');
var connectedRef = firebase.database().ref('.info/connected');
connectedRef.on('value', function(snap) {
if (snap.val() === true) {
// We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect)
var con = myConnectionsRef.push();
// When I disconnect, remove this device
con.onDisconnect().remove();
// Add this device to my connections list
// this value could contain info about the device or a timestamp too
con.set(true);
// When I disconnect, update the last time I was seen online
lastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);
}
});

Firestore Timeout [duplicate]

We are building a real-time chat app using Firestore. We need to handle a situation when Internet connection is absent. Basic message sending code looks like this
let newMsgRef = database.document(“/users/\(userId)/messages/\(docId)“)
newMsgRef.setData(payload) { err in
if let error = err {
// handle error
} else {
// handle OK
}
}
When device is connected, everything is working OK. When device is not connected, the callback is not called, and we don't get the error status.
When device goes back online, the record appears in the database and callback triggers, however this solution is not acceptable for us, because in the meantime application could have been terminated and then we will never get the callback and be able to set the status of the message as sent.
We thought that disabling offline persistence (which is on by default) would make it trigger the failure callback immediately, but unexpectedly - it does not.
We also tried to add a timeout after which the send operation would be considered failed, but there is no way to cancel message delivery when the device is back online, as Firestore uses its queue, and that causes more confusion because message is delivered on receiver’s side, while I can’t handle that on sender’s side.
If we could decrease the timeout - it could be a good solution - we would quickly get a success/failure state, but Firebase doesn’t provide such a setting.
A built-in offline cache could be another option, I could treat all writes as successful and rely on Firestore sync mechanism, but if the application was terminated during the offline, message is not delivered.
Ultimately we need a consistent feedback mechanism which would trigger a callback, or provide a way to monitor the message in the queue etc. - so we know for sure that the message has or has not been sent, and when that happened.
The completion callbacks for Firestore are only called when the data has been written (or rejected) on the server. There is no callback for when there is no network connection, as this is considered a normal condition for the Firestore SDK.
Your best option is to detect whether there is a network connection in another way, and then update your UI accordingly. Some relevant search results:
Check for internet connection with Swift
How to check for an active Internet connection on iOS or macOS?
Check for internet connection availability in Swift
As an alternatively, you can check use Firestore's built-in metadata to determine whether messages have been delivered. As shown in the documentation on events for local changes:
Retrieved documents have a metadata.hasPendingWrites property that indicates whether the document has local changes that haven't been written to the backend yet. You can use this property to determine the source of events received by your snapshot listener:
db.collection("cities").document("SF")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
let source = document.metadata.hasPendingWrites ? "Local" : "Server"
print("\(source) data: \(document.data() ?? [:])")
}
With this you can also show the message correctly in the UI

SignalR Issues with multiple requests

In my application, I'm hosting a fairly CPU-intensive engine on a web server, which is connected to clients via SignalR. From the client, the server will be signalled to do some work (via an AJAX request), and every 200ms will send down a queue of "animation events" which describe the work being done.
This is the code used to set up the connection on the client:
$.connection.hub.start({ transport: ['webSockets', 'serverSentEvents', 'longPolling'] })
And here's the related code in the backend:
private const int PUSH_INTERVAL = 200;
private ManualResetEvent _mrs;
private void SetupTimer(bool running)
{
if (running)
{
UpdateTimer = new Timer(PushEventQueue, null, 0, PUSH_INTERVAL);
}
else
{
/* Lock here to prevent race condition where the final call to PushEventQueue()
* could be followed by the timer calling PushEventQueue() one last time and
* thus the End event would not be the final event to arrive clientside,
* which causes a crash */
_mrs = new ManualResetEvent(false);
UpdateTimer.Dispose(_mrs);
_mrs.WaitOne();
Observer.End();
PushEventQueue(null);
}
}
private void PushEventQueue(object state)
{
SentMessages++;
SignalRConnectionManager<SimulationHub>.PushEventQueueToClient(ConnectionId, new AnimationEventSeries { AnimationPackets = SimulationObserver.EventQueue.FlushQueue(), UpdateTime = DateTime.UtcNow });
}
public static void PushEventQueueToClient(string connectionId, AnimationEventSeries series)
{
HubContext.Clients.Client(connectionId).queue(series);
}
And for completeness' sake, the related Javascript method:
self.hub.client.queue = function(data) {
self.eventQueue.addEvents(data);
};
When testing this functionality on localhost, it works absolutely smoothly, with no delay (as you would expect), using serverSentEvents as a transport method.
However, when used in production, this more often than not takes a very long time to complete. Using SignalR's logging and a bit of my own instrumentation, it can be seen that the first series of events reaches the client within a couple of seconds, which is totally acceptable. However, after that SignalR often gives the following error:
Keep alive has been missed, connection may be dead/slow.
Followed soon after by:
Keep alive timed out. Notifying transport that connection has been lost.
This will happen a few times, and then eventually, up to a minute later, the events will arrive, with my own instrumentation showing that they were sen from the server approximately 200ms apart, as expected. It can also be seen that in production, they were sent with the primary transport method, web sockets.
Is anyone aware of any issues that sending multiple SignalR requests on a timer might cause? Like I say, this primarily seems to happen with web sockets. I've been told that using web sockets is best practice, so I'm keen to keep using them, but if there isn't a workaround to these kinds of issues, then I'm afraid I'll have to remove them permanently.
Edit
I've now removed the option to use web sockets on the live site, and I'm running into the same issues with server sent events - several failed attempts to reconnect after the first queue update arrives.
Summing up our discussion, I don't think there are specific issues with websockets/signalr on azure.
I've sample code here: https://github.com/jonegerton/SignalR.StockTicker which can be used for testing, with some minor tweaks (I'll probably develop it as a test platform at some point).
Its based on the sample project from MS which can be found here: https://github.com/SignalR/SignalR-StockTicker.
I've put an example in azure here (http://stockticker.azurewebsites.net) for testing purposes. It has the default transport configurations enabled (ie websockets >> serversentevents >> longpolling)

How to check if Meteor.call() fails when server connection is down?

When the Meteor server connection is lost, how can I verify that Meteor.call() failed? Meteor.call() doesn't return any value. Basically Ctrl+Z in the Meteor shell when your app is running, then do something in the app that triggers a Meteor.call i.e. adding a new blog post:
Meteor.call('createPhrase', phrase, function(error) {
console.log("This NEVER gets called if server is down.");
if (error) {
throwError(error.reason);
}
});
I tried using Session vars, but the reactivity screws it up, i.e. the code below will trigger an error in my template handler (that get's flashed to the browser quickly) and as soon as isMyError is set to true, then when the Meteor.call is successful the error goes away as per isMyError = false, but this looks really sloppy.
Session.set("isMyError", true);
Meteor.call('createPhrase', phrase, function(error) {
console.log("This NEVER gets called if server is down.");
Session.set("isMyError", false);
if (error) {
throwError(error.reason);
}
});
Template.index.isMeteorStatus = function () {
myClientStatus = Meteor.status();
if ( (myClientStatus.connected === false) || (Session.get("isMyError") === true) ) {
return false;
} else {
return true;
}
};
Meteor's calls are generally entered into a queue that are sent to the server in the order that they are called. If there is no connection they stay in the queue until the server is connected once more.
This is the reason nothing is returned because Meteor hopes that it can reconnect then send the call and when it does it does eventually return a result then.
If you want to validate whether the server is connected at the point of the call it's best to check Meteor.status().connected (which is reactive) and only run Meteor.call if it is else throw an error
if(Meteor.status().connected)
Meteor.call(....
else throwError("Error - not connected");
You could also use navigator.onLine to check whether the network is connected.
The reason you would experience a 60 second delay with Meteor.status().connected on the true status of whether meteor is connected or not is there isn't really a way for a browser to check if its connected or not.
Meteor sends a periodic heartbeat, a 'h' on the websocket/long polling wire to check it is connected. Once it realizes it didn't get a heartbeat on the other end it marks the connection disconnected.
However, it also marks it as disconnected if a Meteor.call or some data is sent through and the socket isn't able to send any data. If you use a Meteor.call beforehand to Meteor.status().connected it would realize much sooner that it is disconnected. I'm not sure it would realize it immediately that you can use them one line after the next, but you could use a Meteor.setTimeout after a second or two to fire the call.
Attempt to succeed:
Meteor is designed very well to attempt to succeed. Instead of 'attempting to fail' with an error stating the network is not available its better to try and queue everything up until the connection is back.
The best thing to do would be to avoid telling the user the network is down because usually they would know this. The queued tasks ensure the userflow would be unchanged as soon as the connection is back.
So it would be better to work with the queues that are built into the reconnection process rather than to avoid them.

Resources