2016-01-26 17 views
17

Ich habe kürzlich ein Problem bei einer Live-Anwendung festgestellt. Ich erkannte, dass ich mehr und mehr Nebenläufigkeit Ausnahmen und Sperren mit einer Datenbank hatte.Wie testet man simultanes Lesen/Schreiben mit PHPUnit?

Im Grunde beginne ich eine Transaktion, die eine SELECT und eine INSERT auf der gleichen Tabelle zu verpflichten benötigt.

Aber weil die Last wirklich schwer ist, sperrt jede Transaktion die Tabelle, in den meisten Fällen ist es so schnell, dass es keine Probleme verursacht, aber es gibt einen Punkt, an dem die Sperren mehr und mehr warten.

Ich konnte dieses Problem etwas beheben, indem ich die Abfragen optimierte.

Nun möchte ich jedoch einige Tests mit PHPUnit schreiben, um meinen Fix zu validieren und Regressionen zu vermeiden.

Ich konnte keine Materialien finden, wie dies zu tun ist.

Da PHP nicht multi-threaded ist, habe ich keine Ideen, wie ich gleichzeitige Abfragen in einem einzigen Test zur Validierung ausführen könnte.

Grundsätzlich möchte ich mehrere Anrufe in einem einzigen Test ausführen können, um sicherzustellen, dass alles in Ordnung ist.

Ich weiß, ich könnte versuchen, einige High-Level-Tests durch direkte Abfrage der http-Server und laden Sie die gesamte Anwendung, aber da mein Problem kommt aus einer eigenständigen Bibliothek möchte ich lieber es gegen sich testen.

Irgendwelche Ideen?

+0

Mit der gleichen Frage – XuDing

+0

Sie können nicht regelmäßig Nebenläufigkeit testen. Die einzigen Dinge, die Sie tun können, ist a) Stresstest und b) die Durchsetzung von Bedingungen, bei denen Sie denken, dass eine Anomalie passieren sollte (falls dies möglich ist). Grundsätzlich sollten Sie die Datenbank überhaupt nicht testen, Sie sollten sicherstellen, dass in White Papers angegeben wird, dass Ihre Algorithmen sicher sind und, wenn Sie extrem sicher sein möchten, spezifische Stresstests auf der höchsten Teststufe hinzufügen (außerhalb der Anwendung). Um die spezifische Frage zu beantworten, können Sie immer multiple Prozesse verwenden, um gleichzeitige Operationen auszuführen, so dass manuelle Verzweigungen oder Bibliotheken wie kirswswallsmith/spork helfen sollten. – Etki

Antwort

5

Die kurze Antwort ist, es gibt keine gute Möglichkeit, gleichzeitige Lese-/Schreibvorgänge auf einer tatsächlichen Datenbank mit PHPUnit zu testen. Es ist einfach nicht das richtige Werkzeug für diesen Job.

Aber es gibt ein paar Facetten für eine gute Lösung, um dies zu testen. Zuerst kann der Code geschrieben werden, um jedes mögliche Szenario zu behandeln. Ein Datenbanksystem wie Postgres schlägt bei Sperren und Transaktionsproblemen sofort fehl. So behandeln Sie es elegant verwende ich Code, der etwa so aussieht (Pseudo-Code, verwendet auch another question antworten):

begin transaction 
while not successful and count < 5 
    try 
     execute sql 
     commit 
    except 
     if error code is '40P01' or '55P03' 
      # Deadlock or lock not available 
      sleep a random time (200 ms to 1 sec) * number of retries 
     else if error code is '40001' or '25P02' 
      # "In failed sql transaction" or serialized transaction failure 
      rollback 
      sleep a random time (200 ms to 1 sec) * number of retries 
      begin transaction 
     else if error message is 'There is no active transaction' 
      sleep a random time (200 ms to 1 sec) * number of retries 
      begin transaction 
    increment count 

Dann werden zwei Sätze von Tests erstellen: ein Satz sollte den Code bestätigen, die Situationen, Umgang mit richtig (dh Unit-Tests). Der andere Satz von Tests betrifft die Umgebung (d. H. Integrations-/Funktionstests).

Unit Tests

ich diese finde eine harte Situation zu sein, in einem PHPUnit Test zu reproduzieren, die mit einer Datenbank verbunden ist, und eine echte Datenbank ist für einen echten Unit-Test nicht geeignet. Erstellen Sie stattdessen PDO-Stubs und Unit-Tests, die jede Art von Datenbankausnahme auslösen. Dies bestätigt der Code funktioniert wie erwartet, aber testet nicht Parallelität auf einer wirklichen Datenbank, z.B .:

$iterationCount = 0; 
$db->runInTransaction(function() use (&$iterationCount) { 
    $iterationCount++; 
    if ($iterationCount === 1) { 
     $exception = new PDOExceptionStub('Deadlock'); 
     $exception->setCode('40P01'); 
     throw $exception; 
    } 
}); 

// First time fails, second time succeeds 
$this->assertEquals(2, $iterationCount, 'Expected 2 iterations of runInTransaction'); 

eine komplette Suite von Tests schreiben, die an die DB nicht anschließen, aber Logik bestätigen.

Integrationstests

Wie Sie gefunden haben, PHPUnit ist einfach nicht das richtige Werkzeug ein Lasttest durchzuführen.Es eignet sich nicht für komplexere Tests als sequenzielle Einheiten- und Integrationstests. Sie können mehrere Instanzen von PHPUnit gleichzeitig ausführen, um die Datenbank stärker zu belasten. Ich finde jedoch, dass dies über das hinausgeht, wofür es gedacht war, und es hilft Ihnen auch nicht, die Datenbank auf Probleme zu überwachen. Daher sehe ich keinen Weg um die höheren Tests, die Sie vermeiden möchten.

Aber Ihre Bibliothek kann getestet werden, ohne Ihre vollständige Anwendung auszuführen. Ich würde die einfachste mögliche Anwendung erstellen, nur um sie zu testen. Es kann ein oder mehrere CLI-Skripts enthalten, die eine Verbindung zu einer Datenbank herstellen. Diese Skripts können mehrfach erstellt werden, um die Datenbank zu belasten. Wenn es hilft, können Sie damit auch eine App mit zusätzlichem Code zum Überwachen der Datenbank verwenden.

+0

danke das hat mir sehr geholfen. –