User Tools

Site Tools


technology:polling

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

technology:polling [2013/01/18 16:48]
rtavassoli
technology:polling [2013/01/18 19:04] (current)
rtavassoli [Der Tätigkeitsbericht in PRO•M]
Line 12: Line 12:
 Was aber, wenn ich was am Mitarbeiter ändere? Dann kann ich nicht nach einer eindeutigen ID pollen. Ich brauche also was anderes: Was aber, wenn ich was am Mitarbeiter ändere? Dann kann ich nicht nach einer eindeutigen ID pollen. Ich brauche also was anderes:
 ===== Pollen ohne natürliches Polling-Kennzeichen ===== ===== Pollen ohne natürliches Polling-Kennzeichen =====
 +Als PollingID reicht eine GUID völlig aus((wobei ein String-Wert genommen wird, damit der Client sich entscheiden kann, vielleicht doch noch zusätzliche Dinge zu setzen, wie z.B. einen Zeitstempel)). Wenn ich nach etwas pollen möchte, setzte ich die PollingID einfach mit Guid.NewGuid()((vielleicht noch + einen Zeitstempel)). Das Ganze wird noch durch folgende Mechanismen sicherer gemacht:
 +  - Wenn ein Denormalisierer ein Ereignis zum Denormalisieren empfängt, das eine PollingID enthält, speichert er in der Transaktion, in der er denormalisiert, auch diese PollingID, plus die AccountID des authentifizierten Kontos in einer Polling Tabelle. Dazu speichert er den Zeitstempel des Beginns der Denormalisierung. Gibt es für die AccountID/PollingiD Kombination bereits einen Eintrag, wird der Zeitstempel angepasst,
 +  - Der Denormalisierer stellt sicher, dass die PollingID von dem Anwender((durch die AccountID identifizierbar)) eine bestimmte Zeit lang zur Verfügung steht. Da ein Client immer nur eine gewisse Zeit lang pollt, muss die garantierte Verfügbarkeitszeit lediglich mindestens so lang sein, wie die längste Polling Dauer des Clients. Ich denke, eine Stunde sollte völlig reichen.
 +  - Wird eine Sicht jetzt nach einer PollingID gepollt, wird einfach ein SELECT Timestamp FROM MyPollingTable WHERE AccountID = @AccountID AND PollingID = @PollingID ausgeführt. Wenn das Ergebnis ein Ergebnis liefert mit Timestamp > Jetzt abzgl. Verfügbarkeitsgarantie, wird ein positives Ergebnis gemeldet, ansonsten ein negatives((wenn der Timestamp länger als eine Stunde her ist, gehe ich einfach mal davon aus, dass das jetzt eine neue PollingID sein soll)).
 +  - Ein zweiter Prozess((oder Thread)) räumt die alten PollingIDs auf, die älter als die garantierte Verfügbarkeitszeit sind.
 +> Dadurch, dass die PollingID Benutzerkonto abhängig gemacht wird, kann es nicht passieren, dass sich zwei Benutzer gegenseitig in die Quere kommen.
  
 +> Über die PollingID werden immer nur bestimmte Sichten abgefragt, also auch wenn ein Anwender dieselbe PollingID für zwei Befehle verwenden sollte, ist das nur schlimm((falsche positive Antwort auf eine Pollinganfrage)) wenn die Befehle auch dieselbe Sicht betreffen.
 +
 +> Die PollingID ist ein String. Der Client könnte auch eine GUID+AggregateID+Version+Zeitstempel setzen, um das noch sicherer zu machen.
 +
 +> Da eine PollingID nach einer bestimmten Zeit verfällt((sollte man dann vielleicht doch für Befehle einführen - wobei Event Handler Befehle auch ausführen können müssen, nachdem das System vielleicht 2 Tage nicht verfügbar war, also da lieber händisch aufräumen, sollten die Befehls-IDs zu viele werden)), ist die Wahrscheinlichkeit, dass in diesem Zeitraum((eine Stunde)) von einem Client((i.d.R. ist eine Anwender auch nur mit einem Client auf einem Computer angemeldet)) dieselbe PollingID für dieselbe Sicht erzeugt wird im Grunde null, weil die GUID Generierung auf einem Rechner eindeutig sein muss!
 +
 +> Auch wenn es einen Konflikt gibt, bedeutet das im schlimmsten Fall, dass das System sagt, dass ein Ergebnis //sichtbar// ist, obwohl es noch nicht sichtbar ist. Das wäre zwar keine besonders vertrauensbildende Benutzererfahrung, aber ja auch nicht weiter schlimm.
 +===== Lauernde Gefahren =====
 +Die PollingID kann u.U. recht wichtig werden. Ein Anwender führt ja Prozessschritte durch, ähnlich wie ein Systemagent((Saga)), und verwendet die gepollten Ergebnisse für die Entscheidung, welche Schritte als nächstes durchzuführen sind. Im besten Fall passiert folgendes:
 +  - Anwender schickt Befehl ab,
 +  - Anwender pollt nach Ergebnis,
 +  - Ergebnis steht innerhalb des Pollingzeitraums zur Verfügung,
 +  - Anwender lädt Ergebnis,
 +  - Anwender trifft Entscheidung über nächste Schritte.
 +Eine der großen Gefahren besteht dann, wenn Sichten implizit verwendet werden um Schritte außerhalb des Systems durchzuführen. Ein Beispiel:
 +==== Der Tätigkeitsbericht in PRO•M ====
 +In PRO•M erfasst ein Mitarbeiter seine Zeiten, und druckt dann irgendwann seinen Tätigkeitsbericht aus. Dieser wird dem Kunden zur Unterschrift vorgelegt - das ist der Prozessschritt //außerhalb// des Systems. Die erfassten Zeiten des Mitarbeiters liegen dann einer späteren Rechnungsstellung zugrunde.
 +=== Die Gefahr ===
 +Der Mitarbeiter druckt den Bericht nachdem er alle seine Zeiten erfasst hat. Einige der Zeiten((z.B. 4 geleistete Stunden)) sind aber noch gar nicht in die Sicht denormalisiert worden. Der Kunde unterschreibt somit für 8 geleistete Stunden. Die Rechnung wird dann mit allen Zeiten erstellt, also mit allen 12 Stunden. Der Kunde hat aber nie für diese 12 Stunden unterschrieben((oder die Rechnung rechnet nur 6 Stunden ab, weil dort noch weniger der geleisteten Stunden angekommen sind)).
 \\ \\ \\ \\
