0
\$\begingroup\$

I'm using Rails with MongoDB for database. In my apps I have 1 action for displaying list of users. User has name, image, score and is_online attributes. The list of users is displayed after ordering with multiple fields (score and is_online) and paging them.

I should now get all users and order by is_online and score and then paging them:

User.where(role_id: Core::Role.where(name: 'member').first.id).order_by([:is_online, :desc], [:score, :desc]).page(params[:page]).per(10)

The problem are:

  1. I think using pagination will ruin the list of users like a duplicate user in some page if the status is online or the score is changed.
  2. If I remove pagination it will overload.

Should I using cache system like redis or else?

\$\endgroup\$
1
\$\begingroup\$

You have a few challenges here.

As you say, pagination will be wonky if a user's status/score changes between page requests. You could use fragment caching to cache the rendered pages, and serve the same content to all users for a fixed period of time. But this has a different drawback: Rendering is lazy, so page 2 (or 3, or N) will only be cached if someone requests it. So that's the same problem as before; sorting may change. Also, if the cache updates between page views, it doesn't matter that you're using a cache, since it's not that same cache version as was used in the previous request.

So what you'll want to do is run something that periodically rebuilds/-queries the entire list, a multi-version cache, and a way to lock a user into a specific version for his/her subsequent page views.

The tradeoff (besides more work) is that the list will be stale after a while.

An example of this is stackexchange's rep leagues tables which, while based on score, lags a bit behind actual score. You can decide whether that's worth the tradeoff.

Now, for querying periodically, I'd recommend

  • using an ActiveJob task to do the querying at fixed intervals
  • storing multiple versions of the query results as a hash in Rails.cache, using a timestamp as key for simplicity
  • a way of cleaning out old results, so stuff doesn't keep accumulating
  • adding a param to page requests to identify the active cache version/timestamp (if blank, serve the latest)
  • some means of telling the user "we're serving you a snapshot of this list as of [time and date]" - and a way of saying "the snapshot you requested is too old, and we've thrown it away"

In the end, it's simpler to only show unpaginated (but limited to top-N) lists with no caching. Or show lists that use a (more) stable sorting parameter (e.g. user name).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.