Как предотвратить сброс этого счетчика на 100 000?

Этот скрипт сбрасывается после 100 000. Что мне нужно изменить, чтобы предотвратить сброс и вместо этого продолжать подсчет?

<?php
$filename1 = 'content/general_site_data/total_site_page_loads.txt';

if (file_exists($filename1)) {
    $fh = fopen("content/general_site_data/total_site_page_loads.txt", "a+");
    if($fh==false)
        die("unable to create file");

    $filec = 'content/general_site_data/total_site_page_loads.txt';
    if (!is_writable($filec))
        die('not writable');

    $total_site_page_loads = trim(file_get_contents($filec)) + 1;
    fwrite(fopen($filec, 'w'), $total_site_page_loads);

    echo '------------------------------<br />
    Site Wide Page Views: '.$total_site_page_loads.'<br />';
} else {
    $fh = fopen($filename1, "a");
    $total_site_page_loads = trim(file_get_contents($filename1)) + 1;
    fwrite($fh, $total_site_page_loads);
    fclose($fh);

    echo '------------------------------<br />
    Site Wide Page Views: '.$total_site_page_loads.'<br />';
}
?>

person mobilestimulus    schedule 09.03.2011    source источник
comment
В этом коде нет ничего, что могло бы вызвать сброс.   -  person Pekka    schedule 09.03.2011
comment
@Pekka Я знаю, да? Но, кажется, все же это делает.   -  person mobilestimulus    schedule 09.03.2011
comment
Можете показать на живом примере, как меняется файл count при приближении к 100 000?   -  person Pekka    schedule 09.03.2011
comment
Блокировки файла здесь нет, второе открытие в режиме w выполняет усечение. Классическое состояние гонки, поэтому возможен сброс. Ожидается ответ.   -  person Charles    schedule 09.03.2011
comment
@Charles, но доступ w работает только в том случае, если файл не существует   -  person Pekka    schedule 09.03.2011
comment
@Pekka, да, серьезное отсутствие отступов затрудняет отслеживание. Тыкать в это сейчас ...   -  person Charles    schedule 09.03.2011
comment
@Charles, да, это действительно нуждается в лучшем отступе   -  person Pekka    schedule 09.03.2011
comment
@Pekka, открытие в w действительно происходит внутри блока if-file-exists. Он открывает его в режиме добавления, а затем повторно открывает в режиме записи. что за хрень   -  person Charles    schedule 09.03.2011
comment
@Чарльз тьфу! В этом действительно мало смысла. Хотя я думаю, это все равно должно работать... Насколько я могу понять   -  person Pekka    schedule 09.03.2011
comment
@mobile, можешь немного перемотать вперед? Я бы не хотел нажимать F5 99 999 раз :)   -  person Pekka    schedule 09.03.2011
comment
@Пекка Лол! Ага. Постоянно перезагружается...   -  person mobilestimulus    schedule 09.03.2011
comment
Код имеет мало смысла. Он определяет имя файла дважды и использует глупый if/else. Не стоит спасать или исследовать источник ошибки.   -  person mario    schedule 09.03.2011
comment
@мобильный аааа. Это действительно похоже на состояние гонки. Попробуйте решение @Charles   -  person Pekka    schedule 09.03.2011
comment
@Pekka, оказывается, что после прохождения кода блок else никогда не должен даже срабатывать - начальное открытие происходит в режиме добавления или создания, поэтому проверка существования файла никогда не должна завершаться ошибкой, если открытие также не завершилось ошибкой, что приведет к смерти сценария. Я обновил свой ответ более безопасным кодом. (Полагаю, это то, что я получил за то, что так долго работал с базами данных с плоскими файлами. Ужасные воспоминания...)   -  person Charles    schedule 09.03.2011


Ответы (2)


Ваш код может страдать от состояния гонки.

В середине вы снова открываете файл в режиме w, который усекает файл до нулевой длины. Если другая копия вашего скрипта откроется и попытается прочитать файл в то время, когда он был усечен, но до того, как он был прочитан, счетчик будет сброшен на ноль.

Вот обновленная версия вашего кода:

    $filename = 'content/general_site_data/total_site_page_loads.txt';
// Open our file in append-or-create mode.
    $fh = fopen($filename, "a+");
    if(!$fh)
        die("unable to create file");
// Before doing anything else, get an exclusive lock on the file.
// This will prevent anybody else from reading or writing to it.
    flock($fh, LOCK_EX);
// Place the pointer at the start of the file.
    fseek($fh, 0);
// Read one line from the file, then increment the number.
// There should only ever be one line.
    $total_site_page_loads = 1 + intval(trim(fgets($fh)));
// Now we can reset the pointer again, and truncate the file to zero length.
    fseek($fh, 0);
    ftruncate($fh, 0);
// Now we can write out our line.
    fwrite($fh, $total_site_page_loads . "\n");
// And we're done.  Closing the file will also release the lock.
    fclose($fh);
    echo '------------------------------',
         '<br />Site Wide Page Views: ',
         $total_site_page_loads,
         '<br />';

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

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

person Charles    schedule 09.03.2011
comment
Хорошая работа. Из любопытства, что произойдет, когда блокировка будет на месте - будут ли параллельные сценарии ждать или пропускать? - person Pekka; 09.03.2011
comment
LOCK_EX — это блокирующая операция, поэтому скрипт будет сидеть и ждать несколько секунд, пока что-то не будет выпущено. Если владелец блокировки выходит, не сняв ее, соответствующие реализации блокировки снимут блокировку. На самом деле это зависит от ОС и файловой системы. Здесь есть всевозможные проблемы с крайним случаем, в том числе операционная система, исчерпавшая блокировку файлов (серьезно) и старые версии NFS, которые портят ситуацию, если файловая система является монтированием NFS, хотя это, похоже, проблема с действительно старыми версиями Solaris на действительно ужасные веб-хосты, обычно управляемые телекоммуникационными компаниями. Иногда я ненавидел свою старую работу. - person Charles; 09.03.2011
comment
Под несколькими моментами я имею в виду микросекунды. Чтение, усечение и перезапись такого крошечного файла должны быть практически мгновенными, если только сервер не испытывает сильного голода по вводу-выводу. Только если вы получаете много-много одновременных запросов, ваши пользователи когда-либо заметят задержку. - person Charles; 09.03.2011
comment
(Кроме того, именно поэтому базы данных такие замечательные. Возможность сказать UPDATE Counter SET hits = hits + 1 WHERE site = 'mine' и позволить механизму базы данных решать проблему параллелизма — это восхитительно.) - person Charles; 09.03.2011
comment
@Charles отличное объяснение, спасибо. - person Pekka; 09.03.2011
comment
@Pekka и @Charles - кажется, это тот ответ, который я искал. Спасибо, что посмотрели, вы оба. - person mobilestimulus; 09.03.2011
comment
@Pekka и @Charles. Не могли бы вы взглянуть на мой дополнительный вопрос? - stackoverflow.com/questions/5251478/ - person mobilestimulus; 09.03.2011

Я не вижу, где может произойти какой-либо сброс, но то, как работает сценарий, кажется довольно простым. Может быть, попробовать изменить total_site_page_loads.txt на что-то вроде 99990 и посмотреть, что происходит с этим файлом, когда вы переходите к 100000?

person John    schedule 09.03.2011