Trage clients en verloren verbindingen · eindproject
Eindproject: Project: scorebord v2
Scorebord v2 is geschreven. Elke kijker heeft een outbox die alleen de nieuwste snapshot bewaart, elke send gebeurt op de eigen thread van de kijker, een kijker die sluit, wordt binnen een halve seconde opgemerkt, en tests/test_scoreboard.py en draait daarna de server met een met opzet vertraagde kijker en met een kijker die stil wordt.
Twee tests
Open tests/test_scoreboard.py. De berichtentests en de hulpfunctie make_connection uit hoofdstuk 7 staan er nog; de drie servertests uit hoofdstuk 7 zijn weg, omdat ze controleerden dat een score direct naar elke kijker wordt gestuurd, wat les 3 met opzet veranderde. Er wachten twee placeholders op je.
test_an_outbox_keeps_only_the_newest controleert het beleid van les 2: maak een outbox met scoreboard_server.make_outbox(), bied hem drie snapshots met verschillende versies aan, en controleer dat take de laatste teruggeeft, dat een tweede take None teruggeeft, en dat outbox["event"].is_set() daarna False is. Snapshots zo klein als {"type": "snapshot", "version": 4, "scores": {}} zijn genoeg; offer en take kijken er nooit in.
test_a_viewer_leaves_the_registry_whatever_ends_it controleert het finally-blok. Maak een bord met scoreboard_server.make_board(["Owls"]) en een stand-in met make_connection("v1"), en geef de stand-in daarna een eigen send die RuntimeError opwerpt, het soort fout dat except ConnectionClosed: niet vangt. Roep scoreboard_server.viewer_handler(viewer, board) aan binnen try, met except RuntimeError: pass, omdat de handler die board["viewers"] leeg is.
Die tweede test beëindigt het gesprek van de kijker via zijn stand-in: de eerste send werpt een exception op, dus de handler is meteen klaar. Dat doet er hier toe. Een test die de CHECK_EVERY) echte tijd wachten, omdat deze tests echte locks en events gebruiken. Elke test moet zijn kijker beëindigen via de send of recv van de stand-in, nooit door erop te wachten.
Dit zijn de twee beloften die v2 aan v1 toevoegt, en het zijn de beloften die een latere verandering stilletjes kan breken. Een offer die de oudste snapshot bewaart, zou kijkers nog steeds hele borden sturen, alleen verouderde, en een finally-blok dat naar een except-clausule is verplaatst, zou nog steeds slagen in elke run waarin kijkers netjes sluiten. Geen van beide fouten is te zien in een transcript; een test betrapt ze allebei in milliseconden.
Houd import scoreboard_server bovenaan en roep zijn
Draai de tests in de
python -m pytest -q
Een afgemaakte versie, op de site uitgevoerd, meldde 7 passed: de vijf berichtgevallen en je twee tests.
Weer de vertraagde kijker
Draai het scenario van les 1 tegen v2. pl_ws.py, een programma dat voor deze cursus is geschreven en elke client speelt, verbindt kijker A en kijker S, een kijker die dit programma met opzet vertraagt, 0,3 s vóór elke send naar hem, en de scorebijhouder scoort drie keer:
python pl_ws.py slow-viewer
Dezelfde run drukte af:
scenario slow-viewer: viewer S is slowed down on purpose; every send from the server to S waits 0.3 s first
server options from your open_server: ping_interval=1.0, ping_timeout=1.0, close_timeout=1.0
viewer A joined; its first message was a snapshot with version 0
viewer S joined; its first message was a snapshot with version 0
the scorekeeper sent 1 point for Owls; its reply was a snapshot with version 1
the scorekeeper sent 2 points for Foxes; its reply was a snapshot with version 2
the scorekeeper sent 3 points for Herons; its reply was a snapshot with version 3
score 1: the reply came before the server finished sending version 1 to S
score 2: the reply came, and the server never sent version 2 to S (a newer snapshot replaced it)
score 3: the reply came before the server finished sending version 3 to S
the server shut down
viewer A received versions [0, 1, 2, 3], then the server closed it with 1001
viewer S received versions [0, 1, 3], then the server closed it with 1001
Vergelijk het met les 1, waar elk antwoord na de trage send naar S kwam. Nu komen de antwoorden aan de scorebijhouder eerst. De tweede regel laat zien dat het programma zijn server bouwde met de open_server. A, die snel is, zag elke versie; S, die traag is, sloeg er één over.
In de run met v2 ontving S versie 0, 1 en 3. Is dat een fout?
De dode kijker
Het laatste scenario verbindt kijker A en kijker D, die zijn
python pl_ws.py dead-viewer
Dezelfde run drukte af:
scenario dead-viewer: viewer D completes its handshake, then never reads and never answers a ping (a dead path, simulated)
server options from your open_server: ping_interval=1.0, ping_timeout=1.0, close_timeout=1.0
viewer A joined; its first message was a snapshot with version 0
viewer D joined and went silent; the registry holds 2 viewers
the scorekeeper sent 1 point for Owls; its reply was a snapshot with version 1
library log: keepalive ping failed
the registry dropped D before the second score
the scorekeeper sent 2 points for Foxes; its reply was a snapshot with version 2
the server shut down
viewer A received versions [0, 1, 2], then the server closed it with 1001
De regel library log: is het blok keepalive ping failed uit les 4, dat pl_ws.py als één regel afdrukt. Een run van hetzelfde scenario op de startbestanden van hoofdstuk 8, vóór les 4, hield D 5 seconden na de eerste score nog vast. Met je drie waarden duurde de hele run op die computer een paar seconden.
Klik op Check my work zodra je tests slagen. Die controleert je server zoals les 2 tot en met 4 dat deden, controleert dat de scorebijhouder wordt beantwoord zonder enige send naar een kijker, en draait je tests tegen een eigen server en drie kapotte: een offer die de oudste snapshot bewaart, een viewer_handler die de kijker alleen verwijdert als zijn verbinding sluit, en een take die de snapshot laat staan. Je tests moeten slagen tegen de eerste, en minstens één ervan moet falen tegen elk van de andere. De controle leest je terminal en de transcripten niet.
Wat er nog openstaat
Scorebord v2 houdt de scorebijhouder snel en het register schoon. Het laat nog steeds iedereen die /score bereikt de scores veranderen: de server vraagt nooit wie er verbinding maakt. Hoofdstuk 9 beslist wie mag scoren, voordat de handshake wordt beantwoord.
Opdracht
Vervang de twee placeholders in tests/test_scoreboard.py door tests, en houd de berichtentests, closed_error en make_connection:
test_an_outbox_keeps_only_the_newest: maak een outbox metscoreboard_server.make_outbox(), bied hem drie snapshots met verschillende versies aan, en controleer dattakede laatst aangebodene teruggeeft, dat een tweedetakeNoneteruggeeft, en dat het event van de outbox gewist is.test_a_viewer_leaves_the_registry_whatever_ends_it: maak een bord en een stand-in metmake_connection, geef de stand-in eensenddieRuntimeErroropwerpt, roepscoreboard_server.viewer_handleraan binnentrymetexcept RuntimeError: pass, en controleer datboard["viewers"]leeg is.
Beëindig het gesprek van elke kijker via de send of recv van zijn stand-in; wacht nooit op een stille controle. Houd import scoreboard_server en roep zijn from scoreboard_server import.
Draai python -m pytest -q totdat elke test slaagt, en draai python pl_ws.py slow-viewer en python pl_ws.py dead-viewer om v2 aan het werk te zien. Klik daarna op Check my work. Die controleert eerst je server zoals les 2 tot en met 4 dat deden, met stand-ins en zonder thread, en controleert dat scorekeeper_handler elke score beantwoordt zonder send naar een kijker. Daarna leest ze je testbestand en draait ze je tests in dit proces: één keer met een eigen werkende server, waarbij minstens drie tests moeten worden verzameld en ze allemaal moeten slagen, en één keer met elk van drie servers die elk één fout bevatten (een offer die de oudste snapshot bewaart, een viewer_handler die de kijker alleen verwijdert als zijn verbinding sluit, en een take die de snapshot laat staan), waarbij minstens één test moet falen. Een run die langer dan 2 seconden duurt, wordt gestopt; tegen een kapotte server telt dat als het opmerken van de fout, tegen de werkende wordt het gemeld. De controle leest je