Антидедлоки в ActiveRecord

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Аватара пользователя
Stepan Selyuk
Сообщения: 198
Зарегистрирован: 2010.02.03, 05:51
Откуда: Cyprus, Limassol
Контактная информация:

Антидедлоки в ActiveRecord

Сообщение Stepan Selyuk »

Hi there!

Как вы думаете, что лучше, использовать прокладку для ActiveRecord, которая запускает методы работающие с базой через try{} catch{} и таким образом может избежать дедлока, или же сделать прокладку непосредственно для \yii\db\Connection ? Только я пока не нашел корневой метод, который делает запрос к PDO.

Код: Выделить всё

namespace app\components\yiiExt;

use yii\db\ActiveRecord;
use yii\db\Exception;

/**
 * Class ActiveRecordDeadlockPrevent
 * @package app\components\yiiExt
 */
class ActiveRecordDeadlockPrevent extends ActiveRecord
{

    const DEADLOCK_USLEEP = 300000;

    const DEADLOCK_TPL = '/1213 deadlock/';

    const MAX_ATTEMPTS = 5;

    protected function callMethod( $name, $args )
    {

        // Статическая переменная, так как возможны вложенные вызовы.
        // Таким образом мы суммарно ограничиваем количество попыток.
        static $attempts = 0;

        while (++$attempts <= self::MAX_ATTEMPTS) {

            try {

                $result = call_user_func_array( [ 'parent', $name ], $args );

                // После успешного вызова обнуляем число попыток
                $attempts = 0;

                return $result;

            } catch ( Exception $e ) {

                // Еще раз нужно проверить число попыток, так как оно могло увеличиться при вложенных вызовах
                if ($attempts <= self::MAX_ATTEMPTS && preg_match( self::DEADLOCK_TPL, $e->getMessage() )) {

                    usleep( self::DEADLOCK_USLEEP );

                } else {

                    throw $e;
                }
            }
        }

        return call_user_func_array( [ 'parent', $name ], $args );
    }

    public function save( $runValidation = true, $attributes = null )
    {

        return $this->callMethod( __FUNCTION__, func_get_args() );
    }

    public function updateCounters( $counters, $condition = '', $params = [ ] )
    {

        return $this->callMethod( __FUNCTION__, func_get_args() );
    }

    public function update( $runValidation = true, $attributeNames = null )
    {

        return $this->callMethod( __FUNCTION__, func_get_args() );
    }

    public function updateAttributes( $attributes )
    {

        return $this->callMethod( __FUNCTION__, func_get_args() );
    }

    public function delete()
    {

        return $this->callMethod( __FUNCTION__, func_get_args() );
    }

    public function refresh()
    {

        return $this->callMethod( __FUNCTION__, func_get_args() );
    }

    public function insert( $runValidation = true, $attributes = null )
    {

        return $this->callMethod( __FUNCTION__, func_get_args() );
    }

    public static function updateAll( $attributes, $condition = '', $params = [ ] )
    {

        return ( new static )->callMethod( __FUNCTION__, func_get_args() );
    }

    public static function deleteAll( $condition = '', $params = [ ] )
    {

        return ( new static )->callMethod( __FUNCTION__, func_get_args() );
    }

    public static function findOne( $condition )
    {

        return ( new static )->callMethod( __FUNCTION__, func_get_args() );
    }

    public static function updateAllCounters( $counters, $condition = '', $params = [ ] )
    {

        return ( new static )->callMethod( __FUNCTION__, func_get_args() );
    }

    public static function findBySql( $sql, $params = [ ] )
    {

        return ( new static )->callMethod( __FUNCTION__, func_get_args() );
    }

    public static function find()
    {

        return ( new static )->callMethod( __FUNCTION__, func_get_args() );
    }

    public static function findAll( $condition )
    {

        return ( new static )->callMethod( __FUNCTION__, func_get_args() );
    }

} 
 
Сначала невидимое, затем видимое. И так у всех программистов :)
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Антидедлоки в ActiveRecord

Сообщение yiijeka »

Корневой метод это public function createCommand($sql = null, $params = []) в Connection.
По дедлокам хз, никогда на них не натыкался, наверное лучше через try{} catch{}....
Аватара пользователя
Stepan Selyuk
Сообщения: 198
Зарегистрирован: 2010.02.03, 05:51
Откуда: Cyprus, Limassol
Контактная информация:

Re: Антидедлоки в ActiveRecord

Сообщение Stepan Selyuk »

lancedevnull писал(а):may be it https://github.com/yiisoft/yii2/blob/bf ... lMutex.php
Цель использования мьютексов — защита данных от повреждения в результате асинхронных изменений (состояние гонки), однако могут порождаться другие проблемы — такие, как взаимная блокировка (клинч).
Дедлоки от того и возникают, что наступает таймаунт, когда операция ждала ждала и вылетела по таймауту, а целевая таблица/строка еще не разблокирована. Поэтому вариант отлавливать такое исключение и брать паузу в программе, потом пробовать повторить тот же запрос...
Сначала невидимое, затем видимое. И так у всех программистов :)
Ответить