-Die Lösung wird ähnlich sein wie die für die [[concepts:idempotency|Idempotenz ID]]. Ich als Absender des Befehls((Command)) setze im Befehl eine Polling ID. Diese Polling ID wird in alle Ereignisse((Domain Events)) geschriebendie ein direktes Ergebnis dieses Befehls sindEvent Handlers und Sagas, die Ereignisse abfangen und als Reaktion auf diese neue Befehle absenden, übernehmen die Polling ID in die Befehle. Die Event Handler und Sagas haben keine eigene Verwendung für die Polling ID((Die Saga setzt eine Correlation ID, die anders ist, weil eine Saga von einem Ereignis ausgelöst wird und die Correlation ID einfach [Aggregate ID, Version, Saga Typ] ist. Wenn ein Anwender einen Befehl sendet, wird die Version nicht zwingend mit geschickt, und der Typ des Clients ist nicht eindeutig/singleton wie die Saga)). +Man könnte diesen Fall lösenindem man einige der Schritte implizit machtDas System wäre dann zumindest //schließlich konsistent//. Z.B. müsste der Mitarbeiter den Tätigkeitsberichtden er dem Kunden zur Unterschrift vorlegtexplizit erstellen. Wenn der Bericht nur 8 von 12 geleisteten Stunden enthält, dann wird das irgendwann später erkanntweil 4 Stunden noch in keinem Bericht stehenDiese können dem Kunden dann nachträglich vorgelegt werden.
-\\ \\ +
-Die Polling ID ist recht wichtig. Wenn ich z.B. einen Report ausdrucken möchte, der alleswas ich erfasst und angegeben habebeinhalten soll, dann möchte ich das erst machen nachdem alle Angaben auch im Report enthalten sind. Das Pollen kann aber erst gestartet werdennachdem der Befehl bestätigt wurdeBei unerwarteten Fehlern beim Bestätigen sollte der Befehl erneut geschickt werden, so lange, bis mir entweder mitgeteilt wurde, dass er fehlgeschlagen ist, oder dass er durchgeführt wurde. Dafür wird die [[concepts:idempotency|Befehlsidempotenz]] verwendet. Erst danach kann das Pollen starten.+
 \\ \\ \\ \\
