Sep 15

Race Conditions, ein BG Problem (Flottenverdoppelung)

Tag: EntwicklungPhoscur @ 18:43

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).

8 Kommentare zu “Race Conditions, ein BG Problem (Flottenverdoppelung)”

  1. DGLDevil schrieb:

    Interessant :)

  2. Testos schrieb:

    Hehe, da schliesse ich mich an. Du könntest mir auch Autos verkaufen und ich würde unterschreiben!

  3. Phoscur schrieb:

    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 ^^

  4. Eventhandler | UGamela Blog schrieb:

    […] 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. […]

  5. trottel schrieb:

    Das geht mit select … for update um einiges effizienter

  6. Phoscur schrieb:

    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

  7. style_ schrieb:

    sehr interressant.
    schreibe selbst ein bg, und das hier hat mir durchaus geholfen

  8. brian schrieb:

    select …. for update funktioniert lediglich mit InnoDB, da InnoDB im gegensatz zu MyIsam zeilensperren unterstützt.

Dein Kommentar

Du musst eingelogt sein, um einen Kommentar zu schreiben.