2011-06-23 2 views
2

Je lance un site web Drupal 7.2 intégrant a flash game avec quelques scripts PHP personnalisés pour les statistiques des joueurs. En utilisant CentOS 5.6/64 bits, PostgreSQL 8.4.8 et PHP 5.3. C'est un Quad-Opteron avec 4 Go de RAM.PHP et pgbouncer en mode transaction: la transaction en cours est annulée

Aux heures de pointe (quand il y a environ 500 joueurs en ligne), mon site web était utilisé avec trop de processus postmaster. Sur les conseils de pgsql-general mailing list J'ai installé pgbouncer 1.3.4 avec le /etc/pgbouncer.ini suivant:

[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 

et ont augmenté shared_buffers = 1024MB et une diminution max_connections = 50 dans postgresql.conf.

Cela a aidé, mais j'ai souvent un problème qu'une déclaration PDO préparée ne sera pas trouvée:

SQLSTATE[26000]: Invalid sql statement name: 7 ERROR: prepared statement "pdo_stmt_00000016" does not exist 
  • probablement parce que pgbouncer commute la connexion entre un prepare() et execute().

Je ne peux pas basculer pgbouncer en mode session - mon site Web va se bloquer.

J'ai essayé d'ajouter PDO :: ATTR_EMULATE_PREPARES => true - mon site web se bloque aussi.

J'ai ajouté beginTransaction() et Commit() autour de chaque prepare() et exécuter() appel - mais je reçois le plus souvent erreur suivant:

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

Ci-dessous un extrait de mon code à défaut avec cette erreur - il est très simple et appelle à seulement cinq instructions 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; 
} 

Toute aide s'il vous plaît? Alex

Répondre

0

Je n'utilise pas PDO, mais utiliser des instructions préparées avec pgBouncer en mode session fonctionne pour moi. J'ai juste besoin de mettre "server_reset_query = DISCARD ALL" pour que les instructions preapred fonctionnent correctement. Pouvez-vous définir pool_mode à la session et également définir la variable mentionnée ci-dessus?

+0

Bonjour, c'est exactement ce que j'ai fait hier et ça semble bien fonctionner sofar (avec PDO, mais sans beginTransaction/commit) –

1

Transaction mise en commun

Pour faire des déclarations préparées fonctionnent dans ce mode aurait besoin pgbouncer pour garder une trace de l'intérieur, qu'il ne fait pas. Donc, seul moyen de continuer à utiliser PgBouncer dans ce mode est de désactiver complètement les instructions préparées .

+0

URL vers les documents pour la mise en commun des transactions: http://pgbouncer.projects.postgresql.org/doc/usage.html#_description @jordani est tout à fait correct, cependant, utilisez le pooling des transactions. – Sean

+0

comment puis-je désactiver les instructions préparées dans mon script? –

+0

@Sean, désolé - mais je ne comprends pas votre commentaire "cependant, utilisez la mise en commun des transactions". Je le fais déjà. –

1
  1. Configurer pgbouncer à utiliser transaction pooling
  2. Ecrire une fonction PL qui crée vos PREPARE déclarations ées
  3. Demandez à votre fonction PL vérifier la vue système pg_prepared_statements et générer toutes vos déclarations préparées s'il en manque.
  4. Changer l'exécution de votre commande SQL être:
    1. BEGIN
    2. SELECT create_prepared_statements();
    3. /* Do whatever it is that you would normally do */
    4. COMMIT

La raison pour laquelle vous avez besoin d'appeler ce encore à BE- écrit create_prepared_statements() La fonction PL est parce que vous ne savez pas le backend auquel votre connexion est envoyée ou si le backend auquel vous parlez a été créé récemment et n'a aucune instruction PREPARE ed. En fonction de la façon dont vous utilisez vos instructions PREPARE 'ed, regardez les fonctions VIEW ou PL car elles génèrent et mettent automatiquement en cache les instructions PREPARE' ed. Je suggère de faire une utilisation plus agressive des fonctions PL/pgsql, mais puisque c'est la méthode la plus facile à maintenir.

Questions connexes