Start learning free

LESSON 125 OF 173 · BACKEND: SPEED

Expire the Cache

Practice

Clearing the cache works for changes made through the shop's own routes. But other programs change the database too, like a stock import or an admin tool, and nobody clears your cache for them. The safety net is a TTL (time to live): every cached answer gets an expiry, and after it the answer is thrown away and worked out again.

To know an answer's age, remember when you saved it. Its age is the current time minus the saved time. Here is the idea on another value, a remembered weather report that may be used for 10 minutes:

var weather = "sunny";
var weatherTime = Now();

if (Now() - weatherTime < 600) {
    Console.WriteLine("still fresh: " + weather);
}

Ask the time through a small clock function, Now(), instead of working out DateTimeOffset.UtcNow in every place: one function gives the time in seconds, and it is the one place to swap for a fake clock. The check doesn't wait a real minute either: it sends a fake time with each request, and the clock your code reads follows it. With a TTL of 60 seconds, your code must give these answers, in this order:

GET /products  (clock at 1000)  →  200  [{"category": "kitchen", "id": 1, "name": "Mug", …}, … 8 products]
GET /products  (clock at 1059)  →  200  [the same 8 products]
GET /debug/queries  →  200  {"queries": 1}
GET /products  (clock at 1060)  →  200  [the same 8 products]
GET /debug/queries  →  200  {"queries": 2}
GET /products  (clock at 1119)  →  200  [the same 8 products]
GET /debug/queries  →  200  {"queries": 2}

At 1059 the list is 59 seconds old, so it comes from the cache. At 1060 it is 60 seconds old, which is not less than 60: the query runs, and the fresh list is saved with the time 1060. At 1119 that list is 59 seconds old again. A shorter TTL gives fresher answers and more queries; a longer one, fewer queries and older answers.

Your task: add a constant CACHE_SECONDS set to 60, a clock function Now() that returns DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0, and a dictionary cacheTimes. In the /products route, save Now() in cacheTimes["products"] when you cache the list, and answer from the cache only if cache has the key "products" and its age is less than CACHE_SECONDS.


Assignment
Follow the Coding Tutorial to give the cached product list a time to live of 60 seconds.


Hint
The age is Now() minus cacheTimes["products"]. Put the key test first in the if: thanks to &&, the saved time is only read when the key is there.


STEP 1 OF 7
    Ctrl + Enter to check