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 stored it, next to the value. Its age is the time passed since then: now().Sub(t) gives it, and .Seconds() turns it into a number of seconds. Here is the idea on another value, a remembered weather report that may be used for 10 minutes:
weather := "sunny"
weatherTime := now()
if now().Sub(weatherTime).Seconds() < 600 {
fmt.Println("still fresh:", weather)
}
Ask the time through a package variable now that holds the clock function time.Now, instead of calling time.Now() everywhere: a variable is one place a test can replace. The check swaps your now for a fake clock that it sets with each request, so it can jump a minute ahead without waiting. 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 stored 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: below cache, add a map cacheTimes of type map[string]time.Time, a constant cacheSeconds set to 60 and a package variable now holding time.Now (the time import is already there). In the GET /products handler, store now() in cacheTimes["products"] next to the fresh list, and answer from the cache, then stop, only if ok is true and the list's age is less than cacheSeconds.
Assignment
Follow the Coding Tutorial to give the cached product list a time to live of 60 seconds.
Hint
Write time.Now without parentheses: that is the function itself, so now() gives the time. Put ok first in the if: thanks to &&, the age only matters when the list is there. Don't forget the return, or the handler goes on and runs the query anyway.
Free plan: 30 tutor messages. PRO is unlimited.
