You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 11 Next »

1. Introduktion

1.1. Formål

Formålet med dette dokument er at beskrive systemarkitekturen for eCPR2 servicen.

1.2. Læsevejledning

Dokumentet er tiltænkt personer der skal drifte eCPR og indeholder dermed primært information om eCPR i forhold til dens relationer til andre systemer, og i første omgang dermed ikke om servicens interne opbygning.

1.3. Definitioner og referencer

NSPNational Service Platform
eCPR2Erstatnings CPR
DGWSDen Gode WebService
NASNational adviseringsservice


2. Introduktion til eCPR

2.1. Løsningens opbygning

eCPR består af én javabaseret web-service, nemlig eCPR-service. Den har ekstern afhængighed til NAS, CPR-enkeltopslag, en intern database samt et view til krs-stamdata. I nedenstående diagram ses et arkitektur overblik over servicen og dens eksterne afhængigheder:

Arkitektur overblik

Sundhedsvæsenenets elektroniske Brugerstyring (SEB) bruges ikke i nuværende version af eCPR. I øjeblikket kan eCPR koordinatorer/Web administratorer indskrives direkte i den interne MariaDB. I fremtiden vil denne brugerstyring foregå gennem SEB.


Sekvensdiagrammer

Nedenfor ses 2 sekvensdisagrammer. De blå komponenenter hører alle til eCPR-servicen, og illustrerer hvordan flowet overordnet set forløber internt.

Sekvensdiagrammet for CreatePersonRequest ses nedenfor.

sd eCPR - Opret person


I forløbet "CreatePerson" bruges Cpr-enkeltopslag samt NAS ikke. I sekvensdiagrammet for UpdatePersonRequest ses hvordan Cpr-enkeltopslag og NAS bruges i et UpdatePerson kald. Cpr-enkeltopslag bruges kun, hvis UpdatePerson indeholder et CPR-nummer, hvorved der bliver tjekket op mod stamdata, om CPR-nummeret eksisterer. I nedenstående sekvensdiagram antages det, at UpdatePersonRequest laves med et medsendt CPR-nummer:

sd  eCPR - UpdatePerson




3. Designmålsætninger og -beslutninger

kvalitetsattributter:

modificerbarhed

skalerbart, flaskehalse, ingen single point of failure

vi laver en liste over hvad vi har fokus på - en checkliste vi anvender til at følge op

3.1. Designbeslutninger

3.1.1. Autentificeringsmetode for NotificationBroker

Spørgsmål: I sin nuværende (NAS1) udgave anvender NotificationBroker ikke DGWS til autentificering af indkommende requests. I stedet anvendes IP whitelisting. Skal dette også være således i NAS2, eller skal NotificationBroker anvende DGWS ligesom de andre NAS komponenter.

Overvejelser: Man kunne lave NotificationBroker i to udgaver (een med og een uden DGWS). Problemet er, at FMK i dag anvender snitfladen uden DGWS, og vil skulle gøre noget andet, hvis NotificationBroker pludselig bruger DGWS. Yderligere er der en forventning om, at IDWS kommer (også for NAS), så en ændring måske vil være spildt.

Beslutning: NotificationBroken anvender IP whitelisting som i dag - også i NAS2.

3.1.2. Topic mapping fra topic til internt topic (Kafka topic)

Leverandør synspunkter i forhold til om konvertering fra topic til internt topic skal være database baseret eller en konfigurerbar regel.

3.1.2.1. Database baseret

Fordele

  1. Man er ikke begrænset af at et givent Kafka topic får et bestemt navn i forhold til det topic man mapper fra.
  2. Når man skal validere om NotifyBroker Subscription Manager kaldes med et valid topic er det nemt at validere ved blot at lave et enkelt opslag i databasen.
  3. Det vil være muligt at nedlægge et topic i to skridt. Det skal forstås på den måde at man først kan slette topic fra databasen. Så vil det ikke længere være muligt at kalde NotifyBroker på det givne topic. Derefter vil subscribere have X antal dage til at få hentet alle data og så kan topic nedlæggges i Kafka.

Ulemper

  1. Man skal sikre sig at både internt topic er oprettet i både Kafka og mappings tabellen.

3.1.2.2. Konfigurerbar regel

Fordele

  1. Når man skal oprette et nyt topic skal dette kun gøres i Kafka.

