Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
Социальная инженерия и социальные хакеры. Авторы: Кузнецов М.В., Симдянов И.В. Самоучитель MySQL 5. Авторы: Кузнецов М.В., Симдянов И.В. Объектно-ориентированное программирование на PHP. Авторы: Кузнецов М.В., Симдянов И.В. PHP. Практика создания Web-сайтов (второе издание). Авторы: Кузнецов М.В., Симдянов И.В. PHP 5. На примерах. Авторы: Кузнецов М.В., Симдянов И.В., Голышев С.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум PHP

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: ОШИБКА!!! Незапланированное обнуление файла данных!
 
 автор: Sfinks   (01.02.2005 в 13:59)   письмо автору
 
 

Добрый день! =))

У меня такая проблема.... Полностью рабочий код, иногда стирает файл, который сам же и записывает!

Фрагмент кода....
<?php
  
// Учет посещаемости
  
@include("prosmotry.php");
  
// Небольшая обработка и изменение данных из файла 
  
if($fprosm = @fopen("prosmotry.php","wb"))
  { 
fwrite($fprosm,'<? $prosm = array (');
    foreach(
$prosm as $time => $info)
      
fwrite($fprosm,"\r\n$time=>array(/* информация */),");
    
fwrite($fprosm,"\r\n); ?>");
    
fclose($fprosm);
  }
?>
Почему это может происходить?

