-
Notifications
You must be signed in to change notification settings - Fork 4
Caching
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.
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.