Ulemper

  1. Når det skal valideres om NotifyBroker og Subscription Manager kaldes med et validt topic er der ikke nogen nem måde at gøre dette på. Umiddelbart kan man kun spørge efter alle topics i Kafka clustered og det må antages at dette er dyrere end at lave et enkelt opslag i en database.
  2. Man binder sig på én bestemt mapping fra topic til Kafka topic. Dette kan give problemer hvis man ønsker at oprette et bestemt topic og Kafka ikke understøtter dette navn.
  3. Driften har særlig navnekonvention i forhold til opretteles af topics. Denne er ikke som udgangspunkt compliant med NAS Topics opbygning, hvorfor der skal defineres og vedligeholdes en mapningsalgoritme

3.1.2.3. Alternativ model skitseret på Kommentarer til NAS 2.0 Design og Arkitekturbeskrivelse






Beslutning: Der anvendes mapping tabel. Roadmap opgave  NAS20-98 - Jira-projektet eksisterer ikke eller du har ikke tilladelse til at se det.  er registreret til at automatisere oprettelse af data i mapping tabel, topic i kafka osv. 

3.1.3. Kafka modul (Arosii)

Herunder findes beskrivelse af design omkring håndtering af nogle af brugsscenarier ifht. brugen af Kafka.

3.1.3.1. Offset håndtering og normal hentning

Når en klient henter beskeder/notifikationer på et pullpoint, skal der fortsættes fra der hvor klienten sidst slap. Denne information gemmes i nasConsumer i offset, som allerede beskrevet. Det kræver dog en mindre forklaring. Det er nemlig ikke blot eet offset, men en samling af flere offsets, der gemmes. Dette skyldes den måde Kafka clusteret bedst muligt udnyttes på. Når en besked afleveres til Notification Broker, vil den blive givet videre til Kafka clusteret og blive gemt i et af de partitioner, der er konfigureret. Der er skal som minimum være opsat det antal partitioner for et topic, som der er noder i clusteret. 

Beskeder vil blive uniform fordelt til partitioner tilhørende et topic. Når beskeder så skal hentes ud igen, skal en Kafka consumer, altså Pullpoint servicen, sættes op sådan, at den henter fra samtlige partitioner for et givet topic. Der skal hentes ud fra det offset, der er gemt for det pågældende pullpoint og partition. Når tilstrækkelige beskeder er hentet og der skal returneres til klient, gemmes nye offsets til det efterspurgte pullpoint i databasen igen.

Måden offsets gemmes på er i en "liste" i offset i nasConsumer. Da dette dog kun er en streng, skal der vælges passende formattering. Dette simplificeres af at partitioner altid angives med tallene fra 0 og opefter. En komma-separeret liste vil derfor være tilstrækkelig.

3.1.3.2. For sent hentning af beskeder

Når beskeder har ligget på Kafka i tilpas lang tid vil de blive slettet. Dette skal konfigureres sammen med topic. For klienter betyder dette at hvis de ikke beskeder hentes ud i tide, vil de blive slettet. I pullpoint servicen vil dette blive håndteret på den måde at offsets rundes op til starten af tilhørende partitioner.

3.1.3.3. Fjerne partitioner

Kafka understøtter ikke at fjerne partitioner. Derfor er det heller ikke noget NAS skal forholde sig til. 

3.1.3.4. Udvidelse af antal partitioner

Hvis et topic oplever meget trafik, så er det mulig tilføje yderligere partitioner. Denne udvidelse vil betyde at beskeder også skal hentes ud fra disse nye partitioner, når en klient igen forespørger på et pullpoint. Dette vil ske transparent i Kafka modulet, da der her altid checkes for antallet af partitioner for det givne topic. Offset for disse nye partitioner vil starte på 0. 

3.1.3.5. Oprettelse af subscribers (initiering af offset)

Når en subscription oprettes bliver der også oprettet en speciel række i nasConsumer, som ikke indeholder noget messageId. Denne række skal indeholde de aktuelle offsets for det tilhørende topic. Disse findes vha. Kafka modulet. 

Beslutning: Ovenstående er blot en beskrivelse af brugen af Kafka og dermed ikke nogen reel beslutning.

3.1.4. Databasemodel i forhold til PP (KIT)

I forhold til modelering af NasConsumer anbefales der at lave en databasemodel hvor der kun indsættes nye rækker. Det vil sige den bliver lidt mere event orienteret i og med at det er nye events der indsættes i databasen. Det giver nogle umiddelbare fordele og ulemper. 