-Da ich erst polle, nachdem ich weiß, dass der Befehl bestätigt wurde, weiß ich, dass ich schließlich((eventually)) ein Ergebnis mit der Polling ID erhalten werde. Ich könnte also unendlich lang pollen. Wenn es zu lange dauert, sollte der Admin informiert werden, dass Ereignisse irgendwo in einer Pipeline hängen geblieben sind. Die Frage ist, woraus sich die Polling ID zusammen setzt. +Rechnungen werden auch nur auf erstellte Tätigkeitsberichte gestelltnicht einfach auf //erfasst Zeiten//. Wenn nun ein neuer Tätigkeitsbericht mit den 4 noch nicht abgerechneten Zeiten erstellt wird, kann dieser abgerechnet werden.
-===== Polling ID ===== +
-Die PollingID ist die [[concepts:idempotency|idempotente]] und somit auch global eindeutige Befehls-ID. Wenn ein Befehl ohne PollingID ausgeführt wirdwird im Domain Event automatisch die BefehlsID als PollingID gesetzt. Wenn ein Event Handler nun ein Ereignis denormalisiert, kann er die PollingID in die Liste der behandelten IDs aufnehmen, damit er, wenn danach gefragt wird, antworten kann, dass die PollingID in der denormalisierten Menge enthalten ist.+
 \\ \\ \\ \\
-Es kann aber sein, dass ein Befehl einen Prozess auslöst, und dass Event Handler und Sagas weitere Befehle absendenum diesen Prozess zu beendenDiese Event Handler und Sagas können nun die PollingID zusammen mit der Befehls-ID speichernund wenn sie gepollt werdengeben sie die Befehls-ID((die neue Polling-ID)) raus.+So ist das System (schließlich) konsistent. Es würde lediglich inakzeptabel sein, wenn die Zeiten viel später als erwartet sichtbar sind, und vielleicht Tage nach Rechnungsstellung auftauchen. 
 +> Das System ist aktuell so gebaut, dass die Sichten //sofort// konsistent sindnicht //schließlich// konsistent, weil sie in derselben Transaktion gebaut werden wie auch die Ausführung des BefehlsEntweder muss das so bleiben, man muss den Workflow ändern, oder man macht folgendes: 
 +=== Warten auf Aktualität der Sicht === 
 +Jede Sicht wird aus Ereignissen aus einem oder mehrerer Event Stores gebaut. Die Ereignisse in einem Event Store enthalten die Ergebnisse von bisher ausgeführten Befehlen. Ich als Anwender hätte gerne die Sicherheitdass ich für eine gewisse Aktion, die ich am dd.mm.yyyy um hh:nn:ss starte, auch die Ereignisse aller Befehle, die bis dahin ausgeführt wurden, zur Verfügung habeWie kann ich das machen, ohne mich auf koordinierte Uhrzeiten auf den verschiedenen Systemen zu verlassen?
 \\ \\ \\ \\
-Alternativ könnten die Event-Handler/Sagas die Polling-ID im Befehl direkt setzen, und zwar mit der Polling-ID des auslösenden Ereignisses. Auch nicht uninteressant. Damit Polling-IDs global eindeutig bleiben, sollte das aber nur bestimmten System Agenten((siehe Diskussion in der Diskussion der [[concepts:idempotency|Idempotenz]]))erlaubt werden, damit sie keine wilkürlichen Polling-IDs setzen.+Event Stores müssen zwei Dinge zusichern 
 +  man kann sie nach der aktuell höchsten Ereignis ID((nicht nach der Sequenz, die kann auch noch NULL sein)) fragen. Diese ist pro Event Store eindeutig. 
 +  - ...to be continued
technology/polling.1358524119.txt.gz · Last modified: 2013/01/18 16:48 by rtavassoli