I have this function that tries to read some values from cache. But if value does not exists it should call alternative source API and save new value into the cache. However, server is very overloaded and almost each time when value does not exists more then one requests are created (a lot of API calls) and each of them will store new vale into cache. However, what I want is to be able to call API many times, but only one process/request to be able to store it in cache:
function fetch_cache($key, $alternativeSource) {
$redis = new Redis();
$redis->pconnect(ENV_REDIS_HOST);
$value = $redis->get($key);
if( $value === NULL ) {
$value = file_get_contents($alternativeSource);
// here goes part that I need help with
$semaphore = sem_get(6000, 1); // does this need to be called each time this function is called?
if( $semaphore === FALSE ) {
// This means I have failed to create semaphore?
}
if( sem_aquire($semaphore, true) ) {
// we have aquired semaphore so here
$redis->set($key, $value);
sem_release($semaphore); // releasing lock
}
// This must be call because I have called sem_get()?
sem_remove($semaphore);
}
return $value;
}
Is this proper use of semaphore in PHP5?
fetch_cache
function. Put sem_get()
into an initialization method (such as __construct
).sem_remove()
, but in a cleanup method (such as __destruct
). Or, you might want to keep them even longer - depends on the logic of your application.sem_acquire()
to acquire locks, and sem_release()
to release them.sem_get()
Creates a set of three semaphores.
The underlying C function semget
is not atomic. There is a possibility of race condition when two processes trying to call semget
. Therefore, semget
should be called in some initialization process. The PHP extension overcomes this issue by means of three semaphores:
Semaphore 0 a.k.a. SYSVSEM_SEM
Is initialized to sem_get
's $max_acquire
and decremented as processes acquires it.
The first process that called sem_get
fetches the value of SYSVSEM_USAGE
semaphore (see below). For the first process, it equals to 1
, because the extension sets it to 1
with atomic semop
function right after semget
. And if this is really the first process, the extension assigns SYSVSEM_SEM
semaphore value to $max_acquire
.
Semaphore 1 a.k.a. SYSVSEM_USAGE
The number of processes using the semaphore.
Semaphore 2 a.k.a. SYSVSEM_SETVAL
Plays a role of a lock for internal SETVAL
and GETVAL
operations (see man 2 semctl
). For example, it is set to 1
while the extension sets SYSVSEM_SEM
to $max_acquire
, then is reset back to zero.
Finally, sem_get
wraps a structure (containing the semaphore set ID, key and other information) into a PHP resource and returns it.
So you should call it in some initialization process, when you're only preparing to work with semaphores.
sem_acquire()
This is where the $max_acquire
goes into play.
SYSVSEM_SEM
's value (let's call it semval
) is initially equal to $max_acquire
. semop()
blocks until semval
becomes greater than or equal to 1
. Then 1
is substracted from semval
.
If $max_acquire = 1
, then semval
becomes zero after the first call, and the next calls to sem_acquire()
will block until semval
is restored by sem_release()
call.
Call it when you need to acquire the next "lock" from the available set ($max_acquire
).
sem_release()
Does pretty much the same as sem_acquire()
, except it increments SYSVSEM_SEM
's value.
Call it when you need to no longer need the "lock" acquired previously with sem_acquire()
.
sem_remove()
Immediately removes the semaphore set, awakening allprocesses blocked in semop
on the set (from IPC_RMID
section, SEMCTL(2) man page).
So this is effectively the same as removing a semaphore with ipcrm
command.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With