strange memcache behaviour on local apache server - php

I'm currently having troubles about local deployment of my web service api. I'm using memcached with PHP Memcache extension. Here's the following behaviour;
I have a login function that firstly checks whether the user information is on memory with following code;
$cache = Memcacher::get_instance()->load( 'user.' . $email);
if the cache exists, then it will return the variable $cache to be true.
if the cache doesn't exist, then I'm saving it to the memory with following code;
$cache = Memcacher::get_instance()->save( 'user.' . $login['value']['email'], $login);
the parameter $login is an associative array that holds user info.
After saving it to the memory, I try to re-login. However, it doesn't hold the info before I save to memcached server 3-4 times.
After 3-4 login, I get the login information from cache successfully.
Can anyone explain me that strange behaviour ? Is there any memcached configuration variable to prevent this ?
NOTE : I'm flushing the memory with restarting memcached with sudo service memcached restart
on Ubuntu
NOTE : Memcacher is a custom module that uses PHP Memcache extension's set() and get() function
NOTE : Here's the source code of Memcacher

You are concatenating an array with a string. At points, it might not be true that $login is an array, therefore you will start receiving the information you have because the concatenation will work. But if it is an array, you will not be able to save the key in memcache. You will receive a notice, which i guess you ignore.

Related

Slow PHP Session

