2013-04-29 1 views
2

Je travaille sur une fonction PL/pgSQL "interpolate_values" avec des calculs qui prennent du temps. Une table appelée « interpolation_jobs » contient des informations de surveillance au sujet de chaque appel de fonction, de sorte que la progression d'un appel de fonction avec une job_id donnée doit être observable parMise à jour immédiate des fonctions PL/pgSQL

SELECT status FROM interpolation_jobs WHERE id = job_id; 

La colonne « statut » contient l'une des valeurs « mis en attente » , "en cours d'exécution" ou "fait". Au début de la fonction, le statut passe de « mis en attente » à « courir », à la fin, il est réglé sur « fait »:

CREATE OR REPLACE FUNCTION interpolate_values (job_id INTEGER) 
RETURNS VOID 
LANGUAGE plpgsql VOLATILE 
AS $$ 
DECLARE 
BEGIN 
    EXECUTE 'UPDATE interpolation_jobs 
     SET status = ''running'', progress = 0.0 
     WHERE id = ' || job_id || ';'; 

-- 
-- ... some extensive database computations ... 
-- 

    EXECUTE 'UPDATE interpolation_jobs 
     SET status = ''done'' 
     WHERE id = ' || job_id || ';'; 

END; 
$$; 

Mon problème est que le statut n'est pas mis à jour au cours de la fonction appel. Les mises à jour ont effectivement lieu lorsque l'appel de fonction retourne. Ainsi, le statut passe directement de «en attente» à «terminé». Les lignes

EXECUTE 'UPDATE interpolation_jobs 
     SET status = ''running'', progress = 0.0 
     WHERE id = ' || job_id || ';'; 

n'ont aucun effet.

Y a-t-il une possibilité de mettre à jour les valeurs immédiatement dans PL/pgSQL, de sorte que les nouvelles valeurs soient accessibles avant que l'appel de fonction ne retourne?

Merci!

EDIT:

Merci pour toutes vos réponses qui m'a beaucoup aidé à comprendre les problèmes généraux des opérations de base de données asynchrones. L'approche de dblink fonctionne pour moi. Il est nécessaire de spécifier IP/Port/utilisateur si la même base de données est utilisée:

SELECT * FROM current_database() INTO _db_name; 
PERFORM dblink_connect('dbname=' || _db_name); 
PERFORM dblink_exec('UPDATE interpolation_jobs SET status = ''running'' WHERE id =' || _job_id); 

-- 
-- ... some extensive database computations ... 
-- 

PERFORM dblink_exec('UPDATE interpolation_jobs SET status = ''done'' WHERE id =' || _job_id); 
PERFORM dblink_disconnect(); 
+0

En bref: non. Les mises à jour de la table d'état font partie de la transaction et ne peuvent être vues que par d'autres processus une fois la transaction validée. Vous auriez besoin d'une notification asynchrone pour accomplir ce que vous voulez. voir 'LISTEN' ou 'NOTIFY' dans le manuel. – wildplasser

Répondre

2

Vous pouvez utiliser dblink pour se connecter à votre base de données et exécuter une requête qui sera immiedietely commited:

CREATE OR REPLACE FUNCTION interpolate_values (_job_id INTEGER) 
RETURNS VOID 
LANGUAGE plpgsql VOLATILE 
AS $$ 
DECLARE 
_conn TEXT; 
_status TEXT; 
BEGIN 
    _conn:='hostaddr=127.0.0.1 port=5433 dbname=<db> user=<user> password=<pass>'; 
    _status='running'; 
    PERFORM dblink_exec(_conn,'UPDATE interpolation_jobs SET status = '''||_status||''', progress = 0.0 WHERE id ='||_job_id); 
    PERFORM pg_sleep(10); --simulate some time consuming calculations 
    _status='finished'; 
    PERFORM dblink_exec(_conn,'UPDATE interpolation_jobs SET status = '''||_status||''', progress = 100.0 WHERE id ='||_job_id); 
END; 
$$; 
+0

Merci. Je pensais avoir mentionné le dblink, mais en vérifiant ma réponse je vois que j'ai oublié. Oups. –

2

Ce que vous semblez vouloir est une lecture ou d'écriture sale sale. Ils ne sont pas disponibles dans PostgreSQL et ne seront probablement jamais supportés. Un parent proche est une transaction autonome. Encore une fois, ceux-ci ne sont pas supportés par PostgreSQL mais le travail pour les ajouter est en cours.

Vous constaterez que même avec des transactions autonomes, il est très difficile d'écrire votre propre système de files d'attente efficace. Ne descendez pas ce chemin, utilisez celui que quelqu'un a déjà écrit. Les systèmes de file d'attente sont durs pour bien écrire, en particulier contre un SGBDR. Les systèmes existants tels que ZeroMQ, RabbitMQ, PGQ, etc. peuvent être évalués en tant qu'alternatives.

Il y a eu une discussion récente sur l'ajout d'une fonctionnalité à SELECT ... FOR UPDATE qui permettrait à PostgreSQL d'ignorer les lignes verrouillées et de récupérer la première ligne qui n'est pas verrouillée. Cette fonctionnalité n'est pas encore disponible et ne sera pas disponible avant 9.4 au plus tôt, alors ne retenez pas votre souffle.

Questions connexes