When writing offline mode, attachments proved to be a uniquely difficult aspect of syncing.
The nature of attachments differ from other data in Trello. Most data in Trello are simple numbers and strings. Files are an entirely different beast; they’re large and unwieldy.
On Android, when a file is shared with your app, you are given a URI and permission to read from that URI.
Back in the days of sharing file paths, the permission was essentially permanent (unless the file was moved or deleted). However, the correct way to do file sharing is via non-file-path URIs that only grant temporary read permission.
When we were online-only, this wasn’t a problem: we would just immediately upload from the source and that was that. But with offline, it might be a long time before we have a chance to upload the attachment. That meant we would lose permission to read the source by the time we uploaded. As such, we needed to make a local copy of the file until we could upload it.
It’s an easy solution, but not obvious at first. In general, if you’re working with shared files, you will need to either use it immediately or store on your own.
Blocking the Queue
We upload each change, one-by-one, in order to guarantee no issues with the model hierarchy (e.g., ensuring we create a card before adding any attachments to it). However, attachments created a huge risk in terms of blocking this queue because:
- Attachments take a lot longer to upload than other requests.
- Attachments fail more often than other requests. Long upload times create plenty of opportunities for spotty internet to screw everything up.
The end result was that the app would feel much less responsive. Imagine you attach a large file then go edit some card names. The card name changes wouldn’t show up for minutes while the attachment would upload. If the attachment failed, exponential backoff would kick in, making the card name edits take even longer. Why should they have to wait for the attachment?
Our solution was to create a second queue just for attachment deltas. All non-attachment changes are processed first, then attachments go last. There are no model hierarchy issues with this since attachment models have no children.
This solution works great. All quick and fast edits now get synced on a regular basis, even if your attachment always fails to upload. It keeps the app feeling zippy even if you’ve uploaded excessively large files for your Edge connection.
That said, the attachment queue itself can still get blocked up. There isn’t any solution to that problem - if you don’t have the network quality necessary to upload large files, it just won’t work. We cap the number of times you can retry any request; at some point we may just give up on an attachment if we can never sync it to the server.
This article was originally posted on the Trello engineering blog and has been reproduced here for posterity.