django-push-notifications icon indicating copy to clipboard operation
django-push-notifications copied to clipboard

Disable duplicates of registration_id

Open dmitry-saritasa opened this issue 9 years ago • 24 comments

Right now if I can add unlimited amount of duplicates and then when I'm using bulk send_message I'm getting duplicated push notifications equal to quantity of dups inside DB. Would be nice to prevent such thing and don't add duplicate value to DB

dmitry-saritasa avatar Sep 11 '15 18:09 dmitry-saritasa

What value is duplicate? The registration_id?

jamaalscarlett avatar Sep 17 '15 18:09 jamaalscarlett

correct. It's possible to commit multiple duplicates and when do send_message bulk - it sends a lot of dups

dmitry-saritasa avatar Sep 17 '15 23:09 dmitry-saritasa

But where are the duplicates coming from? You don't create the registration_id yourself, those come from google.

jamaalscarlett avatar Sep 18 '15 06:09 jamaalscarlett

Example for APNS:

POST /api/v1/devices/apns/ HTTP/1.1
Host: domain.com
Content-Type: application/json
Authorization: token 2192967b2fc60623597790783c9590a1964c6e62
Cache-Control: no-cache
{
    "name": "iOS 6.1",
    "registration_id": "4d2d8feaf9a2d780c6c2d18a83c94483462ebe74aafab1f989711e6292b96c9b"
}

response:

{
  "registration_id": [
    "This field must be unique."
  ]
}

example of GCM

POST /api/v1/devices/gcm/ HTTP/1.1
Host: domain.com
Content-Type: application/json
Authorization: token 2192967b2fc60623597790783c9590a1964c6e62
Cache-Control: no-cache

{
    "name": "Android 5.0",
    "registration_id": "APA91bF2FA873EZ8vv-JifiSKPP8wxBCxOfPhLaHkH2d9mYC-CEY_0I0ZRxRPrGqCdCgMybKUPtGKDqNN8ZgTdtHHp7IGdY5DNqzLUK2ylLpffclz-K4MS80XG16pV-DCuzi4EdRltMp"
}

response:

{
  "name": "Android 5.0",
  "registration_id": "APA91bF2FA873EZ8vv-JifiSKPP8wxBCxOfPhLaHkH2d9mYC-CEY_0I0ZRxRPrGqCdCgMybKUPtGKDqNN8ZgTdtHHp7IGdY5DNqzLUK2ylLpffclz-K4MS80XG16pV-DCuzi4EdRltMp",
  "device_id": "",
  "active": true,
  "date_created": "2015-09-18T21:58:35.788556Z"
}

Another call with the same ID:

{
  "name": "Android 5.0",
  "registration_id": "APA91bF2FA873EZ8vv-JifiSKPP8wxBCxOfPhLaHkH2d9mYC-CEY_0I0ZRxRPrGqCdCgMybKUPtGKDqNN8ZgTdtHHp7IGdY5DNqzLUK2ylLpffclz-K4MS80XG16pV-DCuzi4EdRltMp",
  "device_id": "",
  "active": true,
  "date_created": "2015-09-18T21:59:16.759302Z"
}

so I can submit tons of duplicates for GCM while I can't do the same for APNS using django-push-notifications

I propose a consisten behavior between 2 endpoints.

dmitry-saritasa avatar Sep 18 '15 22:09 dmitry-saritasa

My question is, where are these reg ids coming from? It sounds like you are not handling the saving of the registration_id id correctly in your application.

jamaalscarlett avatar Sep 18 '15 22:09 jamaalscarlett

class GCMDevice(Device):
    registration_id = models.TextField(verbose_name=_("Registration ID"))


class APNSDevice(Device):
    registration_id = models.CharField(verbose_name=_("Registration ID"), max_length=64, unique=True)

so what I see needed is unique=True to GCMDevice model definition

dmitry-saritasa avatar Sep 18 '15 22:09 dmitry-saritasa

reg ids are coming from android app. I don't quite understand the question.

dmitry-saritasa avatar Sep 18 '15 22:09 dmitry-saritasa

But if you are getting the same reg_id repeatedly, that means that you are not persisting the registration id properly on the device. Once you have a valid id, you should save it on the phone/tablet so that the next time you authenticate against the gcm server, you don't try and create a new device with your django app.

jamaalscarlett avatar Sep 19 '15 11:09 jamaalscarlett

I've hit the same issue @saritasa has. Is there a good reason there is no unique constraint on the registration id? This not existing causes issues, at least with the rest_framework classes.

For example, if you plug in the GCMDeviceAuthorizedViewSet you can immediately create multiple entries with the same registration id. In turn, this being allowed breaks other endpoints in the viewset, for example viewing an individual GCM device (ie /device/gcm/<registration_id>). This endpoint will throw a "got multiple results" error.

