Atomicité lors de la création

0 views
Skip to first unread message

Guillaume Betous

unread,
Feb 4, 2009, 2:52:04 AM2/4/09
to rails...@googlegroups.com
Bonjour, j'ai 2 modèles qui, au niveau métier sont liés de la manière suivante :

- Une classe AR "production" contient entre autre une info de stock (dans le sens quantité)
- Une classe AR "mission" puise dans ce stock pour apporter (camion) de la marchandise vers une autre ville
- Une mission n'est possible que si le stock correspondant est strictement positif.

Jusqu'à présent tout se passe dans la classe "mission" :
- un "validate_on_create" s'assure que le stock est suffisant
- un "after_create" décrémente le stock de la valeur correspondant à la mission

Mon soucis est de rendre ça atomique : lors d'un accès concurrent au meme stock, je ne veux pas que 2 personnes pensent qu'il y a du stock, que les 2 décrémentes, et que finalement le stock devienne négatif vu qu'en fait il n'y en avait pas pour les 2.

J'ai bien vu les transactions[1], mais je ne sais pas comment faire pour la mettre dans le modèle, vu que ça touche des actions avant et après la création.

Merci (-:

gUI

[1] http://api.rubyonrails.com/classes/ActiveRecord/Transactions/ClassMethods.html
--
Pour la santé de votre ordinateur, préférez les logiciels libres.
Lire son mail : http://www.mozilla-europe.org/fr/products/thunderbird/
Browser le web : http://www.mozilla-europe.org/fr/products/firefox/
Suite bureautique : http://fr.openoffice.org/

Fabien Jakimowicz

unread,
Feb 4, 2009, 4:32:38 AM2/4/09
to rails...@googlegroups.com
2009/2/4 Guillaume Betous <guillaum...@gmail.com>:

> Bonjour, j'ai 2 modèles qui, au niveau métier sont liés de la manière
> suivante :
>
> - Une classe AR "production" contient entre autre une info de stock (dans le
> sens quantité)
> - Une classe AR "mission" puise dans ce stock pour apporter (camion) de la
> marchandise vers une autre ville
> - Une mission n'est possible que si le stock correspondant est strictement
> positif.
>
> Jusqu'à présent tout se passe dans la classe "mission" :
> - un "validate_on_create" s'assure que le stock est suffisant
> - un "after_create" décrémente le stock de la valeur correspondant à la
> mission
>
> Mon soucis est de rendre ça atomique : lors d'un accès concurrent au meme
> stock, je ne veux pas que 2 personnes pensent qu'il y a du stock, que les 2
> décrémentes, et que finalement le stock devienne négatif vu qu'en fait il
> n'y en avait pas pour les 2.
>
> J'ai bien vu les transactions[1], mais je ne sais pas comment faire pour la
> mettre dans le modèle, vu que ça touche des actions avant et après la
> création.
>

Je pense que pour être vraiment sur du résultat, tu devrais utiliser
des contraintes sur ta base de données directement.

--
http://fabien.jakimowicz.com

Michel Belleville

unread,
Feb 4, 2009, 4:40:24 AM2/4/09
to rails...@googlegroups.com
Sinon, tu peux faire une méthode qui décore create en mettant une transaction autour, du genre :

class Mission < AR::B
...
def create_according_to_stock(*args, &block)
self.transaction do
self.create(args, block)
end
...
end

Et dans ton contrôleur, tu appelles ta méthode décorante à la place du create normal.

Michel Belleville


2009/2/4 Fabien Jakimowicz <fab...@jakimowicz.com>

Michel Belleville

unread,
Feb 4, 2009, 4:41:17 AM2/4/09
to rails...@googlegroups.com
(bon je m'apercois en me relistant que j'ai oublié des indentations et des 'end' mais le principe reste valable)

Michel Belleville


2009/2/4 Michel Belleville <michel.b...@gmail.com>

Guillaume Betous

unread,
Feb 4, 2009, 4:46:57 AM2/4/09
to rails...@googlegroups.com
oui, c'est pas mal comme solution.

je vais tenter ça. merci !

gUI

Tony Chauveau

unread,
Feb 4, 2009, 1:45:03 PM2/4/09
to rails...@googlegroups.com
Peut-être celà serait plus sur avec un lock?

http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

Car là, avec une transaction, 2 utilisateurs peuvent passer si leur
validation se fait en même temps (la 1ère requête de la transaction
passe et la seconde n'a aucune contrainte pour ne pas passer)
--
Posted via http://www.ruby-forum.com/.

Guillaume Betous

unread,
Feb 4, 2009, 2:37:11 PM2/4/09
to rails...@googlegroups.com
J'avais lu ça, mais apparemment c'est réellement pour les cas extrêmes. Le mien est un classique, et s'approche de très près du cas d'école du transfert d'argent entre 2 comptes.

gUI

Fabien Jakimowicz

unread,
Feb 4, 2009, 2:42:43 PM2/4/09
to rails...@googlegroups.com
2009/2/4 Guillaume Betous <guillaum...@gmail.com>:

> J'avais lu ça, mais apparemment c'est réellement pour les cas extrêmes. Le
> mien est un classique, et s'approche de très près du cas d'école du
> transfert d'argent entre 2 comptes.
>

C'est un cas qui peut se produire bien plus souvent qu'on ne le pense
: il suffit d'avoir la malchance de 2 utilisateurs qui font au même
moment (proche de la milliseconde) la même action ...

Quitte à faire un lock, une contrainte sur la base/table est encore
plus sage : cela ne bloque pas la base de données comme un lock et ca
laisse la base gérer la fiabilité des données.

--
http://fabien.jakimowicz.com

Renaud Chaput

unread,
Feb 4, 2009, 2:55:26 PM2/4/09
to rails...@googlegroups.com
Fabien Jakimowicz a écrit :

> Quitte à faire un lock, une contrainte sur la base/table est encore
> plus sage : cela ne bloque pas la base de données comme un lock et ca
> laisse la base gérer la fiabilité des données.
Sauf que la philosophie de rails est de ne rien faire en DB et de tout
gérer dans le modèle ou via l'ORM, dans l'application.
Je ne suis pas trop d'accord avec cette idée, et préfèrerais un moyen
d'implémenter des contraintes (FK, ...) au niveau de la DB si celle ci
la gère, mais cela n'est pas du tout prévu (un choix de conception de
DHH, qui a été discuté).

renchap

Fabien Jakimowicz

unread,
Feb 4, 2009, 3:01:35 PM2/4/09
to rails...@googlegroups.com
2009/2/4 Renaud Chaput <ren...@cocoa-x.com>:

en effet, mais les index uniques (qui sont implémentés dans rails)
contredisent totalement cette logique rails.

De plus, tout n'est pas possible depuis les modèles ;)

--
http://fabien.jakimowicz.com

Tony Chauveau

unread,
Feb 4, 2009, 3:02:50 PM2/4/09
to rails...@googlegroups.com

C'est un choix qui se comprend si l'on souhaite réellement ne pas
dépendre de la base de donnée choisie. Mais celà ne doit pas non plus
empêcher toute optimisation dans le cas d'un projet où le choix de la
base est connu et pérenne

Guillaume Betous

unread,
Feb 4, 2009, 4:56:23 PM2/4/09
to rails...@googlegroups.com
C'est un cas qui peut se produire bien plus souvent qu'on ne le pense
: il suffit d'avoir la malchance de 2 utilisateurs qui font au même
moment (proche de la milliseconde) la même action ...

Je sais, c'est pour ça que je m'en inquiète, mais la transaction Rails sert à ça non ?

gUI

Tony Chauveau

unread,
Feb 4, 2009, 5:35:15 PM2/4/09
to rails...@googlegroups.com

>
> Je sais, c'est pour ça que je m'en inquiète, mais la transaction Rails
> sert
> à ça non ?
>

C'est pas exactement le même problème. La transaction permet d'enchainer
plusieurs requêtes en annulant tout si l'une d'entre elles échoue.
Le lock interdit l'accès en écriture à une table (ou juste une ligne de
cette table) tant qu'on a pas rendu la main.

