Business Context of the Problem
Our iphone app allows users to pay merchants and earn rewards.
Users can also do things like:
- View transaction history
- View their points and available rewards, or claim rewards.
- Earn money by referring friends, view a history of converted and unconverted referrals.
- View running deals at some merchants (happy hour specials, etc)
- View information about merchants (location, hours, etc)
Since things like available deals or rewards, transaction history, and referrals can change at any time, we have a classic cache invalidation problem on the user's device.
Current, Unsatisfactory Solution
Our current architecture is fairly standard, and consists of API endoints like "/merchants", "/rewards", "/transactions", "/deals", and so on.
We currently solve the stale cache problem by:
- Polling the server frequently while the app is open (as often as every minute for some API calls), as well as whenever the app regains focus.
- Indicating possibly stale data with text like Last updated 3 mins ago
We use etags to avoid resending unchanged data whole hog (eg, the entire merchant list), but there is still a lot of wasted bandwidth with our current approach. For example, if a single merchant changes, all merchants are resent. If a single new transaction is added, all transactions are resent.
In practice, from the user's perspective, this usually isn't a big deal, as we're talking about sizes of 1-200K at the very most, and typically less. However, it can be a problem in areas with poor cell reception, a situation that comes up often enough that we hear complaints.
And on the server side, all this polling creates a lot of unnecessary load, especially because processing and database queries occur even when etags are unchanged,
We are re-architecting the app and the server code, and thinking about alternative strategies. Since this is a fairly common problem, I'd like to know how others are solving it.
Some ideas I have so far:
For minimizing data transfer: Keep track, on the server, of the data each user actually has. Send JSON patches of only changed data, rather than fresh copies of all the data. Copies of each user's data could be stored in redis, and expire after a few days or a week. This would keep "active session" fast, while user's without a stored server cache could just be sent a fresh copy of all their data when logging in after a dormant period (same as now). While in theory this could work, keeping server and client copies in sync, by patching both, seems like a rich source of potential bugs, even if I'm using vetted JSON patch libraries.
For minimizing server load: Establish web socket connections with each client, instead of polling. Let events that change the client's state (eg, a purchase, a referral converting, a deal ending, etc) push down websocket messages, which in turn trigger the client to refresh. This could be used in conjunction with idea 1, or with the current method of sending fresh copies of all changed data. Even with the current method, this would still be a huge improvement, since data would only be requested and sent when it needed to be.
Right now, I am leaning toward 2 without 1, as 1 seems too potentially fraught with problems. I have some concerns about relying on websockets on mobile, but after doing research here, on SO, and other places, it seems that this concern isn't valid.
I would love to hear other, perhaps radically different, suggestions for solving these problems.