Есть ли простой способ обработать ситуацию 'DATABASE IS LOCKED' при обращении к SQLite из PHP?

По адресу www.php.net/manual/en/pdo.begintransaction.php в комментариях рекомендуют вот такой способ оформления транзакций при обращении к базе SQLite:



<?php<br>
$conn = new PDO('sqlite:C:\path\to\file.sqlite');<br>
$stmt = $conn->prepare('INSERT INTO my_table(my_id, my_value) VALUES(?, ?)');<br>
$waiting = true; // Set a loop condition to test for<br>
while($waiting) {<br>
    try {<br>
        $conn->beginTransaction();<br>
        for($i=0; $i < 10; $i++) {<br>
            $stmt->bindValue(1, $i, PDO::PARAM_INT);<br>
            $stmt->bindValue(2, 'TEST', PDO::PARAM_STR);<br>
            $stmt->execute();<br>
            sleep(1);<br>
        }<br>
        $conn->commit();<br>
        $waiting = false;<br>
    } catch(PDOException $e) {<br>
        if(stripos($e->getMessage(), 'DATABASE IS LOCKED') !== false) {<br>
            // This should be specific to SQLite, sleep for 0.25 seconds<br>
            // and try again.  We do have to commit the open transaction first though<br>
            $conn->commit();<br>
            usleep(250000);<br>
        } else {<br>
            $conn->rollBack();<br>
            throw $e;<br>
        }<br>
    }<br>
}<br>


Спрашивается: существует ли ещё более нехитрый и изящный способ обработки ситуации 'DATABASE IS LOCKED'?



Можно ли, например, принудить модуль PDO в языке PHP к самостоятельному ожиданию разблокировки базы данных в течение некоторого заданного времени?



(А не то, сами понимаете, если при каждом обращении такой if нагородить, то падает обозримость кода и читаемость.)
  • Вопрос задан
  • 7056 просмотров
Пригласить эксперта
Ответы на вопрос 3
Можно написать небольшую обвёртку.

<?php

function myQuery(\PDO $conn, \closure $function)
    {
    $waiting = true; // Set a loop condition to test for
    while ($waiting)
        {
        try
            {
            $conn->beginTransaction();
            $function($pdo);
            $conn->commit();
            $waiting = false;
            } catch (PDOException $e)
            {
            if (stripos($e->getMessage(), 'DATABASE IS LOCKED') !== false)
                {
                // This should be specific to SQLite, sleep for 0.25 seconds
                // and try again.  We do have to commit the open transaction first though
                $conn->commit();
                usleep(250000);
                }
            else
                {
                $conn->rollBack();
                throw $e;
                }
            }
        } }

$conn = new PDO('sqlite:C:\path\to\file.sqlite');
$stmt = $conn->prepare('INSERT INTO my_table(my_id, my_value) VALUES(?, ?)');

myQuery($conn, function(\PDO $conn)use($stmt)
        {
        for ($i = 0; $i < 10; $i++)
            {
            $stmt->bindValue(1, $i, PDO::PARAM_INT);
            $stmt->bindValue(2, 'TEST', PDO::PARAM_STR);
            $stmt->execute();
            sleep(1);
            }
        });



Можно и не как функцию, а унаследоваться от PDO и сделать свой метод.
Ответ написан
Комментировать
cjey
@cjey
Я так понимаю, что ошибка происходит при многопоточном обращении к базе. Например попытка вставить данные в таблицу в то время пока второй поток выполняет выборку по этой же таблице. Где-то в документации читал, что SQLite так не умеет.

Поэтому когда я решал такую проблему в многопоточном приложении на C#, я создавал обертку с мониторными секциями на обращение к базе.
Ответ написан
Стандардная ситуация, для много поточного приложения, не знаю на сколько это решило бы вашу проблему, так как в пхп я никогда семафорами не пользовался, но: www.php.net/manual/en/ref.sem.php. Или написать свой междупроцессный lock
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы