2 votes

Obtenir une valeur actualisée en utilisant le verrou ?

C'est ma première Procédure.

define frame LockFrame Customer.Name Customer.CreditLimit Customer.Balance.
pause.

DO TRANSACTION:
    for each Customer exclusive-lock:
    assign Customer.CreditLimit = Customer.CreditLimit + 5.
    pause 1 no-message.
    display Customer.Name Customer.CreditLimit Customer.Balance.
    end.
end.

et c'est ma deuxième procédure.

define frame LockFrame Customer.Name Customer.CreditLimit Customer.Balance.
pause.

DO TRANSACTION:
    for each Customer exclusive-lock:
    assign Customer.Balance= Customer.Balance + 2.
    pause 1 no-message.
    display Customer.Name Customer.CreditLimit Customer.Balance.
    end.
end.

Lorsque j'exécute la première procédure et juste après la seconde, je dois récupérer la valeur mise à jour par la première (ici CrditLimit) (et vice versa). Mais je ne suis pas en mesure d'exécuter la seconde procédure car l'enregistrement est verrouillé par la première procédure, et un message d'erreur s'affiche. Je pense que le problème se situe au niveau du verrouillage.

4voto

Tom Bascom Points 3827

C'est exactement ce à quoi vous devez vous attendre.

L'enregistrement est verrouillé, exclusivement au profit de la première procédure, jusqu'à ce que la transaction soit validée. La validation aura lieu à la deuxième instruction END.

Je ne suis pas sûr de la raison pour laquelle vous avez PAUSE là-dedans - vous ne devriez jamais bloquer sur IO à l'intérieur d'un bloc de transaction - cela ne fera que conduire à des problèmes (comme des utilisateurs qui se bloquent l'un l'autre pendant qu'ils se lèvent et vont chercher du café...).

Il n'est pratiquement JAMAIS souhaitable d'inclure la mise à jour d'une table entière dans une transaction (ce que fait le bloc DO TRANSACTION). Même si vous pensez que c'est ce que vous voulez faire, ou si on vous a dit que c'est ce que vous devez faire, c'est probablement faux. C'est généralement le résultat de la confusion entre une "transaction commerciale" et une "transaction de base de données" - ce n'est pas la même chose - surtout lorsque de grandes quantités de données sont en jeu.

Une meilleure façon d'écrire votre code (appliquer le même concept aux deux échantillons) :

define buffer updCustomer for customer.

for each customer no-lock /* where whatever */:

  /* maybe some no-lock logic... */

  do for updCustomer transaction:

    find updCustomer exclusive-lock where recid( updCustomer ) = recid( customer ).
    assign
      updCustomer.creditLimit = customer.creditLimit + 5.
    .

    display
      updCustomer.name
      updCustomer.creditLimit
      updCustomer.balance
    .

  end.

  pause 1 no-message.

end.

L'utilisation de NO-LOCK sur le FOR EACH permet à la logique de sélection ou à d'autres logiques de s'exécuter sans avoir besoin du verrou. L'utilisation du tampon de mise à jour et du bloc DO FOR ... TRANSACTION, le verrouillage de l'enregistrement et la transaction sont étroitement liés à ce seul bloc. Le fait de placer le PAUSE en dehors du bloc permet d'éviter le problème "l'utilisateur va chercher du café".

0voto

Paul Krikke Points 86

J'hésite à commenter les réponses données par Tom, car je le considère comme faisant autorité. Mais je tiens à souligner deux petites choses.

Tout d'abord, l'utilisation de RECID pourrait être mieux utilisée comme ROWID. L'utilisation de ROWID est recommandée par Progress (voir http://documentation.progress.com/output/OpenEdge102a/oe102ahtml/wwhelp/wwhimpl/common/html/wwhelp.htm?context=dvref&file=dvref-15-48.html ) puisque la fonction RECID est "prise en charge pour des raisons de compatibilité ascendante. Pour la plupart des applications, utilisez plutôt la fonction ROWID".

Mais c'est mineur, vraiment. Ce qui est également important à mon avis, c'est ce que Tom a fait dans son exemple pour vous - il a défini un tampon ("define buffer updCustomer for customer.") qu'il a utilisé dans la mise à jour. Je vous encourage à utiliser des tampons CHAQUE fois que vous travaillez avec un enregistrement, en particulier si vous utilisez des procédures persistantes ou des super procédures, ou si vous utilisez des fonctions ou des procédures internes.

Pourquoi ? La définition d'un tampon garantit que la portée du tampon que vous mettez à jour est limitée à l'endroit où vous l'avez défini. Par exemple, Progress fera "fuir" le tampon par défaut dans votre super procédure si vous ne faites pas attention. Imaginez ce scénario... un programme qui trouve un enregistrement, appelle une fonction dans une super procédure pour faire "certaines choses", puis supprime l'enregistrement.

FIND MyTable WHERE MyTable.fk = fkValue NO-LOCK NO-ERROR.
UpdateOtherStuff(MyTable.fkValue).
DeleteMyRecord(MyTable.fkValue).

Mais dans "UpdateOtherStuff", il fait quelques travaux dont celui-ci...

FOR EACH MyTable:
    If MyTable.Thing = 'ThingOne' THEN LEAVE.
    /* other processing here... */
END.

Vous pourriez être surpris de constater que la super procédure partage le tampon par défaut "MyTable" avec votre programme, et finit par repositionner l'enregistrement à un endroit que vous ne voulez pas... de sorte que l'appel à "DeleteMyRecord()" a un enregistrement différent de celui que vous attendez.

Le problème serait résolu si "UpdateOtherStuff" avait un "DEFINE BUFFER ... FOR MyTable" en haut, même si c'était "DEFINE BUFFER MyTAble for MyTable" (aussi étrange que cela puisse paraître...).

C'est pourquoi l'exemple de Tom, qui comprend un DEFINE BUFFER..., devrait servir de modèle pour le travail que vous faites dans l'ABL.

Cette question a été posée précédemment - voir https://stackoverflow.com/a/5490130/1433147 .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X