How to implement non-distributed locks which expire? - concurrency

Meaning, they don't have to be distributed. I'm thinking about using memcached or redis for that. Probably the latter one. What I'm concerned about is "we've got to free some memory, so we'll delete this key/value before it expired" thing. But I'm open to other suggestions as well.

tl;dr Use ready-made solution, suggested by developers.
So, I decided not to use memcached for the purpose. Since it's a caching server. I don't see a way to ensure that it doesn't delete my keys because it's out of memory. With, redis that's not an issue as long as maxmemory-policy = noeviction.
There are 3 links I want to share with you. They are basically 3 ways, that I now know, to solve the issue. As long as you have redis >= 2.6.0 that is.
redis >= 2.6.12
If you've got redis >= 2.6.12, you're lucky and can simply use setnx command with its new options ex and nx:
$redis->set($name, <whatever>, array('nx', 'ex' => $ttl));
But we can't just delete the lock in the end, if we are to allow for critical section taking longer then we expected (>= ttl). Consider the following situation:
C1 acquires the lock
lock expires
C2 acquires the lock
C1 deletes C2's lock
For that not to happen we are going to store current timestamp as a value of the lock. Then, knowing that Lua scripts are atomic (see Atomicity of scripts):
$redis->eval('
if redis.call("get", KEYS[1]) == KEYS[2] then
redis.call("del", KEYS[1])
end
', array($name, $now));
However, is it possible for two clients to have equal now values? For that all the above actions should happen within one second and ttl must be equal to 0.
Resulting code:
function get_redis() {
static $redis;
if ( ! $redis) {
$redis = new Redis;
$redis->connect('127.0.0.1');
}
return $redis;
}
function acquire_lock($name, $ttl) {
if ( ! $ttl)
return FALSE;
$redis = get_redis();
$now = time();
$r = $redis->set($name, $now, array('nx', 'ex' => $ttl));
if ( ! $r)
return FALSE;
$lock = new RedisLock($redis, $name, $now);
register_shutdown_function(function() use ($lock) {
$r = $lock->release();
# if ( ! $r) {
# Here we can log the fact that lock has expired too early
# }
});
return $lock;
}
class RedisLock {
var $redis;
var $name;
var $now;
var $released;
function __construct($redis, $name, $now) {
$this->redis = get_redis();
$this->name = $name;
$this->now = $now;
}
function release() {
if ($this->released)
return TRUE;
$r = $this->redis->eval('
if redis.call("get", KEYS[1]) == KEYS[2] then
redis.call("del", KEYS[1])
return 1
else
return 0
end
', array($this->name, $this->now));
if ($r)
$this->released = TRUE;
return $r;
}
}
$l1 = acquire_lock('l1', 4);
var_dump($l1 ? date('H:i:s', $l1->expires_at) : FALSE);
sleep(2);
$l2 = acquire_lock('l1', 4);
var_dump($l2 ? date('H:i:s', $l2->expires_at) : FALSE); # FALSE
sleep(4);
$l3 = acquire_lock('l1', 4);
var_dump($l3 ? date('H:i:s', $l3->expires_at) : FALSE);
expire
The other solution I found here. You simply make the value expire with expire command:
$redis->eval('
local r = redis.call("setnx", ARGV[1], ARGV[2])
if r == 1 then
redis.call("expire", ARGV[1], ARGV[3])
end
', array($name, $now, $ttl));
So, only acquire_lock function changes:
function acquire_lock($name, $ttl) {
if ( ! $ttl)
return FALSE;
$redis = get_redis();
$now = time();
$r = $redis->eval('
local r = redis.call("setnx", ARGV[1], ARGV[2])
if r == 1 then
redis.call("expire", ARGV[1], ARGV[3])
end
return r
', array($name, $now, $ttl));
if ( ! $r)
return FALSE;
$lock = new RedisLock($redis, $name, $now);
register_shutdown_function(function() use ($lock) {
$r = $lock->release();
# if ( ! $r) {
# Here we can log that lock as expired too early
# }
});
return $lock;
}
getset
And the last one is described again in documentation. Marked with "left for historical reasons" note.
This time we store timestamp of the moment when the lock is to expire. We store it with setnx command. If it succeeds, we've acquired the lock. Otherwise, either someone else's holding the lock, or the lock has expired. Be it the latter, we use getset to set new value and if the old value hasn't changed, we've acquired the lock:
$r = $redis->setnx($name, $expires_at);
if ( ! $r) {
$cur_expires_at = $redis->get($name);
if ($cur_expires_at > time())
return FALSE;
$cur_expires_at_2 = $redis->getset($name, $expires_at);
if ($cur_expires_at_2 != $cur_expires_at)
return FALSE;
}
What makes me uncomfortable here is that we seem to have changed someone else's expires_at value, don't we?
On a side note, you can check which redis is it that you're using this way:
function get_redis_version() {
static $redis_version;
if ( ! $redis_version) {
$redis = get_redis();
$info = $redis->info();
$redis_version = $info['redis_version'];
}
return $redis_version;
}
if (version_compare(get_redis_version(), '2.6.12') >= 0) {
...
}
Some debugging functions:
function redis_var_dump($keys) {
foreach (get_redis()->keys($keys) as $key) {
$ttl = get_redis()->ttl($key);
printf("%s: %s%s%s", $key, get_redis()->get($key),
$ttl >= 0 ? sprintf(" (ttl: %s)", $ttl) : '',
nl());
}
}
function nl() {
return PHP_SAPI == 'cli' ? "\n" : '<br>';
}

Related

Memory leak in concurrent program, or just the way it is?

This is a simplified version of a program which has two channels to carry out some operation
use v6;
my $length = 512;
my Channel $channel-one .= new;
my Channel $to-mix .= new;
my Channel $mixer = $to-mix.Supply.batch( elems => 2).Channel;
my #promises;
for ^4 {
$channel-one.send( 1.rand xx $length );
my $promise = start react whenever $channel-one -> #crew {
my #new-crew = #crew;
#new-crew[#new-crew.elems.rand] = 1;
if ( sum(#new-crew) < $length ) {
say "Emitting in thread ", $*THREAD.id, " Best ", sum(#crew) ;
$to-mix.send( #new-crew );
} else {
say "Found: closing";
$channel-one.close;
say "Closed";
};
}
#promises.push: $promise;
}
my $pairs = start react whenever $mixer -> #pair {
$to-mix.send( #pair.pick ); # To avoid getting it hanged up
my #new-population = crossover-frequencies( #pair[0], #pair[1] );
$channel-one.send( #new-population);
say "Mixing in ", $*THREAD.id;
};
await #promises;
say "Finished";
# Cross over frequencies
sub crossover-frequencies( #frequencies, #frequencies-prime --> Array ) is export {
my #pairs = #frequencies Z #frequencies-prime;
my #new-population = gather {
for #pairs -> #pair {
take #pair.pick;
}
};
return #new-population;
}
It uses one channel for performing some operation (here simplified to setting a random element to one) and another for mixing elements taken in pairs. It works and finishes after a while, but for indicated sizes it starts to grow in memory usage until it reaches almost 1GB before ending.
It might be to-mix channel which is growing, but I don't see it as the source of the leak, since it's getting one element from one block, and one from the other; there might be a few left on the channel before finishing, but not so many as to justify the memory hogging. Any other idea?
An additional problem is that it seems to be always using the same thread for "processing", despite the fact that 4 different threads have been started. I don't know if this is related or not.

Is it possible to terminate a promise's code block from another promise?

I wrote this test program:
await Promise.anyof(
Promise.allof((^5).map: {start { sleep 10; say "done $_" } }),
Promise.in(5).then: { say 'ouch' }
);
sleep 10;
When the second promise times out it prints 'ouch' and the await exits, but the first promise's code block is still running. After five more seconds its five processes end and print 'done':
$ ./test1.p6
ouch
done 0
done 1
done 2
done 3
done 4
I tried to terminate the first promise assigning it to a variable and then calling the .break method from the second promise, but it doesn't work.
Is there a way to kill the first promise and the other five sub-promises it started?
You have to somehow tell the process that it doesn't have to finish.
my $cancel = Cancellation.new;
await Promise.anyof(
Promise.allof(
(^5).map: {
last if $cancel.cancelled;
start {
sleep 10;
say "done $_" unless $cancel.cancelled
}
}
),
Promise.in(5).then: {
$cancel.cancel;
say 'ouch'
}
);
If you want something like Promise.in() that can be cancelled, let's start by looking at the existing code.
method in(Promise:U: $seconds, :$scheduler = $*SCHEDULER) {
my $p := self.new(:$scheduler);
my $vow := $p.vow;
$scheduler.cue({ $vow.keep(True) }, :in($seconds));
$p
}
Note that the result of $scheduler.cue is a Cancellation.
I am just going to wrap a Promise, and a Cancellation in a class for simplicity.
(I don't want to reimplement every method).
class Cancellable-Timer {
has Promise $.Promise;
has $!vow;
has Cancellation $!cancel;
method !SET-SELF ( $!promise, $!vow, $!cancel ){
self
}
method in (::?CLASS:U: $seconds, :$scheduler = $*SCHEDULER) {
my $p := Promise.new(:$scheduler);
my $vow := $p.vow;
my $cancel = $scheduler.cue({ $vow.keep(True) }, :in($seconds));
self.bless!SET-SELF($p,$vow,$cancel);
}
method cancel ( --> Nil ) {
# potential concurrency problem
if $!Promise.status == Planned {
$!cancel.cancel; # cancel the timer
$!vow.break("cancelled"); # break the Promise
}
}
method cancelled () {
# Ignore any concurrency problems by using the Promise
# as the sole source of truth.
$!Promise.status ~~ PromiseStatus::Broken
}
}
my $timer = Cancellable-Timer.in(1);
my $say = $timer.Promise.then: *.say;
Promise.in(0.1).then: {$timer.cancel};
await $say;
Note that the above class is just a rough first draft.
Try with whenever:
$ perl6 -e '
react {
for ^5 -> $num {
whenever start { sleep 10 } {
say "done $num"
}
}
whenever Promise.in: 5 {
say "ouch";
done
}
}
' ouch

Using RegEx to match URL routes

I'm building a PHP Framework for conclusion of my course, and I've stuck on a solution for match some custom routes and standard routes.
My framework's route are similar at routes of Zend Framework 1.
It's match standard routes for
/module/controller/action/param/value/param2/value2/paramn/valuen
The part of URI are optional, and the / route leads to application module, index controller and index action without params and values.
I'm stuck in some custom routes, that I define this way:
/blog/:postname/
/admin/logout/
/blog/posts/:year/:category/
/about/
That routes must match this examples URI requests.
/blog/my-first-post/
/blog/my-first-post/referenced/facebook/
/admin/logout/
/admin/logout/session-id/246753/action
/blog/posts/2013/turism/
/blog/posts/2013/turism/page/2/
But not had to match the standard routes. The custom routes must precede the standard routes.
Some examples of standard routes. Examples:
/
/application/
/application/index/
/application/index/index/
/blog/posts/view/id/3/
/admin/login/
/admin/login/logout (that one are the
/admin/blog/posts/edit/id/3/
/admin/blog/posts/edit/id/3/success/false/
The way I find to do this ellegantily is using RegEx for the matches, but I've trying to learn RegEx for more than one month and don't got it all.
PS: After match the current route, I must to bind the :variable with the related position in the REQUEST_URI.
Thank you for help.
While admittedly tempting, I wouldn't go with regex in this particular case. Even though I usually go that way. A simple loop and match would do, unless your course is setting some restrictions you have to follow.
I put together an example that should get the job done and runs in the console, just to show what i mean.
function get_route($uri){
$routes = [
'blog#show' => 'blog/:postname',
'admin#logout' => 'admin/logout',
'blog#category' => 'blog/posts/:year/:category',
'home#about' => 'about'
];
$params = [];
$uri = preg_replace('/#|\?.+/', '', $uri); // remove hash or query strings
$uri = preg_replace('/(^\/)?(\/$)?/', '', $uri); // trim slashes
$uri = explode('/', $uri);
$action = null;
foreach ($routes as $this_action => $this_route) { // loop through possible routes
$fractions = explode('/', $this_route);
if (sizeof($fractions) !== sizeof($uri)) continue; // did not match length of uri
for ($i=0; $i<sizeof($uri); $i++) { // compare each part of uri to each part of route
if (substr($fractions[$i], 0, 1) !== ':' && $fractions[$i] !== $uri[$i]) break; // not a match and not a param
if ($i === sizeof($uri)-1) { // made it to the last fraction!
$ii = 0;
foreach ($fractions as $fraction) {
if (substr($fraction, 0, 1) == ':') { // it's a param, map it!
$params[substr($fraction,1)] = $uri[$ii];
}
$ii++;
}
return ['action'=>$this_action, 'params'=>$params];
}
}
}
return false;
}
I could reach my needs with this code, a lot of tests has passed.
public function matchCustomRoute($uri)
{
if($uri == '')
{
return null;
}
$customRoutes = $this->getRoutes();
$explodeUri = explode('/', $uri);
$arrayUri = array();
foreach($explodeUri as $uriPart)
{
if($uriPart == '')
{
continue;
}
$arrayUri[] = $uriPart;
}
$countUri = count($arrayUri);
foreach($customRoutes as $key => $value)
{
$explodeRoute = explode('/',$value['route']);
$arrayRoute = array();
foreach($explodeRoute as $routePart)
{
if($routePart == '')
{
continue;
}
$arrayRoute[] = $routePart;
}
$countRoute = count($arrayRoute);
if($countRoute > $countUri)
{
continue;
}
$matches = 0;
for($i = 0 ; $i < $countRoute ; $i++)
{
$match = preg_match('/'.$arrayUri[$i].'/', '/'.$arrayRoute[$i].'/');
if($match == 0)
{
if(substr($arrayRoute[$i], 0, 1) == ':')
{
$value['params'][substr($arrayRoute[$i], 1)] = $arrayUri[$i];
}
else
{
continue;
}
}
$matches++;
}
if($matches == $countRoute)
{
return $value;
}
}
return null;
}
Thank you for help.

Symfony2 Doctrine2 : querybuilder bad query

New to Symfony2 and Doctrine2, i have a function in my entity repository to search entities after form submission. Input is array $get that contain form fields like $get['name'] = 'aname'.
My problem is that when i request with an id, or an id and a name, it's ok by with only a name, all my entities are matched because the query that has been build have no where clause.
Here is my code :
public function search(array $get, $flag = False){
/* Indexed column (used for fast and accurate table cardinality) */
$alias = 'd';
/* DB table to use */
$tableObjectName = 'mysiteMyBundle:DB';
$qb = $this->getEntityManager()
->getRepository($tableObjectName)
->createQueryBuilder($alias)
->select($alias.'.id');
$arr = array();
//Simple array, will grow after problem solved
$numericFields = array(
'id');
$textFields = array(
'name');
while($el = current($get)) {
$field = key($get);
if ( $field == '' or $field == Null or $el == '' or $el == Null ) {
next($get);
}
if ( in_array($field,$numericFields) ){
if ( is_numeric($el) ){
$arr[] = $qb->expr()->eq($alias.".".$field, $el);
}
} else {
if ( in_array($field,$textFields) ) {
$arr[] = $qb->expr()->like($alias.".".$field, $qb->expr()->literal('%'.$el.'%') );
}
}
next($get);
}
if(count($arr) > 0) $qb->andWhere(new Expr\Orx($arr));
else unset($arr);
$query = $qb->getQuery();
if($flag)
return $query;
else
return $query->getResult();
}
The query generated with only a name (ex "myname") input is :
SELECT d0_.id AS id0 FROM DB d0_
It should be:
SELECT d0_.id AS id0 FROM DB d0_ WHERE d0_.name LIKE '%myname%'
What's wrong with my code ?
Thanks !
I don't know if it's related, but do not use "OR" or "AND" operators, because they have a different meaning that the classic "&&" or "||". cf http://php.net/manual/en/language.operators.logical.php
So, first, replace "AND" by "&&", and "OR" by "||".
you should use the setParameter method
$query->where('id = :id')->setParameter('id', $id);

Return number of results from Doctrine query that uses createQuery

$q = $this->_em->createQuery("SELECT s FROM app\models\Quest s
LEFT JOIN s.que c
WHERE s.type = '$sub'
AND c.id = '$id'");
Given a query like the one above, how would I retrieve the number of results?
Alternatively one can look at what Doctrine Paginator class does to a Query object to get a count (this aproach is most probably an overkill though, but it answers your question):
public function count()
{
if ($this->count === null) {
/* #var $countQuery Query */
$countQuery = $this->cloneQuery($this->query);
if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) {
$countQuery->setHint(CountWalker::HINT_DISTINCT, true);
}
if ($this->useOutputWalker($countQuery)) {
$platform = $countQuery->getEntityManager()->getConnection()->getDatabasePlatform(); // law of demeter win
$rsm = new ResultSetMapping();
$rsm->addScalarResult($platform->getSQLResultCasing('dctrn_count'), 'count');
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\CountOutputWalker');
$countQuery->setResultSetMapping($rsm);
} else {
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
}
$countQuery->setFirstResult(null)->setMaxResults(null);
try {
$data = $countQuery->getScalarResult();
$data = array_map('current', $data);
$this->count = array_sum($data);
} catch(NoResultException $e) {
$this->count = 0;
}
}
return $this->count;
}
You can either perform a count query beforehand:
$count = $em->createQuery('SELECT count(s) FROM app\models\Quest s
LEFT JOIN s.que c
WHERE s.type=:type
AND c.id=:id)
->setParameter('type', $sub);
->setParameter('id', $id);
->getSingleScalarResult();
Or you can just execute your query and get the size of the results array:
$quests = $q->getResult();
$count = count($quests);
Use the first method if you need the count so that you can make a decision before actually retrieving the objects.