Messieurs,
Je me suis abonné à cette liste de dev sur le conseil de Nicolas suite à
une question que j'avais sur ce problème de lock des articles.
Il faut mixer des locks et un timestamp d'après moi, mais je ne suis
pas expert en accès concurrentiels ...
Je ne sais pas si je peux me donner le titre "d'expert" en la matière,
mais je vais faire un petit résumé de ce que doit être un verrou d'accès
concurentiel dans le cadre d'un outil comme spip.
Personne n'a jamais eu de problèmes d'écrasement de données à cause de
ce gros manque dans SPIP ???
Si, nécessairement, ce n'est qu'une question de probabilité...
On raisonne bien sûr sur le même article.
On dispose d'une table de trois colonnes :
lock_table : id_article, login, timelocked
PK=(id_article, login)
Quand quelqu'un clique sur "modifier cet article" ou chaque création
d'article, on essaie d'insérer la ligne :
insert into lock_table (l'identifiant courant, mon login, sysdate());
Si l'insert réussit, j'ai pris le verrou.
S'il ne réussit pas les cas suivants sont à envisager :
- erreur de base de donnée, tout le monde et dans les choux, exit.
- quelqu'un d'autre a déjà le verrou.
Le problème se pose alors de savoir si quelqu'un bosse *vraiment* sur
l'article ou si il a abandonné ses modifs.
Je met LOCK_TO en define de mettons 30 minutes.
On a deux moyens de le gérer :
1) tenter directement un update de la ligne avec la clause where :
update lock_table set login=mon_login where id_article=l'identifiant AND
timelocked < sysdate()-LOCK_TO)
Si l'update est OK, on est parti en time out, et j'ai pris le verrou.
Sinon, quelqu'un bosse dessus (ou la connexion base est plantée).
2) Faire un select count(1) from lock_table where id_article=xxx and
timelocked < sysdate()-LOCK_TO
Si j'ai un rang il y a une modification "active" de l'article.
Si j'ai plus d'un rang, erreur grave.
Si je n'ai aucun rang (time out de plus d'une demi heure), je *propose*
de prendre le verrou mais ce n'est pas obligatoire. Dans ce cas je
m'appelle avec un troisième paramètrede forçage de verrou et je fais
l'update de la méthode 1 sans clause where sur le timelocked.
Le retour peut être alors :
update OK (c'est bon, j'ai le verrou)
update ok (base plantée parce que cas impossible, mais à sortir dans un
log d'erreur).
Ma préférence va à la deuxième solution même si elle oblige le codage
d'un écran supplémentaire.
Quoi qu'il en soit, au bout d'un moment, j'ai mon verrou et je peux
tripoter l'article.
Bien entendu, tout ceci est mis au sein d'une fonction PHP genre
get_lock() qui renvoie un code d'erreur "kivabien" (verrou acquis, ok,
ou ko en cours de modif) et qui a entrée les deux paramètres identifiant
article et login et un troisième facultatif dans le cas de la solution
numéro 2 pour forcer ou non la prise de verrou.
Bien maintenant, la perte du verrou.
Quand on clique sur "valider" on ne revient pas au même écran avec
posibilité de re-valider de nouveau. Donc ça va nous simplifier la
tâche, parce que toute validation implique perte de verrou (quitte à le
reprendre deux secondes après en cliquant de nouveau sur modifier).
Cette fonction release_lock() admet en paramètre les mêmes que
get_lock() à savoir l'id de l'article et le login.
On fait simplement un delete from lock_table where id_article=xxx and
login=yyy
Si le delete réussit, OK, on avait encore le verrou et les modifs
peuvent être commitées en base.
Si le delete plante, quelqu'un a pris le verrou entre temps, il ne FAUT
PAS prendre en compte les modifs (et voir si on augmente ou non le temps
LOCK_TO) (ou alors on a perdu la connexion à la base et de toutes façons
au revoir)
On peut ensuite voir à faire un système de merge des modifs, mais là ça
devient compliqué.
Sur ce, je pense que je vous ai donné quasiment les lignes de code à
écrire mais je suis tout à fait dispo pour aider au codage de ces deux
fonctions, relire le code produit, etc...
a++; JG
(PS : tu vois Nico, deux chtites fonctions de rien du tout et c'est
torché).