Sep 15
Race Conditions, ein BG Problem (Flottenverdoppelung)
Ich werde nun nicht nochmal selbst Race Conditions erklären, wenn das schon sehr gut auf Wikipedia beschrieben ist. Link
Bei einem PHP Apache Server agieren ständig mehrere Threads, Scripte oder System (wie auch immer man sie nennen will) gleichzeitig. Jedes Mal wenn ein User einen Script aufruft (quasi immer wenn er irgendwo draufdrückt), wird ein neuer Thread für ihn gebildet, dieser läuft parallel zu den anderen.
Ein BG (Browsergame) wie UGamela hat – wie Viele gar nicht wissen – eine komplexe Spiellogik im Inneren, welche bei bisherigen Versionen nur hingemurkst wurde. Beispiel siehe Fusionskraftwerkbug.
Ein weiteres Problem stellt die insgesamt schlechte Programmierung und somit unnötig und zu lange Berechnungszeit der Scripte dar, je länger der zeitliche Abstand zwischen dem SQL Select Befehl und dem SQL Update Befehl, desto höher die Wahrscheinlichkeit, dass es zu einem Race Condition bedingten Fehler kommt.
Häufigster bekannter Fehler dieser Art ist das Problem der Flottenverdoppelung!
Problemlösung ist in diesem Fall wirklich nicht so einfach, vor allem wenn man nicht noch längere Berechnungszeiten und somit irgendwann Wartezeiten für die User riskieren will.
MySQL stellt dafür einen Befehl bereit, der eine ganze Tabelle sperrt („LOCK TABLE `xx`“) – anstehende Queries warten. Für die ganze Tabelle!! Dh letztendlich für alle Threads, dabei ist gar nicht klar, ob sich die Queries überhaupt überschneiden. Bei größeren Userzahlen gleichzeitig wird das dann wirklich spaßig, wenn die Wartezeiten immer länger werden.
Interessanter wäre es doch die einzelnen Einträge zu Sperren. Doch dies ist mit einigem Aufwand verbunden, weil ich aus Performancegründen benötigte Daten in einem Rutsch laden will (zB die Planeten des Spielers gleichzeitig). Dennoch soll es tief im Grundgerüst verankert werden, sodass letztlich der Modder keine Ahnung davon haben muss. Er schreibt höchstens sein spezielles „WHERE“ im SQL Select Befehl.
Hier ein kleiner Einblick:
PREPARE wtnu FROM ‚SELECT `used` INTO @used FROM `?pre“.$table.“` WHERE „.$where.“ ‚
oder PREPARE wtnu FROM ‚SELECT SUM(`used`) AS @used FROM `?pre“.$table.“` WHERE „.$where.“ ‚CREATE PROCEDURE wtnu ()
COMMENT ‚wait till not used‘
BEGIN
wloop: LOOP
EXECUTE wtnu;
IF @used <= 0 THEN
LEAVE wloop;
END IF;
SLEEP(0.3);
ITERATE wloop;
END LOOP wloop;
END|
1. Query vorbereiten (kommt auf ein oder mehrere Einträge an)
2. Die gezeigt Prozedur aufrufen:
2.1. Vorbereitetes Query aufrufen
2.2. Testen, ob der/die Eintrag/Einträge in Benutzung „used“ sind.
– wenn ja, warten.. die Schleife beginnt bei 2.1 von neuem.
– wenn nein verlasse die Schleife
Ich muss noch etwas daran feilen, ich weiß noch nicht wie ich das Session Caching miteinbinde und ob die Abfrage für mehrere „used“ schon das Wahre ist (ein SUM [Summe] Befehl überprüft ob die Einträge zusammen nicht mehr als 0 sind, „used“ kann 0 oder 1 sein).
September 16th, 2008 at 01:02
Interessant 🙂
September 16th, 2008 at 09:34
Hehe, da schliesse ich mich an. Du könntest mir auch Autos verkaufen und ich würde unterschreiben!
September 16th, 2008 at 17:20
Lol. Danke, aber ich denke das ist eher weniger mein Gebiet xD ;D
Bin grad voll happy, dass ich endlich im Google Index bin ;D meeeehr Besucher ^^
September 21st, 2008 at 22:22
[…] Die Erste – und wohl beste Methode – wäre es aus komplexeren Problemen, wie den Flotten, Events – kleine Jobs für einen Daemon – zu machen. Daemon bedeutet ein Script, fast eigenständiges Programm, das permanent läuft und Ereignisse dann abarbeitet wenn sie Geschehen. Diese Möglichkeit wäre eindeutig einfacher, wobei man nurnoch auf sehr seltene Überscheidungen achten müsste (zwei Events zum gleichen Zeitpunkt mit überschneidenden betroffenen Objekten), aber selbst das wäre kein Problem, das es ja nur eine Daemon Instanz gibt, also keine Race Conditions. […]
Oktober 26th, 2008 at 23:09
Das geht mit select … for update um einiges effizienter
Oktober 27th, 2008 at 19:10
Ah Danke für den Tipp, hab ich noch nicht gesehn im Handbuch…
Ich finde es fehlen sowieso einige MySQL Tutorials, um mehr Effizienz rauszuholen. Grade das Zeugs mit den SubQueries ist ja auch wirklich nicht leicht zu verstehen
April 14th, 2009 at 18:11
sehr interressant.
schreibe selbst ein bg, und das hier hat mir durchaus geholfen
Mai 5th, 2009 at 00:21
select …. for update funktioniert lediglich mit InnoDB, da InnoDB im gegensatz zu MyIsam zeilensperren unterstützt.