I'm running with the changes in https://github.com/lukeburden/django-push-notifications/commit/c6e04bb2e1466aa064dd4d123ed24a3fe3107e3e for now. The included migration won't be friendly for existing installations with duplicates though. If you think this is the correct approach for your project I'll throw up a pull request. If you have any concrete issues with using a unique constraint for GCMDevice.registration_id, I'd be interest to know of them. :)

Thanks guys!

lukeburden avatar Sep 23 '15 05:09 lukeburden

Like I said, if you are registering multiple devices at once with the same registration id, that sounds like user error, or an implementation error on your part. See issue #197 for a discussion on why registration_id should not be unique.

jamaalscarlett avatar Sep 23 '15 10:09 jamaalscarlett

You don't usually have much control over how the client consumes an API. If you expose endpoints that let clients do something that can cause an internal server error (this is the current state of this library) it's cold comfort to point at the client and say, "You shouldn't do that.". Your poor ops person has still been woken up in the middle of the night by your monitoring system!

Thanks for referencing that other discussion, there are definitely a few complicating factors here.

lukeburden avatar Sep 23 '15 19:09 lukeburden

Agreed with @lukeburden. APNS endpoint is working differently compared to GCM Endpoint and simply stating that the problem is device side doesn't mean GCM endpoint is correct. I had to implement alternative endpoint

POST /devices

{
    "name": "iOS 6.1",
    "type": "apns",
    "registration_id": "4d2d8feaf9a2d780c6c2d18a83c94483462ebe74aafab1f989711e6292b96c9b"
}

and internally do validation and proper error handling for duplicates and routing to what table to persist depending on "type" value.

dmitry-saritasa avatar Sep 24 '15 00:09 dmitry-saritasa

We should be able to apply the unique validator in the GCM serializer. It doesn't solve the underlying DB issue but it would address the client problem.

matthewh avatar Sep 24 '15 19:09 matthewh

Hi all. What about the situation if I have more than one account in my application. I have single device. So if I switch accounts on the same device then client send to server the same registration_id! And I have more than one user with the same registration_id. But registration_id must be unique in django-push-notifications. I would like to know - why???

Closius avatar Oct 25 '15 14:10 Closius

That is another good reason against unique reg ids. The good thing is that the model does not enforce the unique constraint, it is only on the django rest framework serializer. If you do not agree with the implementation, it is not hard to integrate drf yourself. I had been using it before it's inclusion in this library so these changes don't effect my app.

jamaalscarlett avatar Oct 25 '15 18:10 jamaalscarlett

I think that best solution would be uniquetogether of reg_id and user_id. That way you will have protection against multiple regIds and option to have multiple accounts on same device

baneizalfe avatar Nov 01 '15 14:11 baneizalfe

FWIW, I've ended up running with a unique_together of registration_id and user_id in my fork. It provides a sensible amount of restriction (a user shouldn't be able to register the same token twice) whilst coping more gracefully with the fact that you may have different users logging in using an installation of an app, hence with the same registration_id.

This also then plays nicely with the rest framework API components in this project, which are automatically contained to records relating to the authenticated user.

lukeburden avatar Nov 02 '15 22:11 lukeburden

@lukeburden i tried your branch but keep getting tab indent error

baneizalfe avatar Nov 06 '15 17:11 baneizalfe

@baneizalfe some mixed spacing may have crept in. Please create an issue on my fork, not here.

lukeburden avatar Nov 06 '15 18:11 lukeburden

@lukeburden please enable issues on you fork

baneizalfe avatar Nov 07 '15 14:11 baneizalfe

@baneizalfe done, but beware, my fork is for my personal needs at the moment. It has changes that may not play nicely with backends other than PostgreSQL, and especially not MySQL due to constraints on use of unique indexing and TextField. I recommend you use this project if you can.

lukeburden avatar Nov 07 '15 17:11 lukeburden

I believe there are a number of valid approaches to handling the uniqueness of tokens and it will depend on the application designer to determine what is appropriate. I agree that it would be acceptable to use the registration_id and the user_id to determine uniqueness in most cases.

I do not use the user_id. I set the name field to something suitable unique and use that. Perhaps this could just be enforced at the application level? Either (user_id, registration_id) or (name, registration_id) would work well.

matthewh avatar Mar 17 '17 21:03 matthewh

Does anyone know the conclusion of this issue? I'm getting issues with multiple reg_ids in GCM devices

adrianmoisey avatar Dec 08 '17 06:12 adrianmoisey

Same issue here also. Quite annoying. Any idea?

alexislg2 avatar Mar 16 '18 16:03 alexislg2