A basic page page with just session_start(); loads just fine, but once I've set something, for example $_SESSION['pet']="dog";, the page load time is around 5 seconds.
I'm using AWS's memcached server and the connection time to it from the EC2 instance is really fast. I'm not sure where the slow down is coming from.
The session.save_handler is set to memcached and session.save_path is set to xxx.cfg.use1.cache.amazonaws.com:11211
phpinfo also displays Registered save handlers as files user memcache memcached
EDIT :
I uploaded test files to demonstrate the issue. The first file is simply session_start(); print_r($_SESSION); (http://rr915webapi.us-east-1.elasticbeanstalk.com/session.php). The second file is session_start();$_SESSION['pet']="dog";$_SESSION['name']="bob";(http://rr915webapi.us-east-1.elasticbeanstalk.com/session-set.php). After you load the second file, you can see the first takes a while longer to load than initially did.
By setting the following in the PHP ini file, the response time was reduced down to milliseconds.
session.lazy_write = 0
memcached.sess_locking = Off
Some possibilities :
if your PHP server running your PHP code and your memcached server / cfg.use1.cache.amazonaws.com are hosted on different regions, it can explain all this time...
there seems to be a a bug in libmemcached 1.0.16...if you update to 1.0.18, will fix the problem, see https://github.com/iuscommunity/wishlist/issues/143 comments and https://bugs.launchpad.net/libmemcached/+bug/1589344

APCIterator empty

I have enabled APC in php.ini, and looking at phpinfo(), it all seems ok.
I also have apc.enable_cli=1 directive in php.ini.
I've stored several keys that start with raw_ prefix and they get stored (apc_store() returns true, apc_fetch() returns the value for that key.
All ok so far.
So I wanted to use APCIterator to fetch all keys that start with raw_, using this statement:
$iterator = new APCIterator("user", '/^raw_\.*/');
A key looks like this:
raw_2014-04-17 12:19:00_0.68206200 1397726355534f9c93a68649.18047787158329
It seems that if I print_r($iterator) or iterate it using foreach(), it is empty.
Any ideas what can be wrong?
Thank you!
Actually the problem was this: How can I get PHP to use the same APC cache when invoked on the CLI and the web?
I used CLI mode to fetch from APC and used store() via HTTP request. APC does not share data between Apache and CLI

Zend Session DB Handler and Serialized Data

We are using Zend Framework 1.7 on a project (old, I know) and just made a change to move the session data from files to the database. This appears to be working fine in both environments we deployed it in.
However, there is one big difference: in environment A (PHP 5.2), the session data is being stored in a plaintext serialized format, e.g.:
key|[php serialized array]
In environment B (PHP 5.3), the data appears to be getting base64 encoded somehow:
IpgJ4fbSZ0v2qi4RmGcgQ9tP7YEUyN1R9-7oroGl4071fnlk_UVkkrkUcpdHdw_UsRYy-6NpL61gTuL2Htcmv3HU5UM3ClwSDndY40kyimDPs3SdS7gNHwhwdpailLOfrIxqV48hZDhNHKlIpSX2QZm0jOHjRhZc2kjXnMgqioLqJiDdgyUCRnqKcZ_ZtBISq8BAZARW61P5Ls_ZSO506ltuNqIJIJGkV2R7qvDKLLCtxUQUwd7P8IlJiC7iq_Q4GIn3gMr0KwAHLP6adzxSusVk5begrx9lBk9Dxp8KkJ8Gx8rfKJfvVBIJKZgmsFsWq41dV0J4y1Lgihvx9nU73g..
Both environments appear to be working otherwise, but I need to make sure we understand why this is happening, and I can't seem to find any documentation or similar situation anywhere. My assumption is that the difference in PHP version and/or configuration in php.ini is influencing it, but I am at a loss as to how.
I had the same problem today on a clustered server, in one I had php 5.2 and in the other php 5.3.
My initial guess was also a version problem, but I found so far that suhosin was the problem since was encrypting the session.
You can decrypt the session, just in the machine it was encrypted; to do you you should change the following in your php.ini:
suhosin.session.encrypt = on
suhosin.cookie.encrypt = on
session.save_handler = files
session.save_path = /tmp
and later write a script like:
$data = $argv[1];
$sessid = 'recovering';
file_put_contents("/tmp/sess_{$sessid}", $data);
session_id($sessid);
session_start();
print_r($_SESSION);
Which will only copy the encrypted string into a sess file and print contents (you can serialize to match the normal session behavior)
Of course if you want later that php don't encrypt through suhosin, you need to set .encrypt = off

session_start hangs

since a few hours our server hangs every time you do a session_start.
For testing purposes i created a script which looks like this:
<?php
session_start();
?>
Calling it from the console hangs and it can't even be stopped with ctrl-c, only kill -9 works. The same for calling it via Apache. /var/lib/php/session/ stays empty but permissions are absolutely fine, www can write and also has read permissions for all parent folders.
According to the admins there were no changes made on the server and there is no special code registered for sessions. The Server is CentOS 4 or 5 and yesterday everything was working perfectly. We rebooted the server and updated PHP, but nothing changed.
I've ran out of ideas, any suggestions?
UPDATE
We solved this problem by moving the project to another server, so while the problem still exists on one server there is no immediate need for a solution anymore.
I will keep the question open in case someone has an idea for others having a similar problem in the future, though.
There are many reasons for that, here are a few of them:
A. The session file could be opened exclusively.
When the file lock is not released properly for whatever reason, it is causing session_start() to hang infinitely on any future script executions.
Workaround: use session_set_save_handler() and make sure the write function uses fopen($file, 'w') instead of fopen($file, 'x')
B. Never use the following in your php.ini file (entropie file to "/dev/random"), this will cause your session_start() to hang:
<?php
ini_set("session.entropy_file", "/dev/random");
ini_set("session.entropy_length", "512");
?>
C.
session_start() needs a directory to write to.
You can get Apache plus PHP running in a normal user account. Apache will then of course have to listen to an other port than 80 (for instance, 8080).
Be sure to do the following things:
- create a temporary directory PREFIX/tmp
- put php.ini in PREFIX/lib
- edit php.ini and set session.save_path to the directory you just created
Otherwise, your scripts will seem to 'hang' on session_start().
If this helps:
In my scenario, session_start() was hanging at the same time I was using the XDebug debugger within PHPStorm, the IDE, on Windows. I found that there was a clear cause: Whenever I killed the debug session from within PHPStorm, the next time I tried to run a debug session, session_start() would hang.
The solution, if this is your scenario, is to make sure to restart Apache every time you kill an XDebug session within your IDE.
I had a weird issue with this myself.
I am using CentOS 5.5x64, PHP 5.2.10-1. A clean ANSI file in the root with nothing other than session_start() was hanging. The session was being written to disk and no errors were being thrown. It just hung.
I tried everything suggested by Thariama, and checked PHP compile settings etc.
My Fix:
yum reinstall php; /etc/init.d/httpd restart
Hope this helps someone.
To everyone complaining about the 30 seconds of downtime being unacceptable, this was an inexplicable issue on a brand new, clean OS install, NOT a running production machine. This solution should NOT be used in a production environment.
Ok I face the same problem on 2 PC, 1 is MAC mini XAMPP, 1 is Windows 10 Xampp.
Both is php spent infinity to run session_start(). Both PHP version is 7.x.x
I found that session files is lock to read and write. So that I added code to make PHP read session files and immediately unlock when done with
<?php
session_start([
'read_and_close' => true,
]);
?>
or
<?php
//For PHP 5.x
session_start();
session_write_close();
?>
After this PHP unlock session file => Problems solve
The problem: -
Iv experienced (and fixed) the problem where file based sessions hang the request, and database based sessions get out of sync by storing out of date session data (like storing each session save in the wrong order).
This is caused by any subsequent request that loads a session (simultaneous requests), like ajax, video embed where the video file is delivered via php script, dynamic resource file (like script or css) delivered via php script, etc.
In file based sessions file locking prevents session writing thus causing a deadlock between the simultaneous request threads.
In database based session the last request thread to complete becomes the most recent save, so for example a video delivery script will complete long after the page request and overwrite the since updated session with old session data.
The fix: -
If your ajax or resource delivery script doesnt need to use sessions then easiest to just remove session usage from it.
Otherwise you'd best make yourself a coffee and do the following: -
Write or employ a session handler (if not already doing so) as per http://www.php.net//manual/en/class.sessionhandler.php (many other examples available via google search).
In your session handler function write() prepend the code ...
// processes may declare their session as read only ...
if(!empty($_SESSION['no_session_write'])) {
unset($_SESSION['no_session_write']);
return true;
}
In your ajax or resource delivery php script add the code (after the session is started) ...
$_SESSION['no_session_write'] = true;
I realise this seems like a lot of stuffing around for what should be a tiny fix, but unfortunately if you need to have simultaneous requests each loading a session then it is required.
NOTE if your ajax or resource delivery script does actually need to write/save data, then you need to do it somewhere other than in the session, like database.
Just put session_write_close(); befor Session_start();
as below:
<?php
session_write_close();
session_start();
.....
?>
I don't know why, but changing this value in /etc/php/7.4/apache2/php.ini worked for me:
;session.save_path = "/var/lib/php/sessions"
session.save_path = "/tmp"
To throw another answer into the mix for those going bananas, I had a session_start() dying only in particular cases and scripts. The reason my session was dying was ultimately because I was storing a lot of data in them after a particularly intensive script, and ultimately the call to session_start() was exhausting the 'memory_limit' setting in php.ini.
After increasing 'memory_limit', those session_start() calls no longer killed my script.
For me, the problem seemed to originate from SeLinux. The needed command was chcon -R -t httpd_sys_content_t [www directory] to give access to the right directory.
See https://askubuntu.com/questions/451922/apache-access-denied-because-search-permissions-are-missing
If you use pgAdmin 4 this can happen as well.
If you have File > Preferences > SQL Editor > Options > "Auto Commit" disabled, and you just ran a query using the query tool but didn't manually commit, then session_start() will freeze.
Enable auto commit, or manually commit, or just close pgAdmin, and it will no longer freeze.
In my case it seems like it was the NFS Share that was locking the session , after restarting the NFS server and only enabled 1 node of web clients the sessions worked normally .
Yet another few cents that might help someone. In my case I was storing in $_SESSION complex data with several different class objects in them and session_start() couldn't handle the whole unserialization as not every class was loaded on session_start. The solution is my case was to serialize/jsonify data before saving it into the $_SESSION and reversing the process after I got the data out of session.

How to clear APC cache entries?

I need to clear all APC cache entries when I deploy a new version of the site.
APC.php has a button for clearing all opcode caches, but I don't see buttons for clearing all User Entries, or all System Entries, or all Per-Directory Entries.
Is it possible to clear all cache entries via the command-line, or some other way?
You can use the PHP function apc_clear_cache.
Calling apc_clear_cache() will clear the system cache and calling apc_clear_cache('user') will clear the user cache.
I don't believe any of these answers actually work for clearing the APC cache from the command line. As Frank Farmer commented above, the CLI runs in a process separate from Apache.
My solution for clearing from the command line was to write a script that copies an APC clearing script to the web directory and accesses it and then deletes it. The script is restricted to being accessed from the localhost.
apc_clear.php
This is the file that the script copies to the web directory, accesses, and deletes.
<?php
if (in_array(#$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1')))
{
apc_clear_cache();
apc_clear_cache('user');
apc_clear_cache('opcode');
echo json_encode(array('success' => true));
}
else
{
die('SUPER TOP SECRET');
}
Cache clearing script
This script copies apc_clear.php to the web directory, accesses it, then deletes it. This is based off of a Symfony task. In the Symfony version, calls are made to the Symfony form of copy and unlink, which handle errors. You may want to add checks that they succeed.
copy($apcPaths['data'], $apcPaths['web']); //'data' is a non web accessable directory
$url = 'http://localhost/apc_clear.php'; //use domain name as necessary
$result = json_decode(file_get_contents($url));
if (isset($result['success']) && $result['success'])
{
//handle success
}
else
{
//handle failure
}
unlink($apcPaths['web']);
I know it's not for everyone but: why not to do a graceful Apache restart?
For e.g. in case of Centos/RedHat Linux:
sudo service httpd graceful
Ubuntu:
sudo service apache2 graceful
This is not stated in the documentation, but to clear the opcode cache you must do:
apc_clear_cache('opcode');
EDIT: This seems to only apply to some older versions of APC..
No matter what version you are using you can't clear mod_php or fastcgi APC cache from a php cli script since the cli script will run from a different process as mod_php or fastcgi. You must call apc_clear_cache() from within the process (or child process) which you want to clear the cache for. Using curl to run a simple php script is one such approach.
If you are running on an NGINX / PHP-FPM stack, your best bet is to probably just reload php-fpm
service php-fpm reload (or whatever your reload command may be on your system)
If you want to clear apc cache in command : (use sudo if you need it)
APCu
php -r "apcu_clear_cache();"
APC
php -r "apc_clear_cache(); apc_clear_cache('user'); apc_clear_cache('opcode');"
Another possibility for command-line usage, not yet mentioned, is to use curl.
This doesn't solve your problem for all cache entries if you're using the stock apc.php script, but it could call an adapted script or another one you've put in place.
This clears the opcode cache:
curl --user apc:$PASSWORD "http://www.example.com/apc.php?CC=1&OB=1&`date +%s`"
Change the OB parameter to 3 to clear the user cache:
curl --user apc:$PASSWORD "http://www.example.com/apc.php?CC=1&OB=3&`date +%s`"
Put both lines in a script and call it with $PASSWORD in your env.
apc_clear_cache() only works on the same php SAPI that you want you cache cleared. If you have PHP-FPM and want to clear apc cache, you have do do it through one of php scripts, NOT the command line, because the two caches are separated.
I have written CacheTool, a command line tool that solves exactly this problem and with one command you can clear your PHP-FPM APC cache from the commandline (it connects to php-fpm for you, and executes apc functions)
It also works for opcache.
See how it works here: http://gordalina.github.io/cachetool/
As defined in APC Document:
To clear the cache run:
php -r 'function_exists("apc_clear_cache") ? apc_clear_cache() : null;'
If you want to monitor the results via json, you can use this kind of script:
<?php
$result1 = apc_clear_cache();
$result2 = apc_clear_cache('user');
$result3 = apc_clear_cache('opcode');
$infos = apc_cache_info();
$infos['apc_clear_cache'] = $result1;
$infos["apc_clear_cache('user')"] = $result2;
$infos["apc_clear_cache('opcode')"] = $result3;
$infos["success"] = $result1 && $result2 && $result3;
header('Content-type: application/json');
echo json_encode($infos);
As mentioned in other answers, this script will have to be called via http or curl and you will have to be secured if it is exposed in the web root of your application. (by ip, token...)
if you run fpm under ubuntu, need to run the code below (checked on 12 and 14)
service php5-fpm reload
The stable of APC is having option to clear a cache in its interface itself. To clear those entries you must login to apc interface.
APC is having option to set username and password in apc.php file.
apc.ini
apc.stat = "1" will force APC to stat (check) the script on each request to determine if it has been modified. If it has been modified it will recompile and cache the new version.
If this setting is off, APC will not check, which usually means that to force APC to recheck files, the web server will have to be restarted or the cache will have to be manually cleared. Note that FastCGI web server configurations may not clear the cache on restart. On a production server where the script files rarely change, a significant performance boost can be achieved by disabled stats.
New APC Admin interface have options to add/clear user cache and opcode cache, One interesting functionality is to add/refresh/delete directory's from opCode Cache
APC Admin Documentation
A good solution for me was to simply not using the outdated user cache any more after deploy.
If you add prefix to each of you keys you can change the prefix on changing the data structure of cache entries. This will help you to get the following behavior on deploy:
Don't use outdated cache entries after deploy of only updated structures
Don't clean the whole cache on deploy to not slow down your page
Some old cached entries can be reused after reverting your deploy (If the entries wasn't automatically removed already)
APC will remove old cache entries after expire OR on missing cache space
This is possible for user cache only.
Create APC.php file
foreach(array('user','opcode','') as $v ){
apc_clear_cache($v);
}
Run it from your browser.
My work-around for Symfony build having loot of instances at the same server:
Step 1.
Create trigger or something to set a file flag (eg. Symfony command) then create marker file ..
file_put_contents('clearAPCU','yes sir i can buggy')
Step 2.
On index file at start add clearing code and remove marker file.
if(file_exists('clearAPCU')){
apcu_clear_cache();
unlink('clearAPCU');
}
Step 2.
Run app.
TL;DR: delete cache files at /storage/framework/cache/data/
I enabled APC but it wasn't installed (and also couldn't be installed), so it threw Call to undefined function Illuminate\Cache\apc_store().
"Ok, I'd just disable it and it should work".
Well, nope. Then I got stuck with Call to undefined function Illuminate\Cache\apc_delete()
We had a problem with APC and symlinks to symlinks to files -- it seems to ignore changes in files itself. Somehow performing touch on the file itself helped. I can not tell what's the difference between modifing a file and touching it, but somehow it was necessary...

Categories