Fordele

  • DGWS krav om at kunne gensende tidligere svar opfyldes nemt da historik over tidligere offset og antal beskeder i svar bibeholdes i databasen. 
  • Antallet af låsninger i databasen minimeres da man ikke skal låse eksisterende rækker i forbindel. 
  • I og med der ikke skal opdateres rækker giver det en lidt simplere kode. 

Ulemper

  • Hvis der ikke ryddes op i databasen, så vil data mængden blive ved med at vokse. Derfor skal der laves oprydning af tabellen.

3.1.4.1. Offset håndtering i databasen

Offsets i Kafka er reelt set blot en kommasepareret liste med offsets. Første værdi er offset for partition 0, anden værdi er offset for partition 1 osv. I databasen gemmes blot blot en "blob" og denne blob gives som parameter til Kafka modulet og det er Kafka modulet der så splitter den kommaseparerede liste. Når beskeder skal hentes via GetMessages tjekkes der først hvor mange partitioner der er for det Kafka topic der skal hentes data fra. Hvis der er det samme antal partitioner i Kafka som det der er gemt i databasen så læses hver partition blot og de nye offsets gemmes i databasen. Hvis der er flere partitioner end den offset blob der er gemt i database, så er det fordi der er tilføjet yderlige partitioner og disse skal også læses. Offset starter altid ved 0 og derfor læses de partitioner der ikke er offset for i databasen fra offset 0. Alle de nye offsets gemmes i databasen. Næste gang GetMessages kaldes vil antallet af offsets i databasen så igen passe med det antal partitioner der findes i Kafka. Da det ikke er muligt at fjerne partitioner for et Kafka topic så er det ikke et scenarie der håndteres. Hvis der alligevel er færre partitioner i Kafka end der er offsets i databasen returneres en alvorlig fejl. 

3.1.4.2. Alternative løsninger

Løsning 1

Nedenstående er umiddelbare fordele og ulemper i forhold til den model der er foreslået under punkt 2.6.5. 

Fordele

  • Databasen er delt mere logisk op i forhold til funktionalitet

Ulemper

  • Da vi kun indsætter data i tabellerne SubscriptionState og KafkaState så er det svært at afgøre hvilken række i de to tabeller der relaterer sig til hinanden. 
  • En lidt dyrere løsning performance mæssigt da der skal læses en tabel ekstra hver gang et pull point kaldes. 

Løsning 2




Nedenstående er umiddelbare fordele og ulemper i forhold til den model der er foreslået under punkt 2.6.5. 

Fordele

  • Databasen er delt mere logisk op i forhold til funktionalitet
  • Der er en direkte relation mellem tabellerne SubscriptionState og KafkaState og dermed kan man nemt se hvilken subscription state og kafka state der relaterer sig til hinanden.

Ulemper

  • En lidt dyrere løsning performance mæssigt da der skal læses en tabel ekstra hver gang et pull point kaldes. 

Beslutning: Model beskrevet under punkt 2.6.5 anvendes. 

3.1.4.3. Aflevering af notifikationer til Kafka

Requests mod notification broker kan indeholde et eller flere notifikationer. Disse notifikationer skal enten alle leveres videre Kafka eller ingen i tilfælde af fejl. Aflevering af kun nogle af de modtage notifikationer til Kafka er problematisk. De problematiske situationer kan yderligere begrænses til fejl ved kommunikation til Kafka. Validering af notifikationerne samt hentning af metadata fra databasen vil ske før Kafka kontaktes og er derfor ikke umiddelbart problematiske; disse er derudover også blot læse operationer.

Umiddelbart er der 2 måder at håndtere dette på:

  1. lade klienten vide at kun nogle beskeder er blevet gemt i Kafka og lade det være op til klienten at bestemme hvad der så skal gøres.
  2. anvende transaktioner ifht. levering af beskeder til Kafka. Hvis afleveringen på et tidspunkt fejler, vil ingen beskeder dermed blive gemt i Kafka og klienten kan roligt forsøge requestet igen.

I NAS 1.x anvendes transaktioner, hvorfor tilsvarende opførsel som beskrevet i 2 forefindes.

Løsning 1 har nogle problemer:

  • ændring af semantikken af snitflade ifht. NAS 1.x
  • ændring af snitflade ifht. WS-notification (ikke direkte undersøgt, men der findes ikke umiddelbart måder at fortælle klienten at kun nogle beskeder er blevet gemt)

Løsning 2 har ikke disse problemer, men vil afkræve yderligere ressourcer af Kafka. Det tyder umiddelbart på at performance ikke (betydeligt) er påvirket af introduktionen af transaktioner – dette er dog ikke testet.

