PHP MySQL get_lock

В сценарии я пытаюсь проверить, работает ли тот же сценарий с использованием MySQL GET_LOCK, Проблема в том, что когда скрипт пытается получить блокировку, которая не является бесплатной, он блокируется навсегда, независимо от того, какой параметр я предоставляю.

<?php

class Controller_Refresher extends Controller {
    public function action_run($key) {
        echo date('H:i:s'."\n", time());
        $this->die_if_running();
        $this->run();
        echo date('H:i:s'."\n", time());
    }

    private function die_if_running() {
        $result = DB::query(Database::SELECT, "SELECT IS_FREE_LOCK('refresher_running') AS free")->execute();
        if (! intval($result[0]['free'])) die('Running already');
        $result = DB::query(Database::SELECT, "SELECT GET_LOCK('refresher_running', 1)")->execute();
    }

    private function run() {
        echo "Starting\n";
        ob_flush();
        sleep(10);

        DB::query(Database::SELECT, "SELECT RELEASE_LOCK('refresher_running')")->execute();
    }
}

Когда я запускаю это в 2 вкладках в браузере, я получаю, например:

-tab 1-
20:48:16
Starting
20:48:26
-tab 2-
20:48:27
Starting
20:48:37

Пока что я хочу сделать, это сделать вторую вкладку die('Running already');,

2 ответа

Решение

Будьте внимательны - эта проблема может быть вызвана блокировкой php файла сессии:

/questions/25123532/pochemu-moe-veb-prilozhenie-wamp-rabotaet-medlenno-esli-ya-zaprashivayu-ego-neskolko-raz-s-pomoschyu-odnogo-brauzera-seansa/25123556#25123556

Поэтому вы должны вызывать session_write_close() перед любым кодом, который должен выполняться одновременно. Я обнаружил это после попытки этого класса Mutex:

http://code.google.com/p/mutex-for-php/

Класс работал отлично, но мои php-скрипты все еще работали один за другим!

Кроме того, вам не нужно IS_FREE_LOCK(). Просто вызовите GET_LOCK('refresher_running', 0), и он либо вернет 1, если он даст вам блокировку, либо 0, если блокировка будет взята. Это более атомно. Конечно, тайм-ауты блокировки могут быть полезны в определенных ситуациях, например, когда вы хотите поставить задачи в очередь, но следите за тайм-аутом сценария, если вы получаете слишком много одновременных запросов.

Зак Моррис

Одним из вариантов будет полагаться на блокировку файловой системы вместо базы данных. Поскольку выполнение сценария требует обработки, это не должно иметь значения. Образец из инструкции с неблокирующим эксклюзивным замком:

$fp = fopen('/tmp/lock.txt', 'r+');

/* Activate the LOCK_NB option on an LOCK_EX operation */
if(!flock($fp, LOCK_EX | LOCK_NB)) {
    die('Running already');
}

/* ... */

fclose($fp);

редактировать

Другой вариант - использовать файл состояния, который создается в начале каждого этапа и будет автоматически удален register_shutdown_function по завершении сценария.

Сценарий просто проверяет наличие файла состояния, и если он уже существует, выполнение останавливается:

define('statusFile', sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'myjob.running');

//
// If a process is already running then exit
//
if (file_exists(statusFile)) {
   die('Running already');
} else {
   file_put_contents(date('Y-m-d H:i:s'), statusFile);
}

//
// Other code here
//

function shutdown() {
   unlink(statusFile);
}

//
// Remove the status file on completion
//
register_shutdown_function('shutdown');
Другие вопросы по тегам