У меня возникло только одно предположение...
При практически одновременном обращении 2х разных пользователей к этому коду, первый еще пишет, второй уже читает, в результате второй читает "", соответственно в файл ничего не пишет.... Результат - обнуление =((

Подумал вставить перед include() задержку строкой:
while(filesize("prosmotry.php") == 0);

но написав такой вот локальный код:
<?php
  $f 
fopen("Edit1.htm","wb");
  echo 
filesize("Edit1.htm")."<br>";
  
fwrite($f,"sdfsdfs");
  echo 
filesize("Edit1.htm")."<br>";
  
fclose($f);
  echo 
filesize("Edit1.htm")."<br>";
?>
увидел 3 нуля.... Боюсь такой способ приведет к бесконечному циклу =((

Как решить эту проблему?

   
 
 автор: cheops   (01.02.2005 в 14:31)   письмо автору
 
   для: Sfinks   (01.02.2005 в 13:59)
 

В этом случае прибегают к блокировки файлов при помощи функции flock()
http://www.softtime.ru/forum/read.php?id_forum=1&id_theme=63
http://www.softtime.ru/forum/read.php?id_forum=1&id_theme=572

   
 
 автор: Sfinks   (01.02.2005 в 23:04)   письмо автору
 
   для: cheops   (01.02.2005 в 14:31)
 

Вот такая модификация будет работать корректно?...
<?php
  
if(file_exists("prosmotry.php"))
    while(!
flock("prosmotry.php"LOCK_EX));
  @include(
"prosmotry.php"); 
    
// Обработка
  
if($fprosm = @fopen("prosmotry.php","wb")) 
  { 
fwrite($fprosm,'<? $prosm = array ('); 
    foreach(
$prosm as $time => $info
      
fwrite($fprosm,"\r\n$time=>array(/* информация */),"); 
    
fwrite($fprosm,"\r\n); ?>"); 
    
fclose($fprosm); 
  }
  
flock("prosmotry.php"LOCK_UN);
?>

   
 
 автор: cheops   (01.02.2005 в 23:08)   письмо автору
 
   для: Sfinks   (01.02.2005 в 23:04)
 

Да, она не позволит побить файл.

   
 
 автор: Sfinks   (01.02.2005 в 23:21)   письмо автору
 
   для: cheops   (01.02.2005 в 23:08)
 

БОЛЬШОЕ СПАСИБО =))

   
 
 автор: cheops   (01.02.2005 в 23:25)   письмо автору
 
   для: cheops   (01.02.2005 в 23:08)
 

Ой, вообще-то я ошибся... функция flock() должна в качестве первого параметра принимать дескриптор файла, т.е. она вызывается после функции fopen()... и до функции fclose()

   
 
 автор: Sfinks   (02.02.2005 в 00:40)   письмо автору
 
   для: cheops   (01.02.2005 в 23:25)
 

А как же тогда сделать задержку до освобождения (окончания записи другим пользователем) файла, перед include()?
Нужно чтоб он includ'ился при любых раскладах....

Может так....
<?php 
  
while(!isset($prosm))
    @include(
"prosmotry.php"); 
  
// Обработка 
  
if($fprosm = @fopen("prosmotry.php","wb")) 
  { 
flock($fprosmLOCK_EX)
    
fwrite($fprosm,'<? $prosm = array ('); 
    foreach(
$prosm as $time => $info
      
fwrite($fprosm,"\r\n$time=>array(/* информация */),"); 
    
fwrite($fprosm,"\r\n); ?>");
    
flock($fprosmLOCK_UN);
    
fclose($fprosm); 
  } 
?>

   
 
 автор: cheops   (02.02.2005 в 01:45)   письмо автору
 
   для: Sfinks   (02.02.2005 в 00:40)
 

Тогда открывать файл следует до include, а в include не открывать а пользоваться уже открытым дескриптором файла.

   
 
 автор: Sfinks   (02.02.2005 в 02:42)   письмо автору
 
   для: cheops   (02.02.2005 в 01:45)
 

А что не так в коде в моем предыдущем вопросе?
Ну, для гарантии можно еще написать:
<?php
  
if(isset($prosm)) unset($prosm);
  while(!isset(
$prosm)) 
    @include(
"prosmotry.php"); 
  ....
?>
$prosm определяется в prosmotry.php, следовательно пока файл не заинклюдится, код дальше не пойдет.
А залочивание происходит ток при записи....
-----------------------------------------------------------------------------------------------------------------
Или вот такой вариант (в prosmotry.php используется return()).....
<?php 
  
if(file_exists("prosmotry.php"))
    while(!@include(
"prosmotry.php")); 
  if(
$fprosm = @fopen("prosmotry.php","wb")) 
  { while(!
flock($fprosmLOCK_EX));
    
// Обработка массива $prosm
    
fwrite($fprosm,'<? $prosm = array ('); 
    foreach(
$prosm as $time => $info
      
fwrite($fprosm,"\r\n$time=>array(/* информация */),"); 
    
fwrite($fprosm,"\r\n); return(true); ?>"); 
    
flock($fprosmLOCK_UN); 
    
fclose($fprosm); 
  } 
?>

   
 
 автор: Sfinks   (02.02.2005 в 05:36)   письмо автору
 
   для: cheops   (02.02.2005 в 01:45)
 

Хм.... Вот последний вариант весьма корректно работает, ток объясните мне пожалуйста одну вешь....
Я смоделировал ситуацию совместного доступа следующим образом: создал 2 файла с таким кодом и в первом перед разлочкой вставил sleep(10); Обращаюсь к первому, тут же ко второму.... Все верно: второй ждет первого и ток потом выполняется, НО!!!!
На localhost'е в WinXP файл все-равно обнуляется, однако на реальном UNIX-сервере все работает правильно.... данные сохраняются!
Почему это происходит?...

   
 
 автор: cheops   (02.02.2005 в 09:25)   письмо автору
 
   для: Sfinks   (02.02.2005 в 05:36)
 

В Windows есть определённые проблемы с блокировкой и может давать вклад include("prosmotry.php"). UNIX специально предназначен для работы в таком режиме.

   
 
 автор: Sfinks   (02.02.2005 в 17:46)   письмо автору
 
   для: cheops   (02.02.2005 в 09:25)
 

ААААААААААААААААААААААААААААААААААААААА
ОН СНОВА ОБНУЛИЛСЯ =(((((((((((((((((((((((((
ЧТО ДЕЛАТЬ?!?!?!?!?!?!?!?????!???!!!!

   
 
 автор: cheops   (02.02.2005 в 23:52)   письмо автору
 
   для: Sfinks   (02.02.2005 в 17:46)
 

Сейчас файлы везде блокируются или только в одном месте? Ситуация с обнулением не воспроизводится? Не может обнуляться она случайно при записи туда пустого значения?
У вас информация не добавляется в конце файла, а каждый раз затирает предыдущие изменения - это нормальное поведение? Ведь передав пустой цикл - можно обнулить файл.

   
 
 автор: Sfinks   (03.02.2005 в 00:19)   письмо автору
 
   для: cheops   (02.02.2005 в 23:52)
 

>Сейчас файлы везде блокируются или только в одном месте?
Я Вам на e-Mail (письмо автору) скинул полный код основного файла!

>Ситуация с обнулением не воспроизводится?
Обнулялся в 16 с копейками, в 20 с копейками.

>Не может обнуляться она случайно при записи туда пустого значения?
Не знаю! Я уже не представляю что думать! Что интересно, остальные файлы проекта перезаписываются таким же образом, но они (тьфу-тьфу-тьфу) ни разу не обнулялись. Может какие-то проблемы с типами значений, или я уже не знаю....

>У вас информация не добавляется в конце файла, а каждый раз затирает
>предыдущие изменения - это нормальное поведение?
Во-первых, я не разобрался со смещениями курсора в файле при дописывании, а во-вторых, в данном конкретном счетчике возможно проще дописывать, но в файле пользователей, это сложнее, так как пользователя нужно искать по всему файлу. Но если Вы мне напишите фрагменты "редактирования" файлов, вместо "перезаписи", буду благодарен! Структуру файлов данных Вы поймете из кода на e-Maile.

>Ведь передав пустой цикл - можно обнулить файл.
Если предыдущим обращением файл сохранен корректно, то передать пустой массив невозможно, так как он инифиализируется инклюдом из этого же файла, непосредственно перед сохранением!

   
 
 автор: Sfinks   (03.02.2005 в 00:37)   письмо автору
 
   для: cheops   (02.02.2005 в 23:52)
 

Вот щас посреди файла откуда-то взялась пустая строка! Как такое возможно? =((

   
 
 автор: Sfinks   (03.02.2005 в 00:43)   письмо автору
 
   для: cheops   (02.02.2005 в 23:52)
 

Вот в 23-30 снова обнулился =((

А файл пользователей размером в 30 раз больше не обнуляется уже 3 недели!

Вся глобальная разница массивов - имена подмассивов первого уровня!

   
 
 автор: Sfinks   (03.02.2005 в 15:51)   письмо автору
 
   для: cheops   (02.02.2005 в 23:52)
 

Так что же мне сделать? =(( Он снова упал =((

   
 
 автор: cheops   (03.02.2005 в 16:12)   письмо автору
 
   для: Sfinks   (03.02.2005 в 15:51)
 

Опишите пожалуйста логику Web-приложения, может вместе придумаем другую схему (более надёжную).

   
 
 автор: Sfinks   (03.02.2005 в 19:37)   письмо автору
 
   для: cheops   (03.02.2005 в 16:12)
 

Это скорее Mobile-приложение.

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

Статистика должна иметь такую структуру, чтобы из нее можно было получить следующие данные:
1. Количество обращений каждого из зарегистрированных пользователей.
2. Количество обращений любых пользователей за указанный период времени (например 7.12.05 с 0:00 до 12:00), сколько из них являются обращениями от пользователей, у которых это минимум пятое обращение за все время пользования сервисом, общее количество обратившихся пользователей и сколько из них являются новыми пользователями (обратились впервые).

Вот сейчас структура данных обоих статистик аналогична:
По первой статистике:
<? $users = array (
'HW9GN4'=>array('reg'=>'СПб','acc'=>276,'tim'=>2),
   ..................
'9H1FN4'=>array('reg'=>'Москва','acc'=>230,'tim'=>3),
); return(
true); ?>

По второй статистике (реализованы ток кол.нов.польз. и общее кол.обращений):
<? $visits = array (
'501290102'=>array('acc'=>1,'new'=>array('ASDF',)),
'501290103'=>array('acc'=>5,'new'=>array('DFGH','SDFG',)),
'501290104'=>array('acc'=>18,'new'=>array('FGHJ','GHJK','HJKL','JKLM',)),
   ..................
'501290828'=>array('acc'=>1,'new'=>array('RLXEU4',)),
); return(
true); ?>

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

   
 
 автор: sfinks   (04.02.2005 в 00:42)   письмо автору
 
   для: cheops   (03.02.2005 в 16:12)
 

А я вот что подумал,....

А если не писать в файл vizity.php, а писать во временный файл, например, vizity_1.php (или следующий порядковый номер, в зависимости от наличия), а по завершении записи переименовывать его в vizity.php?... М? Ведь переименование это лишь исправление FAT? Значит процесс практически мгновенный? Будет ли это надежно?

Или Вы мне все-таки предложите что-либо?

   
 
 автор: cheops   (04.02.2005 в 07:37)   письмо автору
 
   для: sfinks   (04.02.2005 в 00:42)
 

А вы как опеределяете числа
501290102
501290103
501290104

1) Можно вот какой финт сделать - под хранение этого числа завести ещё один файл и хранить там это значение, открытие, чтение и перезапись одного короткого файла будет происходить много быстрее, чем перезапись 100 Кб файла, а в 100 Кб файл останется только дописывать уже готовую информацию и не открывать его для анализа. Правда это не спасёт от порчи файла - здесь вы правы - нужно по датам разбивать - чем короче файл, тем больше у него шансов выжить.
2) Можно вот ещё чего сделать - под текущий период завести 10 файлов, и писать в файлы по датчику случайных чисел, а потом, когда данные будут отправляться в архив - объединять их в один файл. Вот этим можно здорово снять нагрузку с файла, а хранение индекса в отдельной файле не позволит сбиться со счёта.

   
 
 автор: Sfinks   (04.02.2005 в 12:46)   письмо автору
 
   для: cheops   (04.02.2005 в 07:37)
 

Числа 502010112 и т.д. - это время - (int)date("ymdHi",time());

Вот как выглядит фрагмент перезаписи....
<?php
  
if(file_exists("vis.php"))
    while(!@include(
"vis.php"));
  if(
$fvis = @fopen("vis.php","wb"))
  { while(!
flock($fvisLOCK_EX));
    
$dt = (int)date("ymdHi",time());
    
$visits[$dt]["acc"]++;
    
$old false;
    if(isset(
$visits))
      foreach(
$visits as $info)
        if(isset(
$info["new"]))
          if(
in_array($pid,$info["new"]))
          { 
$old true;
            break;
          }
    if(!
$old)
      
$visits[$dt]["new"][] = $pid;
    
fwrite($fvis,'<? $visits = array (');
    if(isset(
$visits))
      foreach(
$visits as $time => $info)
      { 
fwrite($fvis,"\r\n$time=>array('acc'=>".$info["acc"]);
        if(isset(
$info["new"]))
        { 
fwrite($fvis,",'new'=>array(");
          foreach(
$info["new"] as $u_pid)
            
fwrite($fvis,"'$u_pid',");
          
fwrite($fvis,")");
        }
        
fwrite($fvis,"),");
      }
    
fwrite($fvis,"\r\n); return(true); ?>");
    
flock($fvisLOCK_UN);
    
fclose($fvis);
  }
?>

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

Фактически, разбиение ускорения не даст, т.к. всерн придется просматривать все файлы, но надежность увеличится.... но не достаточно =(( Не достаточно, т.к. файл падает несколько раз за сутки. А разбивать по 2 часа, это уже паранойя =((
Нужно какой-то другой выход.....

Наверняка Вы задаете себе вопрос, почему я не использую MySQL.... по нескольким причинам.... 1. Я его совсем не знаю... Абсолютно 0! А учить с 0 для этого конкретного проекта уже некогда. 2. Выкачать его по GPRS.... ну сами понимаете. 3. Это нужно на хосте подключать доп услугу... снова дорого. Поэтому хочу решить так.

Да, и способ с переименованием на 2 поста выше - эт разве не выход?

   
 
 автор: cheops   (04.02.2005 в 21:16)   письмо автору
 
   для: Sfinks   (04.02.2005 в 12:46)
 

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

   
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования