<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Caching on Murat Useinov</title>
    <link>https://useinov.com/tags/caching/</link>
    <description>Recent content in Caching on Murat Useinov</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 16 Jun 2025 21:10:00 +0300</lastBuildDate>
    <atom:link href="https://useinov.com/tags/caching/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>One TTL does not fit the whole page</title>
      <link>https://useinov.com/posts/2025/one-ttl-lie/</link>
      <pubDate>Mon, 16 Jun 2025 21:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/one-ttl-lie/</guid>
      <description>&lt;p&gt;&amp;ldquo;Hello, Ivan&amp;rdquo; in the header. That is what broke the full-page cache on one project: either the whole page became uncacheable, or Ivan&amp;rsquo;s greeting was served to strangers. We managed both, in the same month.&lt;/p&gt;
&lt;p&gt;Full-page cache behind a reverse proxy is a great first step. It dies on the first personalized element.&lt;/p&gt;
&lt;p&gt;The way out is to stop thinking of the page as one object. It is regions with different speeds. The header with the user name changes per user. Navigation changes per deploy. The product list changes every few minutes. Prices and stock change every few seconds. Give all of that one TTL and you must pick the minimum, which means almost no cache at all.&lt;/p&gt;</description>
    </item>
    <item>
      <title>TTL is not an invalidation strategy</title>
      <link>https://useinov.com/posts/2021/ttl-not-invalidation/</link>
      <pubDate>Wed, 24 Feb 2021 22:35:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2021/ttl-not-invalidation/</guid>
      <description>&lt;p&gt;Support ticket: a user changed his name, the site still shows the old one. The profile sits in Redis with TTL one hour. Fine, we set five minutes. A week later the hit rate drops and MySQL feels it. This seesaw has no good position. TTL is insurance, not invalidation.&lt;/p&gt;
&lt;p&gt;The obvious upgrade is cache-aside with explicit delete. Read: try the cache, miss, load from the database, write to the cache. Write: update the row, delete the key. Looks correct. It has a race.&lt;/p&gt;</description>
    </item>
    <item>
      <title>What Redis does when memory ends</title>
      <link>https://useinov.com/posts/2019/redis-maxmemory/</link>
      <pubDate>Tue, 02 Apr 2019 15:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2019/redis-maxmemory/</guid>
      <description>&lt;p&gt;Users logged out at random. Not all, not always. Only sometimes, only in the afternoon. Afternoon is when traffic peaks. Traffic peaks fill the cache. The cache lived in the same Redis as the sessions, the instance hit &lt;code&gt;maxmemory&lt;/code&gt;, and the eviction policy was &lt;code&gt;allkeys-lru&lt;/code&gt;. Redis did exactly what we asked: threw away the least recently used keys, and some of them were sessions of people who went for lunch.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
