Skip to content

How it works

Matthias Isler edited this page Jan 8, 2018 · 1 revision

1. Routing queries trough Lada Cache

In order to be able to cache and invalidate queries, it's important that all queries are routed through this library. This is achieved by overriding the database connection classes of Laravel and by overriding the query builder factory in the models. Unfortunately, Laravel internally doesn't resolve all classes from the IoC container, therefore we can't just register our query builder in it and have it working.

2. Parsing queries

After we are aware of all queries, we need to know certain information about them. Which tables and rows they affect, their type, etc. This is done by the Reflector. The tricky part here is to properly handle sub-queries and stuff like ->withCount(), since Laravel doesn't actually compile them into SQL but fires separate queries.

3. Hashing and tagging queries

Based on the information provided by the Reflector, we are now able to generate a unique hash and a set of tags for every query. The hash is used to check if a query has already been cached. The tags are later on used for invalidating queries on various levels (by table, by row, etc.). As an example, the following query:

select * from users where id = 7

Would result in the hash 2fff44a66c5ea01df4ec3e0ef2d06558 and the following tags:

tags:database:laravel:table_specific:users tags:database:laravel:table_specific:users:row:7

In order to be able to properly hash a query, it must be re-assembled with it's parameters. For security reasons they are sent separately to the database server, e.g. the query doesn't contain them.

4. Serializing and caching queries

After having collected all relevant information, we can either return the cached query value or write it into the cache if it has not yet been cached. This is done by a Cache class which uses a Redis wrapper and an Encoder which serializes the query results.

5. Invalidating queries

As soon as we receive a write query (UPDATE, DELETE, INSERT, ...) we know that we possibly must invalidate some cached queries. Again we are using the Reflector and the Tagger to get all relevant information about the query. Let's take for example the following query:

update users set name = 'John Doe' where id = 7

Which would result in the hash 9e8e8ff5584f58de9d8e40daa8c62a47 and the following tags:

tags:database:laravel:table_specific:users:row:7

The Invalidator now deletes all queries from the cache which are tagged with the tag above. As you can see, it's quite critical that all queries are tagged properly, since otherwise it would result in outdated data being returned by read queries.

It is important to mention that cached queries, by default, do not have TTL set, e.g. they never expire. Since they invalidation mechanism is supposed to remove all outdated queries immediately, this is simply not required resp. would even result in unnecessary misses. If, however, the memory of your server is limited, you might want to set a reasonable TTL in the config.