PECL memcache extension

Don MacAskill don at smugmug.com
Fri Mar 10 21:40:35 UTC 2006


Hi Mikael,

I'm getting a segfault when a server fails and I try to set status. 
Example code:

$memCache = new Memcache();

$memCache->addServer("192.168.0.1", "10000", 1, 1, 1, 15, true, 
'memcacheFailCallback');
$memCache->add("test", "value");

function memcacheFailCallback($Host, $Port) {
	global $memCache;
	$memCache->setServerParams($Host, $Port, 1, -1, false, 
'memcacheFailCallback');
}


Any ideas?  Am I doing something wrong?

Thanks,

Don

Mikael Johansson wrote:
> There's now a snapshot ready that should support your usecase, I would 
> like some feedback as to the suitability of the changes before commiting 
> to the API. Could you have a look at the changes and try them out in 
> your environment?
> 
> In summary; servers may be added in failed mode and by setting 
> retry_interval to -1 you can prevent them from ever being used. Use 
> setServerParams() with retry_interval = 15, status = true to bring a 
> specific server back online. The status of a server can be fetched using 
> getServerStatus(). By providing a failure callback, user code can mark 
> the server as down in the external database until some async job flushes 
> it and flags it as back online. ini_set('memcache.allow_failover', 0); 
> will prevent failover from occuring.
> 
> Code available at
> http://www.synd.info/extensions/memcache/
> 
> Memcache::addServer(host, port = 11211, persistent = true, weight = 1, 
> timeout = 1, retry_interval = 15, status = true, failure_callback = 
> NULL) : bool
>  * Setting retry_interval to -1 disables automatic reconnection of 
> failed hosts
>  * Setting status to false marks the server as failed
>  * failure_callback is run when an operation on a server fails for some 
> reason, implement as myCallback(string host, int port) : void. Both 
> function (eg. 'my_memcache_callback') or OO callbacks (eg. array(new 
> FailureHandler(), 'onfailure')) are supported
> 
> Memcache::setServerParams(host, port = 11211, timeout = 1, 
> retry_interval = 15, status = true, failure_callback = NULL) : bool
>  * Allows for changing parameters at runtime, such as bringing the 
> server online/offline
> 
> Memcache::getServerStatus(host, port = 11211) : int
>  * Returns a non-zero status if the server is online, 0 if the server is 
> marked as failed
> 
> The suggested Memcache::checkState(host, port, key, val) can be 
> implemented in userland by connecting to the host separately and 
> fetching the test key.
> 
> //Mikael
> 
> ----- Original Message ----- From: "Don MacAskill" <don at smugmug.com>
> To: "Mikael Johansson" <mikael at synd.info>
> Cc: "memcached mail list" <memcached at lists.danga.com>; "Antony Dovgal" 
> <antony at zend.com>
> Sent: Sunday, February 05, 2006 8:49 PM
> Subject: Re: PECL memcache extension
> 
> 
>>
>> Wow, this sounds great!  Thanks!
>>
>> My only concern now (pending testing) is with the flushing.  I realize 
>> why you backed out the callback, which makes a lot of sense, but I'm 
>> still struggling to figure out how to flush my server when it comes back.
>>
>> If I use an external service monitor, there's still a race condition - 
>> if the PECL extension detects that the server is back first with it's 
>> retries, it will use it for awhile before the service monitor realizes 
>> it's back and flushes it.
>>
>> Even a second or two of stale data reads could be fatal for us.
>>
>> The immediate and obvious answer would be to do external state 
>> tracking and somehow let the PECL extension know what's going on.   It 
>> may only be immediate and obvious to me since we're doing it already, 
>> but I'm certainly open to other suggestions.  Here's what I'd need:
>>
>> - Access to view and change the state of the server pool so we can 
>> externally set a server back to "up" (or "down") rather than 
>> automatically checking every retry seconds.  Maybe:
>>
>> - Memcache::addServer(host, port, persistent, weight, timeout, 
>> retry_interval, state)  [I'd set retry_interval to 0 to disable 
>> retries, in this case]
>>
>> - Memcache::setState(host, port, state)
>>
>> - Memcache::getState(host, port)
>>
>> - Memcache::checkState(host, port, key, val)  [theoretically, this 
>> would connect to the server, set the key and get the key]
>>
>> - A callback for a server failure would be great, but could maybe be 
>> done by checking the return of the various functions instead.
>>
>> With my current implementation, I already do this for both up/down 
>> state changes, and a server isn't marked as "up" again until a 
>> successful flush_all() has happened.  Since it's externally tracked in 
>> a database, each Apache child is fed the real state each time a script 
>> is run or the state needs updating.
>>
>> If this is getting too messy, or not useful enough to others, I can 
>> instead just not use the extension's pooling mechanism and instead 
>> wrap my own stuff around Memcache::connect() and/or 
>> Memcache::pconnect(). But I can't imagine I'm the only one who's 
>> paranoid about data integrity issues. :)
>>
>> I'd be happy to release our PHP code for external tracking in either 
>> event, if anyone's interested.  It's not really very difficult, but it 
>> might help those who haven't done it in a decent-sized production 
>> environment yet.
>>
>> And, again, if there's an easier way to do this and preserve data 
>> integrity, I'd love to hear it.  :)
>>
>> Don
>>
>>
>> Mikael Johansson wrote:
>>> There's now a "memcache.allow_failover" ini directive in CVS which 
>>> you can use to prevent failover and make the client code return false 
>>> immediatly, defaults to true.
>>>
>>> Failover may occur at any stage in any of the methods that talk to 
>>> the server (set, get, delete, increment, ..) and as long as there are 
>>> other servers available the client code won't notice (other than a 
>>> E_NOTICE being triggered.) Causes that would trigger a failover might 
>>> be socket connect failures, read/write errors or Memcached server 
>>> errors (other than out-of-memory.)
>>>
>>> Each persistent connection struct has its own retry timeout which 
>>> gets set when some failure occur, after it expires the connection 
>>> will be retried and possibly marked failed for another retry_interval 
>>> seconds. Since each Apache child might have a connection struct of 
>>> their own each child would attempt to reconnect every interval 
>>> seconds when serving a request.
>>>
>>> The changes needed to allow a user to specifiy a callback to be run 
>>> on failback was minor; but since each child on every host might run 
>>> it when they reconnect a failed connection struct the results were 
>>> somewhat unreliable. There's also the very real possibility that the 
>>> child creates a completly new struct even though persistent connect 
>>> was specified (for example when the connection pool is exhausted) and 
>>> thus doesn't run the callback at all. In any case; I backed out those 
>>> changes and would recommend using a real service monitor (such as 
>>> "mon") instead, to flush failed servers when they come back online.
>>>
>>> //Mikael
>>>
>>> ----- Original Message ----- From: "Don MacAskill" <don at smugmug.com>
>>> To: "Mikael Johansson" <mikael at synd.info>
>>> Cc: "memcached mail list" <memcached at lists.danga.com>; "Antony 
>>> Dovgal" <antony at zend.com>
>>> Sent: Saturday, February 04, 2006 8:07 PM
>>> Subject: Re: PECL memcache extension
>>>
>>>
>>>>
>>>> Sounds like we're on the same page as far as understanding the 
>>>> problem. And I'd definitely like a flag to be able to automatically 
>>>> flush_all() the server which just re-joined the cluster (or even no 
>>>> option, though I might be missing a scenario where you wouldn't want 
>>>> this).
>>>>
>>>> But rather than having to do a flush_all() on every member of the 
>>>> cluster when #2 happens, I'd much rather see something like a 
>>>> php.ini parameter that lets me tell memcache not to rebalance the 
>>>> cluster when one fails:
>>>>
>>>> memcache.rebalance = false
>>>>
>>>> I have enough memcache servers that a failure of one of them doesn't 
>>>> dramatically affect performance.  But having stale data, or having 
>>>> to flush_all() every server would be a Big Deal.
>>>>
>>>> I suppose I could just write a wrapper for memcache in PHP that 
>>>> handles failure scenarios and not use memcache:addServer() at all if 
>>>> this doesn't sound feasible.
>>>>
>>>> Also, I'd love to get a little insight into exactly what happens 
>>>> when a failure occurs.  What causes memcache to consider a server to 
>>>> be a failure?  Is it only if a socket connect fails?  Or does a 
>>>> failure of some of the commands (delete, for example) also cause a 
>>>> server to be marked as failed?
>>>>
>>>>
>>>> And finally, I see that there's a retry timer.  Is that global for 
>>>> the entire Apache process?  Or just a thread/fork?  If I set it to 
>>>> be 60 seconds or something, does that mean there will only be a 
>>>> single retry every 60 seconds for the entire physical server running 
>>>> Apache?  Or are all the threads/forks going to retry every 60 
>>>> seconds?  I want to make sure we're not retrying so frequently that 
>>>> we're causing it to flap.
>>>>
>>>> A little bit better documentation in this regard would help, but 
>>>> perhaps providing some mechanism where the application can mark a 
>>>> server in the cluster as failed at will would be nice, too.  And is 
>>>> there any way to notify (via php's error log, or firing a function 
>>>> or something) me when a server fails?
>>>>
>>>> Thanks,
>>>>
>>>> Don 


More information about the memcached mailing list