Frage

Wir haben eine asynchrone Aufgabe, die eine potenziell langlaufende Berechnung für ein Objekt durchführt. Das Ergebnis wird dann auf das Objekt zwischengespeichert. Um mehrere Aufgaben zu wiederholen, die gleiche Arbeit zu vermeiden, haben wir mit einem Atom SQL Update Sperren:

UPDATE objects SET locked = 1 WHERE id = 1234 AND locked = 0

Die Verriegelung ist nur für die asynchrone Aufgabe. Das Objekt selbst kann noch vom Benutzer aktualisiert werden. Wenn das passiert, sollte jede unvollendete Aufgabe für eine alte Version des Objekts seine Ergebnisse verwerfen, da sie out-of-date wahrscheinlich sind. Dies ist auch recht einfach mit einem Atom SQL-Update zu tun:

UPDATE objects SET results = '...' WHERE id = 1234 AND version = 1

Wenn das Objekt aktualisiert wurde, seine Version nicht übereinstimmt und so die Ergebnisse verworfen werden.

Diese beiden Atom-Updates sollten alle möglichen Rennbedingungen behandeln. Die Frage ist, wie diese Tests in der Einheit zu überprüfen.

Der erste Semaphore ist einfach zu testen, da es einfach eine Frage ist von der Einrichtung zwei verschiedene Tests mit den beiden möglichen Szenarien: (1), wo das Objekt gesperrt ist und (2), wo das Objekt ist nicht gesperrt. (Wir brauchen nicht auf die Unteilbarkeit der SQL-Abfrage zu testen, wie die in der Verantwortung des Datenbank-Anbieter sein sollte.)

Wie testet man die zweite Semaphore? Das Objekt muss von einem Dritten einige Zeit nach dem ersten Semaphor aber vor dem zweiten geändert werden. Dies würde eine Pause in der Ausführung erfordern, so dass das Update zuverlässig sein und konsequent durchgeführt, aber ich kenne keine Unterstützung für Stützpunkte mit RSpec injizieren. Gibt es eine Möglichkeit, dies zu tun? Oder gibt es eine andere Technik, die ich für die Simulation solcher Rennbedingungen bin mit Blick auf?

War es hilfreich?

Lösung

Sie können eine Idee aus der Elektronikfertigung borgen und Testhaken direkt in den Produktionscode setzen. So wie eine Leiterplatte kann mit speziellen Plätzen für Prüfgeräte hergestellt werden, um die Schaltung zu steuern und zu untersuchen, können wir die gleiche Sache mit dem Code tun.

Angenommen, wir haben einige Code eine Zeile in die Datenbank einfügen:

class TestSubject

  def insert_unless_exists
    if !row_exists?
      insert_row
    end
  end

end

Aber dieser Code auf mehreren Computern ausgeführt wird. Es gibt eine Race-Bedingung, dann, da andere Prozesse die Zeilen zwischen unserem Test und unserem Einsatz können einfügen, eine DuplicateKey Ausnahme verursacht. Wir wollen testen, ob unser Code die Ausnahme behandelt, die von dieser Race-Bedingung führt. Um das zu tun, muss unser Test die Zeile nach dem Aufruf einfügen row_exists? aber vor dem Aufruf von insert_row. Werfen wir also einen Test Haken hinzufügen genau dort:

class TestSubject

  def insert_unless_exists
    if !row_exists?
      before_insert_row_hook
      insert_row
    end
  end

  def before_insert_row_hook
  end

end

Wenn in der freien Lauf lassen, tut der Haken nichts außer essen ein klein wenig CPU-Zeit auf. Aber wenn der Code für die Race-Bedingung getestet, der Test Affen-Patches before_insert_row_hook:

class TestSubject
  def before_insert_row_hook
    insert_row
  end
end

Ist das nicht schlau? Wie eine parasitische Wespe Larve, die den Körper eines ahnungslose Raupe entführt hat, entführt der Test den im Test befindlichen Code so, dass sie den genauen Zustand schaffen wir getestet müssen.

Diese Idee ist so einfach wie die XOR-Cursor, so dass ich vermute, viele Programmierer haben es unabhängig erfunden. Ich habe es gefunden mit Rennbedingungen für die Prüfung Code generell nützlich zu sein. Ich hoffe, es hilft.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top