Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push notifications are sent to obsolete _Installation objects #6783

Closed
UnlikelySassou opened this issue Jul 9, 2020 · 8 comments
Closed
Labels
type:question Support or code-level question

Comments

@UnlikelySassou
Copy link

UnlikelySassou commented Jul 9, 2020

Issue Description

Right now, when a user reinstalls the app, it creates a new Installation object. It creates duplicates in the database since multiple Installations are now associated with the same user profile.

The old Installations still exist with a valid deviceToken. So when fetching for Installations that matches a list of recipients, the current and old installations are returned, which can result in hundreds of push notifications sent to APNS.

I thought that setting a limit of 10 for the Installation query would help reduce it, but it doesn't seem to affect the number of Installations returned.

Is there a way to avoid the creation of duplicates? And a way to limit the number of results from an Installation request?

Push Configuration

Here is my cloud function to send push notifications :

Parse.Cloud.define("sendPushToRecipients", function(request, response) 
{
// Find user profiles from recipients  
var profileQuery = new Parse.Query("Profile");
  profileQuery.containedIn("objectId", request.params.recipients);

//  Find devices associated with the user profiles
  var installationQuery = new Parse.Query(Parse.Installation);
  installationQuery.matchesQuery("currentProfile", profileQuery);
  installationQuery.limit = 10; // not working?
  
  Parse.Push.send({
    where: installationQuery,
     data: request.params.data
    }, {
      success: function() {
        response.success("Push sent!");
      },
      error: function(error) {
        response.error("Push failed: " + error);
      },
      useMasterKey: true
    });
});

Environment Setup

  • Server

    • parse-server version : 2.8.4
    • Hardware: Heroku's Standard-1x (512 MB)
    • Localhost or remote server? : Heroku
  • Database

    • MongoDB version: 4.2.8
    • Hardware: Atlas M10
    • Localhost or remote server? : MongoDB Atlas

Logs/Trace

2020-07-09T13:21:00.916855+00:00 app[web.2]: Result: "Push sent!" functionName=sendPushToRecipients, recipients=[hRUTEEGIbh], alert=New invite., sound=default, appVersion=636
2020-07-09T13:21:01.074835+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.074909+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.074991+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075069+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075133+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075213+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075278+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075362+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075427+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075502+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075579+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075609+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx
2020-07-09T13:21:01.075699+00:00 app[web.2]: verb parse-server-push-adapter APNS APNS transmitted to xxxx

@mtrezza mtrezza added the type:question Support or code-level question label Jul 9, 2020
@mtrezza
Copy link
Member

mtrezza commented Jul 9, 2020

Parse Server has a feature to clean up invalid device tokens.

The feature is still flagged as experimental and whether this is safe for you to enable depends on your Parse Server configuration, see this analysis for details. The feature works well for Android, but not so much for iOS because APNS does not seem to report device tokens of uninstalled apps as invalid.

What you could do as a workaround is to add a user reference to the Installation object and whenever the user logs in / out change the reference accordingly.

@UnlikelySassou
Copy link
Author

Yes, I will have to implement a workaround on my next client update.

In the meantime, do you have any idea why I can't limit the number of results from installationQuery in my cloud function? From the documentation, limit seems to be the way to do it, but I can't get it to work here.

You can limit the number of results by setting limit. By default, results are limited to 100. In the old Parse hosted backend, the maximum limit was 1,000, but Parse Server removed that constraint

@mtrezza
Copy link
Member

mtrezza commented Jul 9, 2020

do you have any idea why I can't limit the number of results from installationQuery in my cloud function?

Because it is installationQuery.limit(10); instead of installationQuery.limit = 10;.

@UnlikelySassou
Copy link
Author

Yes I tried both, but still get more than 10 results

@mtrezza
Copy link
Member

mtrezza commented Jul 9, 2020

Right, it seems that pushes are sent in batches of 100 (or according to your configuration) to distribute the DB load. This overwrites the limit parameter that you set in your query.

const DEFAULT_BATCH_SIZE = 100;

What you can do is query the installation IDs first and then use these IDs as condition in the push query:

let installationQuery = new Parse.Query("_Installation");
installationQuery.equalTo("user", aUser);
installationQuery.limit(10);
let installations = await installationQuery.find();
let installationIds = installations.map(i => i.id);

let pushQuery = new Parse.Query("_Installation");
pushQuery.containedIn("objectId", installationIds);

@UnlikelySassou
Copy link
Author

I can confirm that the limit is set to 100 when I inspect the query.

I get the following error after adding the changes:
SyntaxError: await is only valid in async function

Can you guide me on how to make the function asynchrone? I'm sure it's trivial, but I don't write much javascript.

@mtrezza
Copy link
Member

mtrezza commented Jul 13, 2020

You are using an old version of Parse Server. You would have to translate the code in my previous post into chained Promise syntax, just like you have the rest of your code.

More details on the difference between async/await and chained Promises in the migration guide.

@UnlikelySassou
Copy link
Author

Thanks, I will have a close look at it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:question Support or code-level question
Projects
None yet
Development

No branches or pull requests

2 participants