So I created an app using Ionic and Firebase as my back-end. When the app is run in a web browser or on an iOS emulator, the response is very fast and the app works really well. On iOS however, uploading anything to Firebase takes forever. Note that downloading information from Firebase is fairly fast and simple. Uploading however poses an issue. The wifi I am testing this on is very fast. Does anyone know why this is happening?
The app was released recently and this has been an issue for a lot of my users and myself included!
UPDATE: So after more testing it appears that the issue is specifically with certain functions. These methods are .update() and .add()
Anytime I try to update a field in Firebase it takes forever. Anytime I try to add a document to a collection it also takes forever. Why is this occuring? Here's some code that takes forever to achieve:
async createDMChat(otherUID: string, otherName: string) {
let newDoc: DocumentReference = this.db.firestore.collection('dmchats').doc();
let docId: string = newDoc.id;
let chatsArray = this.dmChats.value;
let timestamp = Date.now();
chatsArray.push(docId);
//Adds to your dm chat
await this.db.collection('users').doc('dmchatinfo').collection('dmchatinfo').doc(this.dataService.uid.value).set({
chatsArray: chatsArray
});
//Adds to other person DM chat
//-------------------THIS IS THE PART THAT TAKES FOREVER-----------------------
//The .update() method is the problem as well as .add() to a collection
await this.db.collection('users').doc('dmchatinfo').collection('dmchatinfo').doc(otherUID).update({
chatsArray: firebase.firestore.FieldValue.arrayUnion(docId)
});
//Pull info on person's UID
let otherUserInfo = await this.db.firestore.collection('users').doc('user').collection('user').doc(otherUID).get();
let otherAvatar = otherUserInfo.data().avatar;
//Sets message in database
await newDoc.set({
chatName: otherName + " & " + this.dataService.user.value.name,
users: [otherUID, this.dataService.uid.value],
lastPosted: timestamp,
avatar1: this.dataService.avatarUrl.value,
avatar2: otherAvatar,
person1: otherName,
person2: this.dataService.user.value.name
});
await newDoc.collection('messages').doc('init').set({
init: 'init'
});
await this.dataService.storage.set(docId, timestamp);
}
In the above code, the .update() is the method that takes forever. Also other functions with the .add() method adding documents to a collection takes forever.
Again THESE METHODS ARE FAST ON WEB BROWSERS AND EMULATORS. Just not in the mobile app.
===========================================================================
NEW UPDATE: So it appears that the problem is actually in waiting for the Promise to return. I rewrote all of the functions used to no longer use add() or update(), but rather used set() after making a new document with doc() to replace add(). I then used set({...},{merge: true}) to replace update().
This time around the changes to the server were instant, but the problem came when waiting for the methods to return a promise from the server. This is the part that is causing the lag now. Does anyone know why this is occurring? I could simply change my code to not wait for these promises to return, but I would like to keep await within my code without having this issue.
Related
I am using appwrite for my backend project. and for the getx state manager. So far, everything is fine with me except for realtime. Please explain how the real-time application recording mechanism works. I have seen many video tutorials and read many articles. How can I get it through the stream? I repeated everything exactly as in the video, but it does not work. How to know if the callback fired or not. In the console it only says that the subscription has been created and that's it. there is nothing more. How to get data from this stream.
//
I have a subscription but I can't get a response//
subscription: ws://195.49.210.221/v1/realtime?project=jetservice_server&channels%5B%5D=collection.77079959596.documents
get_realtime_chat_list() async {
var realtime_chat = await serverProvider.realtime.subscribe([
'collection.' +
userdata.userDataStorage.value.read(my_phone) +
'.documents'
]);
realtime_chat.stream.listen((data) {
try {
if (data.payload.isNotEmpty) {
switch (data.events) {
case ['collection.document.create']:
print(data.payload);
break;
case ['collection.document.delete']:
print("document_deleted");
}
}
print(realtime_chat);
} on AppwriteException catch (e) {
print(e.message);
}
});
}
I don't think the channel you subscribed to is correct. As stated in the docs, it should be databases.[ID].collections.[ID].documents.
Also, it seems like the switch statement isn't quite right. I would suggest grabbing the 1st event from the events array and checking that. You might want to check if the event ends with .create or .delete.
Finally, Appwrite's realtime sends messages when they're triggered. Did you create a new document after subscribing? Also, did you ensure the user has access to the documents?
my firebase data structure looks like the following
user
|__{user_id}
|__userMatch
|__{userMatchId}
|__createdAt: <UNIX time in milliseconds>
I'm trying to listen for the child added event under userMatch since a particular given time. Here's my swift code:
func listenForNewUserMatches(since: NSDate) -> UInt? {
NSLog("listenForNewUserMatches since: \(since)")
var handle:UInt?
let userMatchRef = usersRef.childByAppendingPath("\(user.objectId!)/userMatch")
var query = userMatchRef.queryOrderedByChild("createdAt");
query = query.queryStartingAtValue(since.timeIntervalSince1970 * 1000)
handle = query.observeEventType(FEventType.ChildAdded, withBlock: { snapshot in
let userMatchId = snapshot.key
NSLog("New firebase UserMatch created \(userMatchId)")
}, withCancelBlock: { error in
NSLog("Error listening for new userMatches: \(error)")
})
return handle
}
What's happening is that the event call back is called only once. Subsequent data insertion under userMatch didn't trigger the call. Sort of behaves like observeSingleEventOfType
I have the following data inserted into firebase under user/{some-id}/userMatch:
QGgmQnDLUB
createdAt: 1448934387867
bMfJH1bzNs
createdAt: 1448934354943
Here are the logs:
2015-11-30 17:32:38.632 listenForNewUserMatches since:2015-12-01 01:32:37 +0000
2015-11-30 17:45:55.163 New firebase UserMatch created bMfJH1bzNs
The call back was fired for bMfJH1bzNs but not for QGgmQnDLUB which was added at a later time. It's very consistent: after opening the app, it only fires for the first event. Not sure what I'm doing wrong here.
Update: Actually the behavior is not very consistent. Sometimes the call back is not fired at all, not even once. But since I persist the since time I should use when calling listenForNewUserMatches function. If I kill the app and restart the app, the callback will get fired (listenForNewUserMatches is called upon app start), for the childAdded event before I killed the app. This happens very consistently (callback always called upon kill-restart the app for events that happened prior to killing the app).
Update 2: Don't know why, but if I add queryLimitedToLast to the query, it works all the time now. I mean, by changing userMatchRef.queryOrderedByChild("createdAt") to userMatchRef.queryOrderedByChild("createdAt").queryLimitedToLast(10), it's working now. 10 is just an arbitrary number I chose.
I think the issue comes from the nature of time based data.
You created a query that says: "Get me all the matches that happened after now." This should work when the app is running and new data comes in like bMfJH1bzNs. But older data like QGgmQnDLUB won't show up.
Then when you run again, the since.timeIntervalSince1970 has changed to a later date. Now neither of the objects before will show up in your query.
When you changed your query to use queryLimitedToLast you avoided this issue because you're no longer querying based on time. Now your query says: "Get me the last ten children at this location."
As long as there is data at that location you'll always receive data in the callback.
So you either need to ensure that since.timeIntervalSince1970 is always earlier than the data you expect to come back, or use queryLimitedToLast.
I'm running an app, created using IBM MobileFirst 7, on iOS 8.4. At first it works fine: I can write to jsonstore and make other native function calls.
Then the application starts syncing and writing to the JSONStore (a lot of data) and it stops after a while (no errors in console). It usually happens while trying to call the WL.Client.invokeProcedure function.
I also noticed a strange behavior: if I double tap the home button it makes the next WL.Client.invokeProcedure call in the loop and I receive the results from the adapter but then it stops again (everytime I double tap the home button this happens). This also happens with other native calls (e.g. I tried to make a call to other native function in the JS console by using cordova and it only gets called after I double tap the home button).
Does anyone know what may be causing this behavior?~
EDIT:
Regarding the native calls: Since I'm using cordova, I can use a plugin to access some native functionality, example:
//this is what I meant by "native function calls"
cordova.exec(successCallback, errorCallback, 'SFRPowerSave', 'enable', []);
I'm not sure about WL.Client.invokeProcedure and WL.JSONStore functions, but I think they also use native code.
Here's what I'm doing:
//I get the first 50 dirty documents
function push(){
var numberOfDocumentsToPush = 50;
var dirtyDocuments = currentSyncStore.dirtyDocuments.splice(0, numberOfDocumentsToPush);
//then I call the adapter add method and return a promise
return when(WL.Client.invokeProcedure({
adapter : adapter.name,
procedure : adapter.push.procedure,
parameters : [ JSON.stringify(dirtyDocuments),
JSON.stringify({add: addParams}) ],
compressResponse : false
}, {
timeout: adapter.timeout,
invocationContext: {context: this, document: dirtyDocuments}
});
}
//after the promise is returned, I get the next 50 dirty documents and call the push function again, I usually have a lot of dirtyDocuments (lets say 10000).
//After a while it just stops, the WL.Client.invokeProcedure doesn't reject or resolve the promise and no timeout occurs.
//I can interact with the interface of the application but if I try to call some native function, it will not work (but it gets called immediatly after entering the multitask mode - double tap home button in ipad/iphone)
//In the adapter I call the stored procedure from the DB2 database one time for each document:
function pushLogs(logs, params, addFunction){
var parsedParams = JSON.parse(params);
var addParsedParams = parsedParams.add;
var addConfig = getConfig("logs", addFunction, JSON.stringify(addParsedParams));
var parsedLogs = JSON.parse(logs);
var globalResult = {
isSuccessful: true,
responses: []
};
for (var i = 0; i < parsedLogs.length; i++) {
var options = {
values : JSON.stringify(parsedLogs[i].document),
spConfig: addConfig
};
var result = invokeSQLStoredProcedure(options);
globalResult.isSuccessful = globalResult.isSuccessful && result.isSuccessful;
globalResult.responses.push(result);
}
return globalResult;
}
A PMR was opened to handle this question. It has turned out to be a Cordova defect and is now being handled via APAR PI47657: Application hangs when trying to call JSONStore asynchronously to sync data.
The fix will appear in a future iFix release, available at IBM Fix Central (as well as via the PMR opened).
I have an iPhone app which is pulling just about all it's data from ASP.NET MVC services.
Basically just returning JSON.
When I run the app in the simulator, the data is pulled down very fast etc.. however when I use the actual device (3G or WiFi) it's extremely slow. To the point that the app crashes for taking too long.
a) Should I not be calling a service from the FinishedLaunching method in AppDelegate?
b) Am I calling the service incorrectly?
The method I'm using goes something like this:
public static JsonValue GetJsonFromURL(string url) {
var request = (HttpWebRequest)WebRequest.Create (url);
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
using(var response = (HttpWebResponse)request.GetResponse()) {
using(var streamReader = new StreamReader(response.GetResponseStream())) {
return JsonValue.Load(streamReader);
}
}
}
Is there a better or quicker way I should be querying a service? I've read about doing things on different threads or performing async calls to not lock the UI, but I'm not sure what the best approach or how that code would work.
a) Should I not be calling a service from the FinishedLaunching method in AppDelegate?
You get limited time to get your application up and running, i.e. returning from FinishedLaunching or the iOS watchdog will kill your application. That's about 17 seconds total (but could vary between devices/iOS versions).
Anything that takes some time is better done in another thread, launched from FinishedLaunching. It's even more important if you use networking services as you cannot be sure how much time (or even if) you'll get an answer.
b) Am I calling the service incorrectly?
That looks fine. However remember that the simulator has a faster access to the network (likely), much more RAM and CPU power. Large data set can take a lot of memory / CPU time to decode.
Running from another thread will, at least, cover the extra time required. It can be as simple as adding the code (below) inside your FinishedLaunching.
ThreadPool.QueueUserWorkItem (delegate {
window.BeginInvokeOnMainThread (delegate {
// run your code
});
});
You can have a look at how Touch.Unit does it by looking at its TouchRunner.cs source file.
note: you might want to test not using (asking) for compressed data since the time/memory to decompress it might not be helpful on devices (compared to the simulator). Actual testing needed to confirm ;)
FATAL ERROR: CALL_AND_RETRY_2 Allocation Failed - process out of memory
I'm seeing this error and not quite sure where it's coming from. The project I'm working on has this basic workflow:
Receive XML post from another source
Parse the XML using xml2js
Extract the required information from the newly created JSON object and create a new object.
Send that object to connected clients (using socket.io)
Node Modules in use are:
xml2js
socket.io
choreographer
mysql
When I receive an XML packet the first thing I do is write it to a log.txt file in the event that something needs to be reviewed later. I first fs.readFile to get the current contents, then write the new contents + the old. The log.txt file was probably around 2400KB around last crash, but upon restarting the server it's working fine again so I don't believe this to be the issue.
I don't see a packet in the log right before the crash happened, so I'm not sure what's causing the crash... No new clients connected, no messages were being sent... nothing was being parsed.
Edit
Seeing as node is running constantly should I be using delete <object> after every object I'm using serves its purpose, such as var now = new Date() which I use to compare to things that happen in the past. Or, result object from step 3 after I've passed it to the callback?
Edit 2
I am keeping a master object in the event that a new client connects, they need to see past messages, objects are deleted though, they don't stay for the life of the server, just until their completed on client side. Currently, I'm doing something like this
function parsingFunction(callback) {
//Construct Object
callback(theConstructedObject);
}
parsingFunction(function (data) {
masterObject[someIdentifier] = data;
});
Edit 3
As another step for troubleshooting I dumped the process.memoryUsage().heapUsed right before the parser starts at the parser.on('end', function() {..}); and parsed several xml packets. The highest heap used was around 10-12 MB throughout the test, although during normal conditions the program rests at about 4-5 MB. I don't think this is particularly a deal breaker, but may help in finding the issue.
Perhaps you are accidentally closing on objects recursively. A crazy example:
function f() {
var shouldBeDeleted = function(x) { return x }
return function g() { return shouldBeDeleted(shouldBeDeleted) }
}
To find what is happening fire up node-inspector and set a break point just before the suspected out of memory error. Then click on "Closure" (below Scope Variables near the right border). Perhaps if you click around something will click and you realize what happens.