Løsningsforslag til hendelsesbasert oppdatering

Løsningsforslag til hvordan hendelsesbasert oppdatering kan implementeres i Cerebrum. Løsningsforslaget tar utgangspunkt i Exchange som målsystem.

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

  1. NotificationCollector startes
  2. 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.
  3. Et gitt antall instanser av Exchange Client starter
  4. 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 |                                     +------------+
    |            |
    +------------+
  1. En importen fra et kildesystem kjører, og lagrer f.eks. nye personer i Cerebrum
  2. Når rader skrives til event_log tabellen, startes en utløserprosedyre på databasesiden
  3. Utløserprosedyren sender en beskjed til NotificationCollector om at en hendelse har inntruffet
  4. NotificationCollector henter ut aktuell informasjon om hendelsen, og legger hendelsen på køen som deles med ExchangeClient
  5. ExchangeClient henter ut hendelser fra køen
  6. ExchangeClient utfører passende kall mot Exchange, alt etter hva hendelsen dikterer
  7. 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.

Av jsama
Publisert 14. nov. 2013 14:44