I'm using zend_shm_cache functions as a fast storage of variables. My problem is that the cache doesn't seem to get cleared after TTL is over. Example:
zend_shm_cache_store( $key = 'test3', 'value', 2 );
foreach( range(1,5) as $timer ){
sleep( 1 );
echo $timer.' - ' .zend_shm_cache_fetch( $key ).'<br/>';
}
Returns:
1 - value
2 -
3 - value
4 - value
5 - value
I would expect the cache to be empty after second second. Can you explain what's happening or propose a solution?
I got the answer in the documentation of APCU:
After the ttl has passed, the stored variable will be expunged from
the cache (on the next request).
As my code runs in only one request the cache is never deleted even when its time has expired.
Related
We have a system where you can enter your email address. Now, we want that if you request 3 times in a row (without success) that a special value is reported back.
Everything is working, except how to store the count of tries. We're working with Api-Platform.
This means that a Symfony Session should not do the trick. It will probably restart and create a new session after every request.
So, how can we store a count? Here is an example of what we try to achieve with the usage of Symfony Sessions. Any ideas how to store the count? Sessions weren't possible (Maybe wrong implementation) and a database table seems to be a bit excessive.
if(!$session->has('c_tries')) {
$captchaTries = $session->set('c_tries', 0);
}
$captchaTries = $session->get('c_tries');
$new = $captchaTries + 1;
$session->set('c_tries', $new);
if($captchaTries > 2 ) {
....
It is best to solve this on the client side. You can use cookies.
if (!isset($_COOKIE('trakcer']))) {
setcookie ("TryCount", $captchaTries, time() + 3600); /*expires in 1 hour*/
}
I just designed an admin console for a social networking website. My boss now wants me to cache the results of several MySQL queries that build these results (for 24 hours). The site uses Memcached (with Wordpress W3 total cache) and XCache. I wanted to know what's the best way to do this.
Here is an example of one such query and how I am getting the results (basically I am returning aggregate stats on users, which means my results are fairly simple, eg:
//users who registered in last 365 days
$users_reg_365 = "select ID
from wp_users
where user_registered > NOW() - interval 365 day";
then use the wpdb query class to get the results:
$users_reg_365 = $wpdb->get_results($users_reg_365);
then display the result in the dashboard:
<li><?php echo "Total users who registered within last 365 days: <span class='sqlresult'>" . sizeof($users_reg_365) . "</span>"; ?></li>
My understanding of Memcached/XCache is that it basically stores strings, so would it make sense to just cache sizeof($users_reg_365)?
The last wrinkle is that our Wordpress site uses W3 total cache, which leverages Memcached, and the boss asked me not to use Memcached but XCache instead, but I find the docs a bit confusing. What's the best way to solve this problem? Can SQL itself be told to 'remember' certain queries like this, or is memory caching the way to go?
Thanks!
You can find more about the differences of both here:
Difference between Memcache, APC, XCache and other alternatives I've not heard of
An example how you could
<?php
$m = new Memcached();
$m->addServer('localhost', 11211);
// cache 24hrs
$cache_expire = 86400;
// users is your key
$users_reg_365 = $m->get('users_reg_365');
if (empty($users_reg_365)) {
$users_reg_365 = "select ID from wp_users where user_registered > NOW() - interval 365 day";
$m->set('users_reg_365', $cache_expire);
}
If you need to exactly refresh the cache at middle night change the value of $cache_expire.
You can refer to the full reference of memcached at
http://www.php.net/manual/en/memcached.get.php
Hm. I'm not sure how your caching is configured inside of WordPress. If you have WordPress's object cache set up to use Memcache(d)/XCache for persistent caching, you could do something like this:
$key = 'custom-key-to-look-up-later';
$data = sizeof( $users_reg_365 );
$group = 'group-id';
$expire = 60 * 60 * 24 // time in seconds before expiring the cache.
wp_cache_set( $key, $data, $group, $expire );
Later you can look up that value like this:
$data = wp_cache_get( $key, $group );
if( ! is_wp_error( $data ) )
// the data is good. do something with it.
You can find the docs on these functions here.
Before you begin, though, make sure WordPress is set up to work with Memcache(d) or XCache. :)
IF you want to store the results of any array just serialize/jsonencode it and store as that..
I see 5 output each time when I run this code:
<?php
$v = 5;
apc_store('vwxyz',$v,3);
$before = apc_fetch('vwxyz');
sleep(5);
$after = apc_fetch('vwxyz'); //should be false
echo $before;
echo "<br>";
echo $after;
$later = apc_fetch('vwxyz'); //OK this should definitely be false
echo "<br>";
echo $later;
Shouldn't the cached entry be cleared from the cache and return false to apc_fetch()? The user_ttl setting is 2 for APC. I'm still trying to figure out what user_ttl does (the documentation is quite cryptic).
From the manual:
Time To Live; store var in the cache for ttl seconds. After the ttl
has passed, the stored variable will be expunged from the cache (on
the next request). If no ttl is supplied (or if the ttl is 0), the
value will persist until it is removed from the cache manually, or
otherwise fails to exist in the cache (clear, restart, etc.).
So it says that the item gets removed from the cache after the TTL on the next request. So the item isn't removed from cache until your next request which is why you keep getting 5.
I have a fairly expensive server call that I need to cache for 30 seconds. It seems however that I can not get the cache to expire.
In the code below, after the first time it caches, it will never get past $return->cache_data, even after the time() + 30 seconds.
Note, I can even print $cache->expire and it is definitely set to a time past 30 seconds ago and never updates.
I've manually cleared cache many times to confirm I get the same results.
Does anything look wrong with this?
function mymodule_get_something($id) {
// set the unique cache id
$cid = 'id-'. $id;
// return data if there's an un-expired cache entry
// *** $cache ALWAYS gets populated with my expired data
if ($cache = cache_get($cid, 'cache_mymodule')) {
return $cache->data;
}
// set my super expensive data call here
$something = array('Double Decker Taco', 'Burrito Supreme');
// set the cache to expire in 30 seconds
cache_set($cid, $something, 'cache_mymodule', time() + 30);
// return my data
return $something;
}
There's nothing wrong with your code as such, I think the problem is in how cache_set behaves. From the docs page, passing a UNIX timestamp:
Indicates that the item should be kept at least until the given time, after which it behaves like CACHE_TEMPORARY.
CACHE_TEMPORARY behaves like this:
Indicates that the item should be removed at the next general cache wipe.
My best guess is that because you're not implicitly forcing that general cache wipe (using cache_clear_all()) the cache object will persist.
I think a simple way around it would just be to manually test the expiry time after your cache check, and let it fall through to re-setting that cache object if it has expired:
if ($cache = cache_get($cid, 'cache_mymodule')) {
if ($cache->expire > REQUEST_TIME) {
return $cache->data;
}
}
The setup: High traffic website and a list of image URLs that we want to display. We have one image spot, and each item in the set of image URLs has a target display percentage for the day. Example:
Image1 - 10%
Image2 - 30%
Image3 - 60%
Because the traffic amount can vary from day to day, I'm doing the percentages within blocks of 1000. The images also need to be picked randomly, but still fit the distribution accurately.
Question: I've implemented POC code for doing this in memcache, but I'm uncomfortable with the way data is stored (multiple hash keys mapped by a "master record" with meta data). This also needs to be able to fall back to a database if the memcache servers go down. I'm also concerned about concurrency issues for the master record.
Is there a simpler way to accomplish this? Perhaps a fast mysql query or a better way to bring memcache into this?
Thanks
You could do what you said, pregenerate a block of 1000 values pointing at the images you'll return:
$distribution = "011022201111202102100120 ..." # exactly evenly distributed
Then store that block in MySQL and memcache, and use another key (in both MySQL and memcache) to hold the current index value for the above string. Whenever the image script is hit increment the value in memcache. If memcache goes down, go to MySQL instead (UPDATE, then SELECT; there may be a better way to do this part).
To keep memcache and MySQL in sync you could have a cron job copy the current index value from memcache to MySQL. You'll lose some accuracy but that may not be critical in this situation.
You could store multiple distributions in both MySQL and memcache and have another key that points to the currently active distribution. That way you can pregenerate future image blocks. When the index exceeds the distribution the script would increment the key and go to the next one.
Roughly:
function FetchImageFname( )
{
$images = array( 0 => 'image1.jpg', 1 => 'image2.jpg', 2 => 'image3.jpg' );
$distribution = FetchDistribution( );
$currentindex = FetchCurrentIndex( );
$x = 0;
while( $distribution[$currentindex] == '' && $x < 10 );
{
IncrementCurrentDistribKey( );
$distribution = FetchDistribution( );
$currentindex = FetchCurrentIndex( );
$x++;
}
if( $distribution[$currentindex] == '' )
{
// XXX Tried and failed. Send error to central logs.
return( $images[0] );
}
return( $distribution[$currentindex] );
}
function FetchDistribution( )
{
$current_distib_key = FetchCurrentDistribKey( );
$distribution = FetchFromMemcache( $current_distrib_key );
if( !$distribution )
$distribution = FetchFromMySQL( $current_distrib_key );
return $distribution;
}
function FetchCurrentIndex( )
{
$current_index = MemcacheIncrement( 'foo' );
if( $current_index === false )
$current_index = MySQLIncrement( 'foo' );
return $current_index;
}
.. etc. The function names kind of stink, but I think you'll get the idea. When the memcache server is back up again, you can copy the data from MySQL back to memcache and it is instantly reactivated.
A hit to the database is most likely going to take longer so I would stick with memcache. You are going to have more issues with concurrency using MySQL than memcache. memcache is better equipped to handle a lot of requests and if the servers go down, this is going to be the least of your worries on a high traffic website.
Maybe a MySQL expert can pipe in here with a good query structure if you give us more specifics.