Hallo Klaus,
Schau Dir an, wie die betreffenden Knoten aussehen:
das habe ich gemacht und das Ergebnis war enttäuschend.
das Ergebnis habe ich in etwa erwartet.
payload lft rgt
4303 1318 1319
5290 1318 1319
Verschaffe Dir einen schnellen Überblick, ob dieses Problem mehrfach vorliegt:
-- suche lft-Werte, die mehrfach auftreten - was ja ein Fehler ist.
SELECT
COUNT(lft)
FROM
000node
HAVING COUNT(lft) > 1
Eintrag:
viele Nested-Set-Tutorials sehen sich sehr ähnlich und gehen oberflächlich über das Eintragen hinweg. Sie verwenden zwar Locking, aber kein ausreichendes Locking und bauen so ein Time-of-check-to-time-of-use-Problem.
Hier ermittelst Du Werte, die du anschließend verwendest:
Die root_id ist kein Problem, denn ID-Werte ändert man ja nicht.
lft and rgt werden durch Deine Anwendung sehr wohl verändert - und genau
aus diesem Grund musst Du bereits diese Abfrage durch die Schreibsperre
absichern.
Stell Dir vor, parallel wird ein weiterer Eintrag vorgenommen, der ein
klein wenig früher an ist. Für das Eintragen wurde bereits ein WRITE
LOCK angefordert, aber die Sperre ist noch nicht in Kraft, weil folgende
Abfrage gerade noch ausgeführt wird.
$query_node_referenz="select root_id,lft,rgt FROM ".tableprefix."node WHERE payload ='$ReferenzID'";
$result_node_referenz=mysql_query($query_node_referenz);
if ($result_node_referenz==FALSE)
{
error(mysql_error(),'','',''); // ja, ich weiß, was nun kommt...
}
$row_node_referenz=mysql_fetch_row($result_node_referenz);
$V_ROOT_ID=$row_node_referenz[0];
$V_LFT=$row_node_referenz[1];
$V_RGT=$row_node_referenz[2];
Jetzt erhält die andere Session ihr WRITE LOCK und verändert die lft und
rgt-Werte auch des Knotens, den diese Session hier bearbeitet.
// Und jetzt der neue Eintrag
$result10=mysql_query("LOCK TABLES '".tableprefix."node' WRITE");
Diese Session muss warten, bis die andere Session ihre Sperren freigegeben
hat ... und arbeitet nun mit falschen rgt- und lft-Werten in den Variablen
$V_RGT und $V_LFT.
$query_update1="UPDATE ".tableprefix."node
SET lft = lft + 2
WHERE root_id = ".$V_ROOT_ID."
AND lft > ".$V_RGT."
AND rgt >= ".$V_RGT."";
$result_update1=mysql_query($query_update1);$query_update2="UPDATE ".tableprefix."node
SET rgt = rgt + 2
WHERE root_id = ".$V_ROOT_ID."
AND rgt >= ".$V_RGT."";
$result_update2=mysql_query($query_update2);$query2="
INSERT INTO ".tableprefix."node ( root_id, payload, lft, rgt )
VALUES ( $V_ROOT_ID, $MainID, $V_RGT, $V_RGT + 1 )";
$result12=mysql_query($query2);
$result13=mysql_query("UNLOCK TABLES");
> Nun der Fall, dass keine ReferenzID übergeben wurde:
auch dort liegt das gleiche Problem vor.
Freundliche Grüße
Vinzenz