PHP и pgbouncer в режиме транзакции: текущая транзакция прервана - PullRequest
2 голосов
/ 23 июня 2011

Я запускаю веб-сайт Drupal 7.2, в который встроено флеш-игра с несколькими пользовательскими сценариями PHP для статистики игроков.Использование CentOS 5.6 / 64 бит, PostgreSQL 8.4.8 и PHP 5.3.Это Quad-Opteron с 4 ГБ оперативной памяти.

В пиковые периоды времени (когда в сети около 500 игроков) мой веб-сайт работал со слишком большим количеством процессов postmaster.По совету общего списка рассылки pgsql Я установил pgbouncer 1.3.4 со следующим /etc/pgbouncer.ini:

[databases]
pref = host=/tmp user=pref password=XXX dbname=pref

[pgbouncer]
logfile = /var/log/pgbouncer.log
pidfile = /var/run/pgbouncer/pgbouncer.pid
listen_port = 6432
unix_socket_dir = /tmp

auth_type = md5
auth_file = /var/lib/pgsql/data/global/pg_auth

pool_mode = transaction
;pool_mode = session

server_check_delay = 10

max_client_conn = 200
default_pool_size = 16

log_connections = 0
log_disconnections = 0
log_pooler_errors = 1

И увеличил shared_buffers = 1024MB и уменьшил max_connections= 50 в postgresql.conf.

Это помогло, но у меня часто возникает проблема, заключающаяся в том, что подготовленный оператор PDO не будет найден:

SQLSTATE[26000]: Invalid sql statement name: 7 ERROR: prepared statement "pdo_stmt_00000016" does not exist
  • , вероятно, из-за переключения pgbouncerсоединение между prepare () и execute ().

Я не могу переключить pgbouncer в режим сеанса - мой веб-сайт зависнет.

Я пытался добавить PDO :: ATTR_EMULATE_PREPARES => true - мой веб-сайт тоже зависает.

Я добавил beginTransaction () и commit () вокруг каждого вызова prepare () и execute () - но потом я получаюочень часто встречается следующая ошибка:

SQLSTATE[25P02]: In failed sql transaction: 7 ERROR:  current transaction is aborted, commands ignored until end of transaction block

Ниже приведен отрывок моего кода с ошибкой - это очень просто и просто вызывает пять операторов SELECT:

function fetch_top() {
        $table       = '';
        $top         = '';

        try {
                # throw exception on any errors
                $options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
                $db = new PDO(sprintf('pgsql:host=%s port=%u; dbname=%s',
                        DBHOST, DBPORT, DBNAME), DBUSER, DBPASS, $options);

                # last week's winner
                $db->beginTransaction();
                $sth = $db->prepare("
select u.id,
        u.first_name,
        u.avatar,
        u.female,
        u.city,
        m.money,
        u.login > u.logout as online
from pref_users u, pref_money m where
        m.yw=to_char(current_timestamp - interval '1 week', 'IYYY-IW') and
        u.id=m.id
order by m.money desc
limit 1
");
                $sth->execute();
                $winner = $sth->fetch(PDO::FETCH_OBJ);
                $db->commit();

                $db->beginTransaction();
                $sth = $db->prepare('
select count(id) from (
    select id,
           row_number() over(partition by yw order by money desc) as ranking
    from pref_money
) x
where x.ranking = 1 and id=?
');
                $sth->execute(array($winner->id));
                $winner_medals = $sth->fetchColumn();
                $db->commit();

                # current week leader
                $db->beginTransaction();
                $sth = $db->prepare("
select u.id,
        u.first_name,
        u.avatar,
        u.female,
        u.city,
        m.money,
        u.login > u.logout as online
from pref_users u, pref_money m where
        m.yw=to_char(current_timestamp, 'IYYY-IW') and
        u.id=m.id
order by m.money desc
limit 1
");
                $sth->execute();
                $leader = $sth->fetch(PDO::FETCH_OBJ);
                $db->commit();

                $db->beginTransaction();
                $sth = $db->prepare('
select count(id) from (
    select id,
           row_number() over(partition by yw order by money desc) as ranking
    from pref_money
) x
where x.ranking = 1 and id=?
');
                $sth->execute(array($leader->id));
                $leader_medals = $sth->fetchColumn();
                $db->commit();

                # fetch top players
                $db->beginTransaction();
                $sth = $db->prepare("
select u.id,
        u.first_name,
        u.female,
        u.city,
        m.money,
        u.login > u.logout as online
from pref_users u, pref_money m where
        m.yw=to_char(current_timestamp, 'IYYY-IW') and
        u.id=m.id
order by m.money desc
limit 7
");
                $sth->execute();
                $i = 0;
                while ($player = $sth->fetch(PDO::FETCH_OBJ)) {
                        $top .= user_link($player) . ($i++ > 0 ? '' : '&nbsp;&raquo;') . '<br />';
                }
                $db->commit();

                # create the HTML table
                $table = sprintf('.... skipped for brevity ....');
        } catch (Exception $e) {
                exit('Database problem: ' . $e->getMessage());
        }

        return $table;
}

Любая помощь, пожалуйста?Alex

Ответы [ 3 ]

1 голос
/ 23 июня 2011
  1. Конфигурирование pgbouncer для использования transaction pooling
  2. Напишите функцию PL, которая создает PREPARE ed операторов
  3. Проверьте вашу функцию PLсистемное представление pg_prepared_statements и генерирует все подготовленные операторы, если таковые отсутствуют.
  4. Измените выполнение команды SQL на:
    1. BEGIN
    2. SELECT create_prepared_statements();
    3. /* Do whatever it is that you would normally do */
    4. COMMIT

Причина, по которой вам нужно позвонить ещеcreate_prepared_statements() Функция PL написана потому, что вы не знаете, к какому бэкэнду было подключено ваше соединение, или был ли бэкэнд, с которым вы разговариваете, недавно порожденным и не имеет PREPARE операторов ed.

В зависимости от того, как вы используете PREPARE 'ed-операторы, посмотрите на VIEW или PL-функции, так как они автоматически генерируют и кэшируют PREPARE' ed-операторы.Я бы предложил более агрессивно использовать функции PL / pgsql, так как это самый простой способ поддержки.

1 голос
/ 23 июня 2011

Пул транзакций

Чтобы заставить подготовленные операторы работать в этом режиме, потребуется PgBouncer для следите за ними внутри, чего он не делает. Так что единственный способ продолжать использовать PgBouncer в этом режиме, чтобы отключить подготовленные операторы полностью.

0 голосов
/ 24 июня 2011

Я не использую PDO, но использование подготовленных операторов с pgBouncer в режиме сеанса работает для меня. Мне просто нужно установить "server_reset_query = DISCARD ALL" для правильной работы предварительно подготовленных операторов. Можете ли вы установить pool_mode для сеанса, а также установить вышеупомянутую переменную?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...