Jump to content

Yet another WireCache question about expire dates


chrizz
 Share

Recommended Posts

maybe it's to late, maybe I am just overseeing an important detail. 

I have read the docs for WireCache multiple times, I have debugged a lot but for some reason I cannot find a useful explanation and I am starting to feel concerned that my use case isn't even covered by WireCache. Using a expire date during the get requests leads to the desired result only if the original expire date (during save) was lower. This one way approach seems to make ithe following impossible: 

What I want to achieve: 

  • I want to store data in cache retrieved from an external API
  • make sure that data is always present, even if the API is down: I set the expire date during cache->save() to 1 month
  • cached data is retrieved via $cache->get("cacheName") without an explicit expireDate (means: it expires in a month)
  • a cronjob should update this cache on a 5-minute basis. Therefore I am trying to get the cached data via $cache->get("cacheName", (5*60))

Unfortunately it seems as if this cannot be achieved with the WireCache implementation, because lower expireDates during the ->get will *always* result in a "cache miss". It seems that what would help me is a creation timestamp for a cache entry which could be checked as well. The more I think about the more I come to the conclusion that this kind of "two-expire-dates" isn't coverable with WireCache

Do I oversee something or is this known/wanted  behavior?

 

some code in case you want to try it on your own:

$this->cache->save("cachetest","abc", 10);
echo "1: <pre>".print_r($this->cache->get("cachetest"),true)."</pre>";
echo "2: <pre>".print_r($this->cache->get("cachetest", 5),true)."</pre>";
echo "3: <pre>".print_r($this->cache->get("cachetest", 30),true)."</pre>";

sleep(15);

echo "4: <pre>".print_r($this->cache->get("cachetest"),true)."</pre>";
echo "5: <pre>".print_r($this->cache->get("cachetest", 5),true)."</pre>";
echo "6: <pre>".print_r($this->cache->get("cachetest", 30),true)."</pre>";

Output:

1:
abc
2:
3:
abc
4:
5:
abc
6:
abc

I would have expected that number 2 would result in a cache hit, because the expire date is still 5s in the future. 

Link to comment
Share on other sites

The short answer is: don't use a lifetime in calls to $cache->get().

Somewhat longer: it looks like a bug to me. It certainly doesn't do what the docs say, namely check the age of an entry. The behavior is a bit different if you specify a template function as the third value, as that uses the lifetime to fill the cache with the function's return value, but that's neither here nor there.

In your case (unless I'm misinterpreting what you wrote), the only call that needs a lifetime is the one to $cache->save() in your cron job. The other parts can just call $cache->get('cachetest') and be happy (unless the cache wasn't updated within the last month).

  • Like 1
Link to comment
Share on other sites

to me it looks like a bug as well, but I didn't mention it yesterday because I didn't even know what exactly to expect ?

Using the lifetime only during save could lead to unwanted behavior, that the cache is gone during the get request (e.g. the API is down for longer than 5min). That's why I would need some kind of fallback... So basically:

* GET logic: use always data from cache
* SAVE: retrieve data from API and save to cache if last save is > 5min

I think I will go another way, ignoring the cache capabilites and use pages instead. Nevertheless: thanks for your input ?

 

Link to comment
Share on other sites

On 10/28/2021 at 5:08 PM, chrizz said:

Using the lifetime only during save could lead to unwanted behavior, that the cache is gone during the get request (e.g. the API is down for longer than 5min). That's why I would need some kind of fallback... So basically:

* GET logic: use always data from cache
* SAVE: retrieve data from API and save to cache if last save is > 5min

Why don't you split your cron job's logic to use two separate cache calls? $cache->get('cachetest') to read your cached data without expiring it, and $cache->save('cachetest', '+1 month', $apiresult) only if your api call was successful. You'll always have the last successful call's result in your cache this way, and it will be much cheaper than full fledged pages.

Link to comment
Share on other sites

  • 4 weeks later...
On 11/5/2021 at 7:56 AM, BitPoet said:

Why don't you split your cron job's logic to use two separate cache calls? $cache->get('cachetest') to read your cached data without expiring it, and $cache->save('cachetest', '+1 month', $apiresult) only if your api call was successful. You'll always have the last successful call's result in your cache this way, and it will be much cheaper than full fledged pages.

are you sure that this would work? In the test I did above, the value wasn't returned from cache once the cache has expired

echo "4: <pre>".print_r($this->cache->get("cachetest"),true)."</pre>";
Link to comment
Share on other sites

On 11/30/2021 at 8:34 PM, chrizz said:

are you sure that this would work? In the test I did above, the value wasn't returned from cache once the cache has expired

I think we've talking past each other a bit, so let me see if I can word it differently. The cache should never return an expired value, but I can understand that the strange behavior in your tests 5 and 6 suggests differently. If you don't want to lose a value, don't let it expire. Instead, put the logic to overwrite the cached value with a newer one when your API call is successful into your code. And perhaps store the last retrieval time in your cached value, so you have the creation timestamp you mentioned.

Perhaps code says more than a thousand words:

<?php

/**
 * Code for Cron Job
 */

$apiResult = your_api_call();
$cachedResult = json_decode($cache->get('yourcachekey'), true);

if($apiResult)
  // Only if API call was successful
  if(!$cachedResult || $cachedResult['created'] < time() - 300) {
    // Cache entry older than 5 minutes, write new value to cache
    $cache->save('yourcachekey', json_encode(['created' => time(), 'apiResult' => $apiResult], WireCache::expireNever);
  }
}
                 
/**
 * Code for frontend use of API data
 */

$cachedResult = json_decode($cache->get('yourcachekey'), true);
$apiResult = $cachedResult['apiResult'];
                 
if($apiResult['created'] < strtotime('-1 month')) {
  // Deal with a stale cache value that hasn't been updated in over a month here
}

 

  • Thanks 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...