Beslutning: Jvf. diskussion på Slack er det besluttet at der skal returneres en exception til kalder med information om hvilke beskeder der ikke kunne afleveres. 

3.1.5. Forskelle mellem WS-Notification og NAS2

WS-Notification er en familie af standarder bestående af:

NAS2 har til opgave (prioriteret rækkefølge):

  1. At udstille de samme snitflader som NAS1.
  2. At implementere WS-Notification

Prioriteringen ovenfor medfører, at NAS2 på visse punkter afviger fra WS-Notification standarden. Det følgende afsnit beskriver de væsentligste af disse afvigelser. Generelt henvides der til for anvenderguiden for detaljeret snitflade dokumentation af de områder hvor NAS2 implementerer yderlige begrænsninger af WS-Notification snitfalden. 

3.1.5.1. WS-BrokeredNotification og WS-Topicsde beskrivelsede beskrivelse

NAS1 implementerer ikke noget fra WS-BrokeredNotification og WS-Topics. Derfor er der heller ikke implementeret noget fra disse to standarder i NAS2. 

3.1.5.2. Format af notifikationspayload

Data i en Notification er domæne-specifik og ikke defineret som en del af WS-BaseNotification. NAS2 foreskriver (ligesom NAS1) at indholdet af en Notification skal være af typen NotifyContent. Dette er således en indskrænkning af WS-BaseNotification. Denne indskrænkning er ikke begrænsende, da NotifyContent igen kan indeholde hvad som helst (Any).

3.1.5.3. Push-style notificeringer

I følge WS-BaseNotification er det muligt for aftagere af notifikationer at oprette deres abonnement med en push-style semantik. Abonnenter giver en URL (endpoint), hvorpå NotificationBroker kan sende notificeringer (push). Specifikationen nævner en række tilfælde, hvor denne push mekanisme ikke er passende: "For example, certain NotificationConsumers are behind a firewall such that the NotificationProducer cannot initiate a message exchange to send the Notification. A similar circumstance exists for NotificationConsumers that are unable or unwilling to provide an endpoint to which the NotificationProducer can send Notification Messages. In other situations, the NotificationConsumer prefers to control the timing of receipt of Notification Messages, instead of receiving Notification Messages at unpredictable intervals, it may prefer to “pull” retrieve the Notification Messages at a time of its own choosing."

Hverken NAS1 eller NAS2 tilbyder denne type push notificeringer.

3.1.5.4. Ikke implementerede operationer

Nedenstående er en liste over de operationer der er defineret i WS-BASeNotification der IKKE er implementeret. Dette skyldes at disse heller ikke er implementeret i NAS1. 

  • ResumeSubscription fra PausableSubscriptionMabager
  • PauseSubscription fra PausableSubscriptionMabager
  • Unsubscribe fra PausableSubscriptionMabager
  • Renew fra PausableSubscriptionManager
  • GetCurrentMessage fra NotificationProducer

3.1.6. Poll timeout mod Kafka

Når der skal læses data fra Kafka sker det via et klient bibliotek. Når data skal læses sker det ved kald til KafkaConsumer.poll(timeout). Poll metoden returnerer så snart der er læst data fra Kafka eller timeout perioden er gået. Når poll kaldes, og der er data tilgængelig i Kafka, er der ingen garanti for at alle beskeder på et givent topic returneres. Der returneres blot et antal af beskeder. Man skal så kalde gentagende gange for at læse alle beskeder fra et givent topic. Hvis timeout sættes til 0 eller en meget lav værdi, så kan Kafka klient biblioteket ikke nå at hente data fra Kafka inden timeout perioden er gået og så er der ikke nogen garanti for at data på noget tidspunkt bliver hentet fra Kafka. 

Nuværende NAS2 logik

Nedenstående er groft sagt den logik der er omkring selve kaldet til poll(timeout) i NAS2. Timeout har været konfigureret til enten 0 eller et lavt antal milisekunder.

  1. Poll(timeout) kaldes.
    1. returneres der 0 beskeder så returneres der til kalderen. 
    2. Er det totale antal af beskeder hentet fra Kafka større end det maksimum brugeren ønsker, så returneres der til brugeren. 
  2. Gå til step 1

