Innhold
1 "Motoren" for hendelsesbasert oppdatering
Applikasjonen, eller "motoren", for hendelsesbasert oppdatering, kan kalles Exchange Event Daemon i dette tilfellet. EED er, og besørger, oppstart av forskjellige komponenter som besørger propagering av hendelser mellom
1.1 Komponenter
EED består i hovedsak av følgende komponenter (klasser/prosesser):
- Notification Collector
Denne komponenten lytter etter NOTIFY-hendelser fra Postgres (valg av hvilke kanaler det lyttes på, skjer på bakgrunn av konfigurasjon). Hendelsene som blir mottatt, blir lagt i en kø (Queue-datatypen), for videre behandling av Exchange Client. Hendelsene blir også markert som "under behandling" i Postgres.
Hvis det ikke er funksjonalitet tilgjengelig for å få NOTIFY-hendelser med nyttelast, kan man alternativt la Notification Collector hente ut alle hendelser som ikke er påbegynt, for så å køe endringen (sammen med dens numeriske identifikator).
- Exchange Client
Komponenten henter nye hendelser som skal behandles fra køen, og leter opp endringsinformasjonen fra tabellen change_log. Endringens karakter bestemmer videre hvilke kall som blir gjort mot Exchange. Når hendelsen er bekreftet utført i Exchange, oppdaterer Exchange Client aktuell rad i change_log som utført.
1.2 Generell oversikt over flyt i Exchange Event Daemon
- NotificationCollector startes
- Ubehandlede events laget før NotificationCollector ble startet hentes inn og køes. Hvis ingen hendelsesmeldinger blir mottatt i løpet av et gitt tidsrom, så sjekker NotificationCollector databasen etter endringer som ikke har blitt tatt.
- Et gitt antall instanser av Exchange Client starter
- EED kjører et utplukk fra change_log med jevne mellomrom, som henter ut ubehandlede hendelser fra fortiden, og køer disse
1.2.1 Illustrasjon over sammenheng og flyt mellom de forskjellige komponentene
+-------+ | | +--------+ | *- | | EED | | import| +--------+ | | | | | +---+ +-------+ +----->|CLQ| ---+ | 1. 4. | +---+ | 5. | | | \/ | \/ +----------+ +--------------+ +-----------+ | | | | | | | Cerebrum | | Notification | | Exchange | | | | Collector | | Client | | | | | | | +----------+ +--------------+ +-----------+ | /\ | | | | | | 3. | | 2. | | 6. | | | \/ | | +------------+ +------------+ | +------->| | | | | | Exchange | | Trigger- |-+ | | | procedure | +------------+ | | +------------+
- En importen fra et kildesystem kjører, og lagrer f.eks. nye personer i Cerebrum
- Når rader skrives til event_log tabellen, startes en utløserprosedyre på databasesiden
- Utløserprosedyren sender en beskjed til NotificationCollector om at en hendelse har inntruffet
- NotificationCollector henter ut aktuell informasjon om hendelsen, og legger hendelsen på køen som deles med ExchangeClient
- ExchangeClient henter ut hendelser fra køen
- ExchangeClient utfører passende kall mot Exchange, alt etter hva hendelsen dikterer
- Hvis kallet mot Exchange ikke kan bli utført ved daværende tidspunkt køes hendelsen på nytt.
2 Change- og EventLog
For å kunne behandle hendelser effektivt, er det behov for en EventLog (en liste over hendelser som skal utføres). ChangeLog er ment til å være historikk, og derfor er det ønskelig å separere ut hendelser fra denne.
2.1 Endringer i databasens skjema
Følgende tabeller må defineres i databasen for å kunne behandle hendelser:
2.1.1 event_log
Det defineres en tabell, event_log, med følgende kolonner, som beskriver hendelser som har oppstått.
- event_id
Numerisk identifikator for hendelsen
- event_type
Hendelsens type, f.eks. navneendring
- event_target
Entiteten hendelsen gjelder
- target_system
Målsystem for hendelsen, à la source_system
- taken_time
Tidspunktet hendelsen ble hentet av integrasjonen mot målsystemet, for behandling (f.eks. opprettelse av en postboks i Exchange).
- processed_time
Tidspunktet hendelsen var ferdig behandlet (implisitt suksess) av den integrasjonen som besørger behandling.
TODO: Bare sletting i stedet, samt arkivering i ChangeLog?
- failed
Numerisk verdi som betegner hvor mange ganger behandling av en hendelse har feilet. Hvis en hendelse feiler mer en N-ganger, kan det være ønskelig å rapportere hendelsen, samt forkaste den
TODO: Ønsker vi dette slik?
Samtlige rader i tabellen som ikke er påbegynt kan enkelt hentes ut med følgende kall (f.eks. ved oppstart av de forskjellige integrasjonene):
UPDATE event_log SET taken_time = now() WHERE target_system = 'exchange' taken_time IS NULL RETURNING event_id;
Dette skal være en "atomisk operasjon", som returnerer den numeriske identifikator til de hendelsene som har blir reservert for oppdatering.
De numeriske identifikatorene kan deretter benyttes til å plukke ut informasjon om aktuelle hendelser fra event_log-tabellen.
2.1.2 target_system_code
Alle hendelser er assoiert med et målsystem, hvor de skal ende opp. En tabell for beskrivelse av målsystem kan kalles target_system_code og modeleres etter authoritative_system_code (en lik modell for disse tabellen letter eventuell kombinasjon av tabellene i fremtiden, hvis det er ønskelig).
Eksempelvis kan følgende kolonner defineres:
- code
Numerisk identifikator av målsystemet
- code_str
Tekstlig representasjon av målsystemt (f.eks. 'Exchange' eller 'Fronter')
- description
En kort beskrivelse av målsystemet
2.1.3 event_to_target
For å kunne knytte forskjellige hendelsestyper mot målsystem, kan man definere en ny tabell, event_to_target, med følgende felter:
- target_system
Kolonnen er knyttet mot code kolonnen i target_system_code tabellen via en fremmednøkkel
- event_type
Kolonnen knyttes mot change_type_id kolonnen i change_type tabellen via en fremmednøkkel
TODO: Be om kommentar på dette
2.2 Implemenetasjon av abstraksjonslag over databasen
Abstraksjonslaget (klassen) EventLog kan implementeres rimelig likt som for ChangeLog modulen, med funksjoner for å populere og tømme loggen.
log_change funksjonen til Database-modulen kan populere event_log tabellen med de samme hendelsene som blir populert i change_log tabellen.
Der det blir satt inn en rad i change_log tabellen, vil det potensielt sett kunne legges inn flere rader i event_log, da det lages en rad per målsystem.
TODO: Mer spesifikk beskrivelse
Per i dag kalles funksjoner i ChangeLog-modulen via Database-modulen.
Initsialiseringen av Database-klassen følger dette mønsteret:
CLDatabase-klassen importeres som en mixin til Database-klassen, når Database-klassen initsialisers.
CLDatabase-klassen defineres som en subklasse av DBDriver-klassen, samt ChangeLog-klassen.
2.2.1 Implementasjonsforslag
En måte å implementere dette på, er ved duplisere triangelet CLDatabase-, Database- og ChangeLog-klassen danner i dag, ved å definere en ny klasse, ELDatabase. ELDatabase subklasser CLDatabase og den kommende EventLog-klassen.
2.3 Om sending av melding om nye hendelser fra databasen til EED
2.3.1 LISTEN og NOTIFY
Postgres tilbyr funksjonalitet for å sende beskjeder om hendelser via databasen. Beskjedene mottas av en klient (f.eks. psql eller et program som har koblet til Postgres). Beskjeden kan sendes av en klient, eller en utløser-prosedyre i Postgres.
Beskjedene blir sendt over en kanal, og kan ha en nyttelast. Klientene som lytter etter beskjeder, deklarerer hvilke kanaler de vil lytte på.
2.3.2 Utløserprosedyren i detalj
For å få gitt beskjed til EED om at en ny hendelse har oppstått, kan følgende utløserprosedyre defineres i Postgres:
CREATE OR REPLACE FUNCTION send_event_notify() RETURNS TRIGGER AS $event_notify$ BEGIN PERFORM pg_notify('' || NEW.change_program, '' || NEW.change_id); RETURN NULL; END; $event_notify$ LANGUAGE plpgsql; CREATE TRIGGER event_notify AFTER INSERT ON event_log FOR EACH ROW EXECUTE PROCEDURE send_event_notify();
Prosedyren kjøres etter INSERT (og derav implisitt COMMIT) på tabellen change_log. Hvilken kanal beskjeden skal sendes på, dikteres av hvilken endringstype som har blitt utført, og hvilke målsystem som abonnerer på den aktuelle endringstypen via event_to_target.
Ved å velge kanal på bakgrunn av abonnering på hendelsestyper, kan man kjøre flere separate integrasjoner samtidig, uten at integrasjonene trenger å forkaste endringer som ikke er relevante for målsystemet.
Endringsmeldingen kan og sendes over en kanal som er dedikert til den aktuelle integrasjonen. Det vil være fortrinnsmessig om nyttelasten er den numeriske identifikatoren til raden som har blitt opprettet.