achtung, langer text, vorn ein paar Erfahrungen, weiter hinten ein paar konkrete Vorschläge
Basierend auf Erfahrungen von einem vor langer Zeit von uns erstellten Projekt
für einen sehr großen Konzern, bei dem 6 s2m isdn Karten mit je 30 isdn Kanälen
gesteuert werden mussten und die vorherige Lösung (die nicht von uns kam) eine
komplette Fehlkonstruktion war:
Es ging um die Umsetzung einer callbox, sozusagen ein Anrufbeantworter, nur umgekehrt,
der nahm keine Anrufe an, sondern musste einen freien der 180 isdn Kanäle auswählen
und diesem dann eine passende Sounddatei vorspielen. Im Protokoll sollte dann enthalten
sein, ob der Angerufene abgehoben hat, wie lange der Call aktiv war und noch ein paar
andere Metadaten. Wenn abgehoben, dann wird der nicht wieder angerufen. Wenn nicht
wird es je nach Auftrag nach x Minuten noch mal versucht.
Was war das Problem der vorherigen Lösung: Ein Programmierer des Konzerns hatte dafür
eine C++-Anwendung geschrieben, die Daten aus einer Postgres-Datenbank bekam und sich
dort per random einen Call aus der Call Tabelle holte, versuchte diesen zu sperren,
diesen auf dem isdn kanal zu verarbeiten und dann nach Durchführung die Metadaten zurück
in die Datenbank zu schreiben. Auch nicht erfolgreiche sperren wurden versucht, in die
datenbank zu schreiben.
Das ganze System sollte ausgelegt werden für 50000 ausgehende Anrufe pro Tag und als
Zeitraum war nur zwischen 08:00 und 20:00 wochentags erlaubt, also ein halber Tag.
Das der gesamte Tag 86400 Sekunden hat, wissen die meisten von uns, es bleiben also
nur 43200 Sekunden für 50000 Anrufe, also weniger als eine Sekunde pro Anruf, die
aber leider inkl. der tüdelüt Melodie am anfang und am ende alle zwischen 25 und 35
sekunden braucht. Typischer Inhalt: Wir haben ihren auftrag erhalten und bearbeiten
den so schnell wie möglich, ...
Die Auftragsdaten kamen in die Datenbank von einer Konzern SAP Schnittstelle, an die
der Inhalt der o.a. Metadaten dann später auch zurückgehen sollte.
Was war das Problem der gewählten Anwendung c++? Der client wurde auf dem master control
server in der anzahl der verfügbaren kanäle gestartet, also liefen ca 180 parallel laufende
executables auf einer linux maschine, die schon ziemlichen dampf hatte. Wenn die nix machen
wäre das auch kein problem.
Gleichzeitig lief dort aber auch die Postgres Datenbank, mit der sich alle
clients verbunden haben. Wenn ein call rausging, sorgte die executable dafür,
das der s2m controller server, auf dessen kanal der Client konfiguriert war, dieser das
dann erledigen sollte. Die s2m hardware einsteckkarte war auch wohl high end, dir hatten davon
eine im office, angeblicher wert ca 10000 € (wir hatten zwar keinen s2m anschluss, aber für die
programmierung ging das mit der karte in einem Simulationsmodus ganz gut und beim endkunden
hatten wir zugriff auf eine reale maschine mit realen s2m).
Um nicht zu weit auszuholen: die vorherige Anwendung war nach dem Serverstart innerhalb
von 30-45 minuten je nach anzahl der Aufträge als Datenbank nicht mehr erreichbar, weil
alle clients wie bekloppt immer die gesamten auftragsmenge runtergeladen habe, sich im
client per random einen ausgesucht haben und den dann versuchten zu sperren. Das führte
bei der Konstellation beim vorherigen Entwickler mit 50 Aufträgen und gerade mal 10 aktiven
isdn kanälen zu ganz guten ergebnissen im testbetrieb, so das man damit versuchte, das
System scharf zu schalten.
Der reale Dateneingang, der auch schon bescheiden programmiert war, sorgte aber
mit allen aktiven 180 Kanal executables dafür, das nicht mal mehr simple
sql befehle vom
postgres server innerhalb akzeptabler zeit beantwortet wurden (auch nach 60 minuten kam
das zeitweise keine antwort). Das System lief komplett amok und wurde dafür dann wohl sogar in
der Presse bekannt, das es eine Arztpraxis 24 Stunden am Tag ca alle 3-5 Minuten angerufen
hat und egal, ob die abgenommen haben oder nicht, danach wieder angerufen wurde. Das ging
wohl mehrere Tage so, obwohl angeblich die Daten aus dem System entfernt wurden. Da es
auch ncoh die Notrufnummer dieser Arztpraxis war, konnte man die auch nicht so einfach
abschalten. Man entschied sich dann nach dem Prssebericht in dem Konzern, die Lösung
komplett abzuschalten, weil die auch nach mehreren reboots aller beteiligten Systeme
der unkontrollierbare Amoklauf erneut losging.
Die Technik (serverhardware) war auf jeden fall nicht schuld und als wir dann mit dem damaligen
Projektleiter, zu dem wir im Rahmen eines anderen Projekts schon einen sehr guten Kontakt hatten,
zu einer gemeinsamen Analyse des vorhandenen Systems eingeladen wurden, konnte ich mir mehrfach
das schmunzeln nicht verkneifen. Alle Clients holten sich im Sekundentakt immer alle Aufträge
in den eigenen Speicher von der Datenbank, es gab ein optimistisches locking, d.h. ob ein auftrag
evtl schon von jemand gesperrt war oder nicht war dem client nicht bekannt, er versuchte einfach
einen lock auf einen auftrag mit einem update. wenn der klappt, legte der los usw. Wenn der aber nicht klappt weil
schon von jemand anders gesperrt solte der client sich die auftragsliste noch mal neu und es ging
von vorne los (ohne in irgnedeiner art einen timeout zu haben).
Ihr ahnt es, wenn 10000 calls in der Auftragsliste sind, ist für alle genug frei, zufällig was
zu erwischen, was frei ist. wenn aber nur 20 oder 30 calls drin sind, laufen ca 150 von 180 clients
komplett amok und bringen jede datenbank an die Belastungsgrenze oder wie in diesem Fall deutlich
darüber hinaus.
Wir haben das dann so gemacht, das wir statt postgres firebird genommen haben, weil wir uns damit besser
auskennen und direkt mit einem eintrag eines neuen callauftrags von der sap schnittstelle sorgte
ein trigger auf dem server dafür, das dieser schon einem kanal zugeordnet wird, der den abarbeiten
soll.
python scripte auf den servern mit dem s2m karten, für die es dafür saubere apis gab, holten
sich immer nur den jeweils obersten eintrag aus der firebird
db aus seiner kanal queue, vermerkt
diesen einen datensatz mit einem Insert in eine andere Tabelle als "in arbeit" (also kein update
auf dem record selbst), arbeitet den ab und verschiebt den nach ausführung in eine andere
tabelle und trägt die metadaten in der job ausführungstabelle per insert nach.
Wenn nix in seiner queue ist macht der client auch nix und wartet 10 sekunden. Wenn was gerade
abgearbeitet wurde, wartet der aber nicht, sondern holt sich den nächsten call sofort. Wenn
keiner da ist, dann eben wieder warten.
inserts sind immer pflegeleichter für multithreaded datenbanken, weil die nix sperren müssen im gegenatz
zu updates.
Eine Datensatzsperre muss nicht zwingend das sein, was die Datenbank dafür fordert
(so was wie in firebird zB mit "select ... for update" oder direkter update), weil das hässliche
nebeneffekte haben kann, wie zum Beispiel erforderliche langlaufende Transaktionen für die dauer
der sperre.
Eine extra lock tabelle, die von dir selber erstellt wird mit unique pk von der
datentabelle und timestamp/user/
ip/connection id/was auch immer kannst du auch
extra selber erstellen und via trigger dafür sorgen, das echte updates auf der eigentlichen
Tabelle nur dann erlaubt sind, wenn zu den metadaten vom aktuellen client die in der lock tabelle
übereinstimmen.
So kann ein Lock auch mal minuten, stunden oder sogar tage gültig sein, ohne
irgendwas blockierendes machen zu müssen wie transaktionen lang offen halten. Und ganz nebenbei
kannst du darüber dann auch gleich anzeigen, wer was gerade gelockt hat und seit wann und wenn
das gewünscht ist kann ein andere client einen fremden lock übernehmen oder löschen.
Wenn eine Queue größer wird und warum auch immer nicht sauber abgearbeitet wird (s2m server reboot oder kaputt zB)
dann wird der inhalt von seiner queue von einem server prozess, der als sp implementiert war, auf andere
aktive queues verschieben, sofern die noch keine exec job log eintrag haben.
Die Architektur was also eigentlich zentral von der firebird
db gesteuert und nicht zufallsbedingt
von hunderten Clients, die sich alle gegenseitig dauernd blockieren.
Mich erinnerte die Anforderung deines Kunden sehr stark an die Programmierung, die unser Vorgänger
da verbrochen hat. Hundert Clients wühlen sich durch die Daten, sucht sich irgendwas nach Lust und
Laune zufällig aus, sperren das dann mit updates usw.
Die Realität selbst in sehr großen Callcentern, die sich solche Forderungen gerne mal ausdenken,
kann man oft in entsprechende Bahnen lenken, in dem man ähnlich wie den o.a. isdn kanälen
automatische serverbasierende regeln hinterlegt, weil es auch keinerlei sinn ergibt, das sich 100
call center agents auf dem Screen alle die ersten 50 aktiven datensätze anglotzen, um dann irgendwas
anzuklicken, was jemand anders aber vielleicht auch schon gemacht hat. 50 records im sekundentakt
sind im durchschnitt 20ms zeit pro record. Es wäre kompletter Unsinn, die Arbeitszeit da von
hunderten Mitarbeitern zu verplempern, in dem hunderte den selben kram auf dem screen sehen, von denen
mit sehr hoher wahrscheinlichkeit schon alles gesperrt ist, wenn das dann auch ncoh wild herumflackert
wird es noch verrückter. Clientbasierte filter, die tausende records vom server holen und erst lokal
ausfiltern sind da auch ein NoGo in Enterpriseumgebungen.
Mein tip also: versuch erst mal dem Kunden die Probleme seiner Idee zu verdeutlichen und versuch
es gar nicht erst, das mit hunderten client executables zu implementieren, die das selber
unkoordiniert für sich entscheiden.
In der Firebird Welt wäre in meiner Umsetzung ein zentrales Regelwerk für die Queue Zuordnung automatisiert
per trigger o.ä. zuständig und wenn ein client dann was für ihn relevantes neues sehen soll, wird der per
event alterter benachrichtigt (zB weil sein username als eventname benutzt wird) und nur der, für den es was
relevantes neues gibt, holt sich dann die notwendigen daten neu (zB nur die sperrmerkmale infos
der für ihn relvanten records mit pk der zugehörigen daten,wie zum Beispiel adressen).
alle schon bekannte daten kann man lokal vorhalten und muss nicht jedes mal den gesamten kram neu laden.
Lade die die anzuzeigenden Daten nur dann neu wenn das erforderlich ist und trenne das ganz sauber vom
laden der statusinfos, um zu wissen welchen status 10000 records haben brauchst du nichts anderes als
die pks der daten mit dem zugehörigen pk des zustands. wenn da ein pk kommt, den dein client noch nicht
kennt kann der die details dazu ja noch mal extra holen.
und ganz wichtig: wenn der client keine lebenszeichen vom anwender erkennt, weil weder mousemove noch keypress
erkannt wurde, muss man auch nichts in echtzeit aktualisieren, dann reicht auch refresh im minutentakt.
wenn man das gut designt bekommt man mit brauchbarer Firebird Server Hardware ziemlich problemlos 5000
sqls pro Sekunde vom server beantwortet, auch weil der meiste kram im Rahmen von readonly Transaktionen
geholt werden kann.
wenn das nicht reicht kann man das über replikation auf mehrere Server verteilen, die dann je 5000 sqls pro sekunde
können.
dafür einen chatserver oder ähnliches dazwischen zu packen wäre für mich keinesfalls ein vorteil, die machen
oft am ende noch dinge, die du dann gar nicht mehr unter kontrolle hast. versuch die relevanten infos vom
datenbankserver auf das minimum einzuschränken,also mit sqls das zu holen oder zu senden was notwendig ist.
Verlass dich nicht auf Komponenten, das die schon nur das machen was notwendig ist, das ist keineswegs der
Fall (beispiel dataset recordcount,
db lookup comboboxen und fields usw).
über umsetzungen auf basis von
ado/datasource/dataset/dbgrid usw wird dir das Projekt unter vollast
sicherlich früher als später um die Ohren fliegen. Da sorgt oft der client serveseitig für deutlich
mehr last als man denkt.
da macht das auch keine unterschied ob es
mssql oder firebird ist.
Vielleicht sind in meinem langen post aber ja schon ein paar Anregungen für dich dabei,
wenn dir der ganze text nicht zu lang war.
p.s.: in dem o.a. Projekt, bei dem eigentlich 50000 calls als maximum pro Tag vorgesehen waren, fanden
wir ca. 9 monate, nachdem das projekt live ging, aufgrund eines Streiks der Konzernmitarbeiter an einem Tag
sogar 500000 calls, die da erfolgreich abgewickelt wurden. Da war man auf beiden seiten sehr froh, das
selbst damit das von uns implementierte Gesamtsystem nicht überfordert war.