Skip to content
Andrew Geweke edited this page Nov 2, 2013 · 6 revisions

One of the key reasons that low_card_tables works as well as it does is that it simply caches the entire low-card table in memory, as a set of model objects. This means that almost all operations happen incredibly quickly, with no database queries whatsoever; it only needs to hit the database when it creates a new row, or if it sees a reference to a row that has been created since the cache was loaded — and this all happens automatically, and transparently.

IMPORTANT

There is one caveat, though, and it concerns queries. In short:

  • Caching can, in certain cases, cause queries against low-card attributes to miss rows that have been recently created; you will see a query not return rows that it should. These cases range from uncommon to nonexistent, depending on your application.
  • low_card_tables contains caching policies that can eliminate this completely (by turning off caching) or reduce the risk of it happening enormously.
  • Surprisingly, for many use cases, this is not a problem.

The default caching policy reduces the risk of encountering this issue a great deal. If you want to be extra-paranoid, simply say LowCardTables.low_card_cache_expiration 0, and caching will be turned off completely. But read on for more details.

The example:

Say you have two processes (for example, two Rails Unicorn or Passenger processes), A and B, that each access the low-card table. Now, say you have the following rows:

user_statuses
+-----------------------+
| id | deleted | gender |
+----+---------+--------+
| 1  | 0       | female |
| 2  | 0       | male   |
| 3  | 1       | male   |
+-----------------------+

(This would occur only if no women have ever deleted their accounts, for example.)

Imagine process A and process B both have cached this table. Now, a woman deletes her account; process B handles that request. When saving this user record, low_card_tables will see a request for a row with { :deleted => true, :gender => 'female' }, not find it, and create it as ID 4; its cache will then contain all four rows.

Now, someone accesses an admin page that displays a list of all deleted users, and process A serves it. It will run a query like User.where(:deleted => true). Internally, low_card_tables uses its cache to translate this to a list of IDs that have { :deleted => false }. However, because process A has not flushed its cache yet, it doesn't know about the row with ID 4. It issues a query like SELECT * FROM users WHERE user_status_id IN (3). Because the newly-deleted user has user_status_id = 4, that user is not displayed on that page, incorrectly.

Clone this wiki locally