среда, октября 13, 2010

php flock

Продолжаю тему блокировки файла в PHP пришёл к такой схеме:

  1. на файл (file) создается замок/файл на запись (wlock) и замок на чтение (rlock);
  2. пред чтением создается жесткая ссылка (rlink) на замок rlock и проверяется количество ссылок на замок wlock == 1, т.е. проверяем на запись, если всё удачно, то читаем файл и в конце удаляем rlink;
  3. перед записью создаем wlink на wlock и считаем количество ссылок wlock == 2 и rlock == 1, т.е. проверяем нет ли записывающих и читающих, если всё успешно, то записываем в файл, а затем удаляем wlink;
Думаю понятен принцип.

сам код:


function get_link_count($fname){
    $fp = fopen($fname, 'r');
    if ($fp) {
        $stat = fstat($fp);
        fclose($fp);
        return $stat['nlink'];
    }
    return false;
}

function custom_flock($fname, $mode = 'r'){
    /* возвращает имя ссылки в случае удачи, иначе false
        @$fname имя файла

        @$mode режим блокировки
        r - на чтение
            или
        w - на запись
        если режим чтения, то смотрим не заблокирован ли файл на запись
        если режим записи, то смотрим не заблокирован ли файл на запись или чтение
    */
    $rlock = '.lock.' . $fname . '.read';
    $wlock = '.lock.' . $fname . '.write';

    
    clearstatcache();
    if (file_exists($fname) and touch($rlock) and touch($wlock)) {

        if ('w' == $mode){
            $link = $wlock . rand() . '.link';
            if (link($wlock, $link)){
                if(get_link_count($wlock) == 2 and get_link_count($rlock) == 1){
                    return $link;
                } else {
                    unlink($link);
                }
            } else {
                unlink($link);
            }
        } else {
            $link = $rlock . rand() . '.link';
            if (link($rlock, $link)){
                if (get_link_count($wlock) == 1){
                    return $link;
                } else {
                    unlink($link);
                }
            } else {
                unlink($link);
            }
        }
    }
    return false;
}

Данная схема не гарантирует целостности файла при падении скрипта, для этого нужно делать журнал для откатки на последнюю версию. Так же нужно автоматически удалять старые ссылки в случае падения скрипта.

код можно скачать здесь
в архиве доступны тесты которые я опробовал на тесте ab в результате тестов целостность файла была сохранена, там 2 теста test_counter.php и test_chain.php. test_chain.php как раз таки и подтверждает целостность файла.

Тест заключается в следующем последовательно пишем в файл 1 или 0 если был последний 0, то запишем 1 и наоборот в конце теста проверяем правильность последовательности 0 и 1 если последовательность правильная, то целостность файла сохраняется.

19 комментариев:

Анонимный комментирует...

Как-то сложновато, много, на первый взгляд, лишних операций. Ссылки, подсчёт ссылок.

Я пока архив не скачивал. Скажи параметры тестирования ab, и сколько циклов прогонял?

Jungle комментирует...

вот так гонял ab -kc 100 -t 30 http://localhost/jungle/custom_flock/test_chain.php

потом так ab -n 500 -c 100 http://localhost/jungle/custom_flock/test_chain.php

операций нет так уж и много, зато целостность сохраняется

скачай архив, попробуй погонять тесты.

Анонимный комментирует...

У тебя значения конкурентности слишком большие. Я вчера тоже игрался, и понял, что нецелесообразно ставить его значение более 3-10, потому что тогда вообще эффект не наблюдается. Он захлёбывается, что ли :)

Я гоняю на конкурентности 3.
Сбои у моих старых функций наблюдались, если прогнать 1000 циклов ab с 50 запросами каждый и конкурентностью 3 (ab -n 50 -c 3). При таких условиях получалось примерно 20 сбросов.

Я сейчас допишу свою функцию, поставлю на такой прогон и спать. А потом проснусь, поработаю ещё, и, если поеду в лес, запущу твой тест. Хотя, честно говоря, страшно компьютер в пустой квартире оставлять под таким стрессовым тестом, так что может до следующей ночи тогда.

В любом случае спасибо! Не оставил меня в беде :)

Jungle комментирует...

да не за что, самому интересно стало, вот и решил помочь, надеюсь что выход будет найден :)

Jungle комментирует...

провел тест ещё без блокировки файла, который подтвердил, что мой метод работает ;)

так что бери на вооружение, не ломай себе голову, двигай другие проекты, а то будет дедлайн :)

Анонимный комментирует...

В смысле "без блокировки". Поясни, пожалуйста.

Jungle комментирует...

да просто тест без всяких блокировок файла, что бы сравнить 2 результата метод с блокировкой VS без блокировки

Анонимный комментирует...

И что, хочешь сказать без блокировок сбросов не было? Трудно поверить. Как точно ты проверял? Какой алгоритм?

Jungle комментирует...

наоборот без блокировок были сбросы, т.е. последовательность 0 и 1 была не правильной, а с блокировкой последовательность была правильной.

Анонимный комментирует...

Кстати, опытным путём выяснил, что задержка в 5000 микросекунд - это очень много, слишком. Пока в локалке у меня оптимальные результаты при 500 мс, но я думаю, их надо подбирать под сервер и нагрузку. Возможно, в Daos 2.0 это будет отдельно настраиваться в группе тонких настроек, ну или вообще автоматически подбираться параметры.

Jungle комментирует...

ну вот видишь твой метод с задержками, а мой без :)

Анонимный комментирует...

Это как это без? В функциях, которые ты сделал под Daos задержки остались.

Jungle комментирует...

нет я имею ввиду не твой модифицированный код, а так да для статистики нужны задержки тогда

Анонимный комментирует...

Минут через 15-20 запускаю тестирование с твоими версиями функций (задержки только уменьшил до такого же значения, как у меня сейчас) и спать :) Утром отпишусь.

Анонимный комментирует...

Не, всё же не стал запускать. Сейчас голова совсем не варит, но просматривая твой код наткнулся на какую-то странную конструкцию.

Что за do? Странный цикл. Разве в PHP допустимы такие конструкции? Я знаю while. И если допустимы, то при каких условиях из этого цикла выход?

Анонимный комментирует...

Ну вот, а второй комментарий потерялся?f

Jungle комментирует...

хм, не знаю, только этот был
ну как там дела?

Jungle комментирует...

нашел комментарий :), blogger глючит :)
да есть такой оператор do

он как while, только условие проверяется в конце цикла.

Анонимный комментирует...

Я уже понял :) Тест запущу на эту ночь теперь, прошлым утром тестировал вариант с сессиями. Он, кстати, очень неплохо себя показал.