Med ovenstående logik så bliver poll(timeout) kaldt indtil der er læst nok beskeder fra Kafka eller poll(timeout) returnerer 0 beskeder. Det vil i langt de fleste tilfælde være timeout situationen der indtræffer først. Det betyder også at alle kald til GetMessages operationen har en svartid på minimum timeout værdien. Derfor har det også været ønsket at timeout skal være så lav som muligt. Det betyder at der er oplevet situationer hvor alle beskeder ikke er returneret til kalderen da timeout har været så lav at beskeder ikke er blevet læst fra Kafka. 

Løsningsforslag

Nedenstående er to løsningsforslag til at løse problematikken hvor alle beskeder ikke når at blive læst ud fra Kafka på grund af for lav timeout. 

Forhøje timeout

Forhøje timeout mod Kafka til 1 sekund. Efter en del test virker det stabilt at sætte timeout til 1 sekund når poll(timeout) kaldes. 

Fordele

  • Der skal ikke ændres i logik da det blot er en konfigurationsparameter der skal ændres. 
  • Der tilføjes ikke yderlige kompleksitet i koden. 

Ulemper

  • Alle kald til GetMessages får en svartid på minimum et sekund.
  • Hvis en timeout på et sekund ikke er nok får vi ikke nogen form for alarm. Dog anses det ikke sandsynligt at der er i normal situationer er svartider på mere end et sekund fra Kafka. Tænkte situationer hvor det kunne ske er voldsom belastning af Kafka cluster, netværksproblemer eller meget lange svartider fra disk system. I alle disse tilfælde bør det dog være noget der skal håndteres på anden måde. 

Ændre logik

Logikken omkring afhentning af beskeder fra Kafka ændres som beskrevet nedenfor. 

  1. KafkaConsumer.endOffsets(partitions) kaldes for at finde seneste offsets for det topic der skal læses data fra. 
  2. Seneste offsets sammenlignes med de offsets vi skal læses data fra. 
    1. Hvis seneste offsets er lig eller mindre end der hvor vi skal læse data fra, så returneres der til kalder. 
  3. Poll(timeout) kaldes med en passende timeout. Det kan f.eks. være et sekund. 
    1. Hvis offsets for de returnerede beskeder er større eller lig de offsets fremfundet i step 1 returneres hentede data til kalder. 
    2. hvis poll(timeout) ikke returnerede data betyder det at vi har ramt timeout, men vi ved at der er data i Kafka. Returner fejl til kalder. 
  4. Gå til Step 3.

Fordele

  • Vi kan logge og fortælle kalder hvis vi rammer timeout på poll metoden selvom der er data i Kafka. 
  • Svartider kan holdes på et minimum da vi kun rammer timeout i tilfælde af fejl. 

Ulemper

  • Der tilføjes øget kompleksitet til koden.

3.1.7. Udvidet poll timeout ved kald til Kafka når subscription er bagud

Ifm at anvenderne er skiftet fra NAS1 til NAS2 er det blevet tydeligt at den algoritme som NAS bruger til at kalde Kafka med ikke altid er optimal. Hvis en subscription er kommet bagud har anvenderne meget svært ved at indhente afsenderne. Det ser du til at den lave default timeout til poll kaldet gør at Kafka ofte returnerer 0 beskeder, hvilket får algoritmen til at tro den har nået enden af streamen og derfor stopper med at spørge.

Vi ønsker derfor 2 ændringer indført i Streamer implementationen:

Vælge poll strategi baseret på offsets

Vi ønsker at de endOffsets der læses i initConsumer metoden returneres herfra og gives med som input til KafkaIterator klassen. Denne kan derved bruge disse til at afgøre om subscriptionen er bagud i forhold til en ny property (kafka.poll.delta.max) og hvis dette er tilfældet vælge at anvende en ny poll metode der er mere aggressiv.
Den nye metode skal anvende en anden konfigurerbar poll timeout (kafka.poll.catchup.timeout) når den kalder Kafka. På den måde vil kun de subscriptions der er bagud belaste svartiderne.

Afslut iterator baseret på samlet svartid

Metoden hasNext() i klassen KafkaIterator skal anvende en ny konfiguration (kafka.polls.max.time) til at afgøre hvor længe de samlede kald til poll() metoden i Kafka må tage. Hvis tiden er overskredet skal metoden returnerer false så vi ikke får timeouts i loadbalancher mv.

Fordele

  • Anvender kan indhente det den er bagud

Ulemper

  • Hvis en anvender er bagud kan det give forhøjet svartid for denne anvender indtil vedkommende har indhentet det den er bagud. Det er dog nødvendigt for ikke at blive ved med at være bagud.
  • No labels