Guillaume Betous

unread,
Feb 5, 2009, 1:26:07 AM2/5/09
to rails...@googlegroups.com
C'est pas exactement le même problème. La transaction permet d'enchainer
plusieurs requêtes en annulant tout si l'une d'entre elles échoue.
Le lock interdit l'accès en écriture à une table (ou juste une ligne de
cette table) tant qu'on a pas rendu la main.

Ah bin j'avais rien compris (-:

Donc oui en effet j'ai bien besoin d'un lock.

Michel Belleville

unread,
Feb 5, 2009, 1:26:50 AM2/5/09
to rails...@googlegroups.com
En fait, à ta place je ferais les deux approches à la fois. Le blocage de l'objet t'assure la non-concurrence des requêtes (le script est le seul à passer à la fois), et la transaction t'assure l'atomicité (tout passe à la fois ou rien ne passe du tout).
Michel Belleville


2009/2/4 Tony Chauveau <list-i...@andreas-s.net>

Guillaume Betous

unread,
Feb 5, 2009, 2:15:30 AM2/5/09
to rails...@googlegroups.com
Je peux toujours essayer, ne serait-ce que pour l'exercice.

Mais cela dit, je me suis trompé. En y réfléchissant bien, si j'ai la non concurrence, j'ai pas besoin de l'atomicité.

Et au passage j'ajouterais que il me semble que "atomicité" est ici impropre : l'atomicité garantissant normalement la non-concurrence (en tous cas dans le cadre des instructions processeur), puisque dans opération atomique on parle de operation non interruptible.

gUI

Michel Belleville

unread,
Feb 5, 2009, 4:03:08 AM2/5/09
to rails...@googlegroups.com
Disons, dans ce cas-là c'est un peu toi qui assure l'atomicité en vérifiant que le compte y est et que tu ne décrémente que si le nouvel objet est validé. Si tu ne faisais pas ça, le lock ne serait pas suffisant.

Michel Belleville


2009/2/5 Guillaume Betous <guillaum...@gmail.com>
Reply all
Reply to author
Forward
0 new messages