Skip to content

Store persistent data in a sound way #4841

Closed
@gnprice

Description

@gnprice

The way we currently store the user's data on the device's storage, between runs of the app, is with the design from redux-persist:

  • The units of storage are the top-level subtrees of the Redux state: state.accounts is one, state.users is another, and so on.
  • When something changes, we write out the new version of each top-level subtree that changed: if a user changed their avatar then we write out the entire new state.users, and so on.
  • When things change across several different top-level subtrees, we write out each of them separately.

As a result, it's possible for the stored data to get into a state that was never the state inside the app, and that's internally inconsistent. This was the cause of #4587, for example.

For the most part, this doesn't cause user-visible problems today -- the crashes in #4587 were an exception to that pattern.

But the reason it doesn't is that we currently don't really rely that heavily on the stored data: each time the app starts up, we go and do an initial fetch of the server data from scratch. That means that for all the data that ultimately comes from the server -- which includes most of our data and most of the complexity in our data -- any inconsistencies that creep into the stored data will typically get wiped away almost immediately after we first even read it from storage. (The way that issue #4587 managed to evade that protection is that it caused a crash before we got far enough to do that initial fetch.) So we're largely protected from consequences of this issue… but the price is that we're not really getting much use out of storing that data.

There are several things we'd like to do that would mean relying more firmly on the stored data, and not constantly refreshing it from scratch:

  • The status quo means that when you launch the app, the data you're looking at is stale and there's a long lag before you see up-to-date data. This is an especially bad experience when you got there by opening a notification -- there's a message you know you got, it was right there in the notification, but we don't initially know about it. We have the basic design for how to fix this, as Support downgrading to a long-lived event queue #3916; but it means maintaining your data in device storage and relying on that, rather than refreshing from scratch.

  • We'd like to improve the experience of switching between several accounts (in several orgs): Add quick organization switcher #1321. Doing a really good job of that would require maintaining all the same data about all your accounts as we currently do about the one active account; and fundamentally, doing much at all in that direction will require maintaining at least some data about all your accounts. In principle I guess we could maintain all that data while keeping the reload-from-scratch-on-each-launch strategy; but it'd mean even more data getting reloaded from scratch, so even more lag.

  • We'd like to offer fuller-featured, higher-quality notifications: with an inline reply action (Allow message responses from within Android notification bar #2512), with end-to-end encryption from the Zulip server (Encrypt push notifications end-to-end from Zulip server #1190), pre-fetching the conversation's messages so it's ready to go if you open the notification (Eagerly fetch relevant conversation on getting notification #3599), among other things.

    All of these require more integration between the platform-native code (in Kotlin for Android, and in the future in Swift for iOS) and JS than we currently have: either for the platform-native code to see more of the information that the JS code has, or to send more information to JS, or both. That would be a lot easier if we have the data in a reasonable schema that the platform-native code can consume directly. Even if we don't do that -- even if we do all the integration by having the platform-native code call into JS -- it means continuously using the stored data, beyond the moments after each launch.

If we were to pursue any of those things without giving attention to this issue, it's likely we would encounter a lot more bugs like #4587. That kind of bug can be quite bad for a user, and they're pretty unpleasant to debug too, because fundamentally they involve our data being in a state that doesn't make any sense.

As a result, we need to do some combination of fixing this issue properly; mitigating it to reduce its impact; and carefully working around it in the context of any of the above features that we build before we've fixed it.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions