Fix UAID and registration_id confusion in the push component.#4605
Fix UAID and registration_id confusion in the push component.#4605mhammond merged 1 commit intomozilla:mainfrom
Conversation
|
This is a very large patch - sorry about that. I did need to stop myself from going even further though :) This isn't even a breaking change for Fenix! It seems to work well and I think will make push even more reliable for most consumers. There are still further changes we should make to make it even better for webpush, but this is a step in that direction. I'm flagging JR too - feel free to have as deep or light a review as you like, but I'm primary making sure I'm not misunderstanding our conversations about "should never be multiple UAIDs, a change in UAID means old subs are dead", etc. CC @grigoryk |
Codecov Report
@@ Coverage Diff @@
## main #4605 +/- ##
=======================================
Coverage 80.63% 80.63%
=======================================
Files 51 51
Lines 5221 5221
=======================================
Hits 4210 4210
Misses 1011 1011 Continue to review full report at Codecov.
|
9069af8 to
b5ae692
Compare
| fn update_endpoint(&self, channel_id: &str, endpoint: &str) -> Result<bool>; | ||
|
|
||
| fn update_native_id(&self, uaid: &str, native_id: &str) -> Result<bool>; | ||
| // Some of our "meta" keys are more important than others, so they get special helpers. |
There was a problem hiding this comment.
Ah! Yeah. I see the one of the potential problems now.
FWIW, much like tokenserver, there was a thread that devices might have multiple UAIDs when this was first built. (The second UAID would be handled by a marketing thing, apparently, and would be it's own system.) That added a LOT of complexity that was never actually needed.
👍🏻👍🏻 for pulling all that code and simplifying things.
| uaid TEXT NOT NULL, | ||
| channel_id TEXT NOT NULL UNIQUE, | ||
| channel_id TEXT NOT NULL PRIMARY KEY, | ||
| -- `endpoint` must be unique; if 2 scopes ended up with the same endpoint, we'd possibly |
There was a problem hiding this comment.
Note: On the server, we use fernet to construct the endpoint, it will always be unique, including if you've already previously registered this UAID+ChannelID. Prior endpoints will continue to work so long as both the UAID and ChannelID remain valid.
| if self.auth.is_none() { | ||
| self.auth = response["secret"].as_str().map(ToString::to_string); | ||
| // asserting here seems bad! :) But what does this mean? We supplied ours, how could | ||
| // the server disagree? (The server seems to only supply this if the UAID changed?) |
There was a problem hiding this comment.
SenderID really, really shouldn't change. It's how the server authenticates to the bridge system for FCM. IIRC, there was a bug, however, where some devices had been registered to a different SenderID, and then a different copy of the UA was reporting a different SenderID value.
Honestly, asserting here is a really good idea if that ever happens, since it indicates something went very, very wrong.
| }; | ||
| if is_new_uaid { | ||
| // apparently the uaid changing but not getting a new secret guarantees we will be | ||
| // unable to decrypt payloads. Not clear how we will recover from this though. |
There was a problem hiding this comment.
You don't.
Honestly, if you have a new UAID, you shouldn't be getting messages from the old UAID, since that indicates a possible routing error on the server. You might want to send an unregister with the old uaid and secret, just to make sure that the server kills off the old one, just to make sure it's dead, but that's probably not needed.
| // > may result in new endpoint registration | ||
| // which is touching on something we need to address - how to better communicate: | ||
| // * we need a new endpoint registration | ||
| // * all subscriptions need updating because we have a new endpoint. |
There was a problem hiding this comment.
I thought that's what the onpushsubscriptionchange message was for? Or am I misunderstanding?
There was a problem hiding this comment.
Thanks, I worded that badly. I changed that part of the comment to read:
// Long story short, there's no way to know that this `update()` call thinks all our
// subscriptions need renewal, but we instead rely on `verify_connection()` being
// called regularly for this purpose.
tarikeshaq
left a comment
There was a problem hiding this comment.
Wow, this is a huge improvement!! A couple of nits and questions but otherwise changelog and 🚢 🚢
|
|
||
| fn get_meta(&self, key: &str) -> Result<Option<String>>; | ||
| fn get_auth(&self) -> Result<Option<String>>; | ||
| fn set_auth(&self, uaid: &str) -> Result<()>; |
There was a problem hiding this comment.
| fn set_auth(&self, uaid: &str) -> Result<()>; | |
| fn set_auth(&self, auth: &str) -> Result<()>; |
| pub fn open_in_memory() -> Result<Self> { | ||
| // A nod to our tests which use this. | ||
| #[cfg(test)] | ||
| env_logger::try_init().ok(); |
| ) | ||
| .into()); | ||
| } | ||
| // Don't fetch the connection from the server if we've already got one. |
There was a problem hiding this comment.
| // Don't fetch the connection from the server if we've already got one. | |
| // Don't fetch the subscription from the server if we've already got one. |
| match e.kind() { | ||
| ErrorKind::UAIDNotRecognizedError(_) => { | ||
| // Our subscriptions are dead, but for now, just let the existing mechanisms | ||
| // deal with that (eg, next `subscribe()` or `verify_subscriptions()`) |
There was a problem hiding this comment.
| // deal with that (eg, next `subscribe()` or `verify_subscriptions()`) | |
| // deal with that (eg, next `subscribe()` or `verify_connection()`) |
|
|
||
| // Dispatch Information returned from [`PushManager::dispatch_info_for_chid`] | ||
| dictionary DispatchInfo { | ||
| string uaid; |
There was a problem hiding this comment.
would this make this a breaking change? Not 100% sure how the dispach_info_for_chid is used on AC though
There was a problem hiding this comment.
Looks like it won't be break our consumers, not sure if it's still considered breaking because https://github.com/mozilla-mobile/android-components/blob/main/components/feature/push/src/main/java/mozilla/components/feature/push/Connection.kt#L226 and https://github.com/mozilla-mobile/android-components/blob/main/components/feature/push/src/main/java/mozilla/components/feature/push/Connection.kt#L165 are the only two uses, and they don't read the uaid field
There was a problem hiding this comment.
yes, thanks, that raises a good question - it's technically a breaking change, but it's not in practice - which raises a kind of philosophical question about whether it should be declared breaking or not!
| pub fn unsubscribe(&mut self, channel_id: &str) -> Result<bool> { | ||
| // TODO(teshaq): This should throw an error instead of return false | ||
| // keeping this as false in the meantime while uniffing to not change behavior | ||
| // markh: both branches below are broken in our v3 schema - someone may have subscribed, |
There was a problem hiding this comment.
Hmm ya, I wonder what happens if we change the behaviour so that unsubscribe doesn't error out even if they try to unsubscribe to a channel they never subscribed to... maybe use the false I wanted to get rid of as an indicator that it was never subscribed to? Wouldn't be a great API experience thou.. thinking out loud
jonalmeida
left a comment
There was a problem hiding this comment.
This looks to me as a consumer. Quite keen for this to land. 🙂
6cf5a27 to
dbd7cae
Compare
There's one UAID and one registration_id per device, not per-subscription. Fixes mozilla#4575 * `uaid`, auth-key and `registration_id` are no longer stored in the database against each subscription, but instead stored in the `metadata` table, and thus assumed to be the same for all subscriptions. * If a `subscribe()` call returns a different UAID than we had previously, we consider all existing subscriptions dead - we delete all old subscriptions and the old UAID, then store the new subscription against the new UAID. * To help with managing the UAID correctly, the `ConnectHttp` object, which holds on to the UAID etc, is now short-lived - it only lives as long as one API call (whereas it previously lived for as long as the push manager). This means that the UAID persisted in the meta-data table is the canonical source of truth. * The "registration id" (think FCM token) is now ignored in the constructor (it should be removed ASAP, but it's there and ignored to avoid a breaking change. The only way to supply this is to call the `update()` method - but the component will persist this value. The end result is that, assuming we've ever called `update()` in the past, is that after startup we can still call `subscribe` before we've called `update()` as we will use the previous value. This means we can avoid some complexity in android-components where we have a complicated error prone dance to avoid constructing the component until FCM has initialized.
There's one UAID and one registration_id per device, not per-subscription.
Fixes #4575
uaid, auth-key andregistration_idare no longer stored in the databaseagainst each subscription, but instead stored in the
metadatatable, andthus assumed to be the same for all subscriptions.
If a
subscribe()call returns a different UAID than we had previously, weconsider all existing subscriptions dead - we delete all old subscriptions
and the old UAID, then store the new subscription against the new UAID.
To help with managing the UAID correctly, the
ConnectHttpobject, whichholds on to the UAID etc, is now short-lived - it only lives as long as one
API call (whereas it previously lived for as long as the push manager).
This means that the UAID persisted in the meta-data table is the canonical
source of truth.
The "registration id" (think FCM token) is now ignored in the
constructor (it should be removed ASAP, but it's there and ignored
to avoid a breaking change. The only way to supply this is to call
the
update()method - but the component will persist this value.The end result is that, assuming we've ever called
update()in thepast, is that after startup we can still call
subscribebefore we'vecalled
update()as we will use the previous value. This means we canavoid some complexity in android-components where we have a complicated
error prone dance to avoid constructing the component until FCM has
initialized.
Pull Request checklist
[ci full]to the PR title.