Ontwerp en implementatie van een framework om ...lib.ugent.be/fulltxt/RUG01/002/154/020/RUG01... ·...
Transcript of Ontwerp en implementatie van een framework om ...lib.ugent.be/fulltxt/RUG01/002/154/020/RUG01... ·...
Academiejaar 2013–2014 Faculteit Ingenieurswetenschappen en Architectuur
Valentin Vaerwyckweg 1 – 9000 Gent
Ontwerp en implementatie van een
framework om shopfloorberichten te
monitoren
Begeleiders: Jimmy HAEGEMAN
Raf KEGELEERS
Yves CHAVES
Jonas WILLE
Promotoren: dr. Leen BROUNS
Hein VANHOUTTEGHEM
Jens VANDECASTEELE
Masterproef voorgedragen tot het behalen van het diploma van
Master in de industriële wetenschappen: informatica
Powered by TCPDF (www.tcpdf.org)
VOORWOORD ii
Voorwoord
Als eerste zou ik graag dhr. Jürgen Blondeel willen bedanken die een stage op Volvo
mogelijk gemaakt heeft. Verder wil ik mijn begeleiders , Yves Chaves, Raf Kegeleers en
Jonas Wille, bedanken voor de hulp en bijdrage tot deze masterproef. Daarnaast wil ik
ook Tom Geens bedanken voor het nalezen van de masterproef en het uittesten van het
framework. Ook de andere collega's op Volvo ben ik dankbaar voor de aangename sfeer
op de werkvloer. Als laatste zou ik ook graag mijn interne promotor, Leen Brouns, willen
bedanken voor de feedback en het nalezen van de scriptie.
Bedankt.
Jens Vandecasteele, juni 2014
ABSTRACT iii
Abstract
In deze masterproef wordt een framework ontworpen om shop�oorberichten te monitoren.
Er wordt ook dieper ingegaan op de technische kanten van de implementatie. Als eerste
worden er twee ontwerpen met elkaar vergeleken. Bij het eerste ontwerp staat een actief- en
historiektabel centraal, bij het andere wordt er gebruikgemaakt van partitioning. Hierbij
is het vooral van belang dat de continue stroom aan berichten performant opgeslagen
kunnen worden op de databank en vervolgens eenvoudig en snel opvraagbaar zijn in tijden
van nood. Ten tweede werden er ook con�guratieschermen voorzien om de parsers in te
stellen die het bericht verwerken. Via deze schermen wordt ook de con�guratie verzorgd
van het opkuisprogramma dat periodiek loopt om oude records te verwijderen. Ten derde
werd er een loadbalancer ingezet om de werklast te verdelen over de verschillende processen.
Aansluitend werd dan ook het framework uitvoerig getest om de performantie na te gaan.
Tot slot volgt er een onderzoek naar interval-partitioning, de mogelijkheid om nadien te
parsen en het performanter maken van het opvragen van logberichten.
ABSTRACT iv
Abstract
This paper describes the design of a framework to monitor shop �oor messages. Fur-
thermore it contains technical details about the implementation. It all starts with the
comparison of two designs. The �rst design embodies an active and history table, the
other one uses partitioning. The most important requirement is to save a continuous �ow
of messages in a performant way. Afterwards these messages should be easily accessible in
times of need. Additionally, it had to be possible to con�gure the parsers and the clean-up
program, which periodically runs to delete old records. A load balancer was used to di-
minish the workload across multiple processes. The paper concludes with research about
interval partitioning, the ability to parse logging messages after saving them on database
and enhancing the performance of fetching logging messages.
INHOUDSOPGAVE v
Inhoudsopgave
Voorwoord ii
Abstract iii
Abstract iv
Inhoudsopgave v
1 Inleiding 1
2 Speci�eke Volvo-omgeving 3
2.1 SduClient - TClient - TServer . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Scheduled en detached batch . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 ValueList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.4 Taalonafhankelijkheid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.5 Beperkingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3 Ontwerp 7
3.1 Actief-historiek ontwerp . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.2 Partitioning ontwerp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Samenvatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4 Functionele analyse 12
4.1 Verwerken van binnenkomende berichten . . . . . . . . . . . . . . . . . . . 12
4.1.1 Loadbalancing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.1.2 Webservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.2 Gebruikersscherm voor parserbeheer . . . . . . . . . . . . . . . . . . . . . . 14
4.3 Gebruikersscherm om logberichten te bekijken . . . . . . . . . . . . . . . . 17
4.4 Periodiek partities verwijderen en toevoegen . . . . . . . . . . . . . . . . . 20
4.5 Beheerscherm voor partitiecon�guraties . . . . . . . . . . . . . . . . . . . . 22
4.5.1 Hoofdscherm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.5.2 Mappingscherm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
INHOUDSOPGAVE vi
4.5.3 Detailscherm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5 Partitioning 25
5.1 Algemeen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.2 Impact van onderhoudsoperaties . . . . . . . . . . . . . . . . . . . . . . . . 27
5.3 Partities toevoegen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
5.4 Partities verwijderen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.5 Create table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.6 Fetching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5.7 Opvolging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6 Technische analyse 33
6.1 Verwerking van binnenkomende berichten . . . . . . . . . . . . . . . . . . . 33
6.1.1 Loadbalancing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
6.1.2 Webservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
6.1.3 Overzicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
6.1.4 Parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
6.2 Gebruikersscherm voor parsers . . . . . . . . . . . . . . . . . . . . . . . . . 39
6.2.1 Dynamisch herladen van de parsers . . . . . . . . . . . . . . . . . . 39
6.3 Periodiek partities verwijderen en toevoegen . . . . . . . . . . . . . . . . . 41
6.3.1 Resterende tijd opvragen . . . . . . . . . . . . . . . . . . . . . . . . 42
6.3.2 Oude partities verwijderen . . . . . . . . . . . . . . . . . . . . . . . 42
6.3.3 Toevoegen van nieuwe partities . . . . . . . . . . . . . . . . . . . . 44
6.4 Beheerscherm voor partitiecon�guraties . . . . . . . . . . . . . . . . . . . . 46
6.4.1 Werking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.4.2 Zoeken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
7 Testing 48
7.1 DbUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
7.2 Liquibase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
7.3 EasyMock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
7.4 Partitioning testomgeving . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
7.4.1 Opstelling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
7.4.2 Resultaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
7.4.3 Bevindingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
7.4.4 Conclusie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
8 Resultaten 54
8.1 Stresstest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
8.1.1 Lokaal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
INHOUDSOPGAVE vii
8.1.2 VMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.1.3 Conclusie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.2 Opvragen logberichten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
8.2.1 Opstelling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
8.2.2 Bevindingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
8.2.3 Conclusie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
9 Uitbreidingen 58
9.1 Gebruikersscherm voor logberichten . . . . . . . . . . . . . . . . . . . . . . 58
9.1.1 Aanpassingen aan Hibernate . . . . . . . . . . . . . . . . . . . . . . 58
9.1.2 Opzoeken met JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . 60
9.2 Slimmer maken van partities toevoegen . . . . . . . . . . . . . . . . . . . . 62
9.3 Verplaatsing van de parsingfunctionaliteit . . . . . . . . . . . . . . . . . . 62
9.3.1 ScheduledExecutorService . . . . . . . . . . . . . . . . . . . . . . . 62
9.3.2 Java.util.Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
9.3.3 Quartz scheduler . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
9.4 Interval partitioning bij Oracle 11g . . . . . . . . . . . . . . . . . . . . . . 65
10 Algemene conclusie 67
A Databanktabellen 69
B Klassendiagram van webservice en loadbalancer 71
C PL/SQL insert testomgeving 72
D Resultaten testomgeving 74
E Resultaten Hibernate 79
F Resultaten lokale stresstest 81
G Resultaten OpenVMS stresstest 84
Bibliogra�e 85
L¼st van �guren 87
L¼st van tabellen 88
Lijst van codefragmenten 90
INLEIDING 1
Hoofdstuk 1
Inleiding
Deze masterproef werd uitgewerkt onder begeleiding van Volvo Car Gent. Volvo Car
Gent is één van de drie hoofdassemblagefabrieken van Volvo Car Corporation. De andere
assemblagefabrieken zijn Volvo Car Torslanda (Zweden) en Volvo Car Chengdu (China).
In Gent werken ongeveer 5000 werknemers aan vier verschillende Volvomodellen: de Volvo
S60, XC60 en de volledig vernieuwde V40 en V40 Cross Country. Om de productie van
wagens makkelijker op te volgen is er nood aan bijkomende software. De bedoeling is
om een framework te ontwikkelen dat shop�oorberichten1 monitort. Dit houdt in dat
er een continue stroom van logberichten opgevangen, verwerkt en opgeslagen zal worden.
Vervolgens moeten de verwerkte data eenvoudig geconsulteerd kunnen worden.
Concreet houdt dit het volgende in:
� Loggen van alle acties van de shop�oordevices2.
� Zorgen dat alle berichten geparset worden en opgeslagen worden in een databank.
� Zorgen dat de data ten gepaste tijde verwijderd worden.
� Ontwikkelen van een user interface met diverse selectiemogelijkheden om de logbe-
richten op te vragen voor een bepaald tijdsinterval, voor een bepaalde installatie of
voor een bepaalde wagen. Het resultaat moet zijn dat de data in een leesbaar formaat
getoond worden, zodat dit programma gebruikt kan worden door een niet-IT-pro�el.
� Ontwikkelen van een scherm waarin beheerd wordt hoe de verschillende berichten
geparset moeten worden om deze op een goede manier in de databank op te slaan.
1toestellen aan de productielijn sturen constant berichten uit die opgevangen moeten worden2toestellen aan de productielijn, bv. PLC, RFID antenne, lasrobot,...
INLEIDING 2
Als eerste wordt er wat meer uitleg gegeven over de speci�eke Volvo-omgeving. Vervolgens
worden er twee ontwerpen nader bekeken en vergeleken met elkaar om uiteindelijk het beste
ontwerp dieper uit te werken. In de verdieping wordt eerst kort uitgelegd wat de algemene
functionaliteit van de uiteindelijke componenten is. Vervolgens wordt het principe en de
impact van partitioning van Oracle uitgewerkt, gevolgd door de technische aspecten van
de eindcomponenten. Daarna volgen de resultaten van de implementatie, aangevuld met
bedenkingen en uiteindelijke keuzes.
SPECIFIEKE VOLVO-OMGEVING 3
Hoofdstuk 2
Speci�eke Volvo-omgeving
Op Volvo bestaan er verschillende systemen. De systemen waarop deze masterproef be-
trekking heeft zijn: CIP en SYS. CIP staat voor Common Information for Production en
is het productie sturings- en opvolgingssysteem van Volvo. SYS is afgeleid van SYSTEM
en bundelt alle algemene zaken zoals het framework, common libs, loginschermen, autho-
risaties, ... Elk systeem wordt onderverdeeld in projecten, gateways en utilities. Gateways
vallen uit de scope van deze masterproef. Utilities zijn modules die niet speci�ek gekop-
peld zijn aan projecten. Ze kunnen door alle andere modules gebruikt worden. Hier komen
vooral de gemeenschappelijke componenten in terecht. Projecten worden verder opgedeeld
in modules. Elk project heeft minstens één module en kan uit volgende modules bestaan:
� entity module: database access module (max 1)
� service module: business logica module (max 1)
� tclient module: visuele interface module (max 1)
� batch modules: achtergrondprocessen (meerdere mogelijk)
Figuur 2.1 geeft een overzicht van de projectstructuur bij Volvo. Elke module heeft als
parent de eProject-module. Deze module kan speci�eke con�guratie bevatten voor de on-
derliggende modules. De eProject-module heeft als parent de uBaseXXX-module, waarbij
xxx het systeem is. Deze module heeft een speci�eke con�guratie voor alle projecten en
modules in dit systeem. Hierin worden bijvoorbeeld de dataSources1 gede�nieerd naar
1object dat gebruikt wordt om te connecteren met een databank
2.1 SduClient - TClient - TServer 4
de juiste databank voor dat systeem. De uBaseXXX modules hebben allemaal als pa-
rent uBasePom, hierin staat de con�guratie voor alle systemen, projecten en modules (bv.
repositories, build goals, ...). Projectnamen beginnen steeds met de letter 'e'.
Figuur 2.1: Projectstructuur in Volvo-omgeving
2.1 SduClient - TClient - TServer
SduClient is een programma gemaakt door Volvo om user interfaces te ontwikkelen. Het
biedt de mogelijkheid om componenten naar het scherm te slepen, vergelijkbaar met de
toolboxfunctionaliteit van Visual Studio. Toch zijn er enkele verschillen merkbaar. Er
onderscheiden zich twee views: de Designview en de Treeview . De Designview geeft visueel
weer hoe de user interface er zal uitzien. Op de Treeview zijn alle gebruikte componenten
zichtbaar in een boomstructuur. Hierop staan ook de details van de componenten weerge-
geven. Een belangrijk voordeel van de Treeview is het tonen van de verborgen elementen,
deze worden namelijk niet weergegeven op de Designview. Nog een ander voordeel van de
Treeview, is de mogelijkheid om reeds bestaande componenten, inclusief hun kindercom-
ponenten, te kopiëren uit een ander SduClient-project. Om een voorbeeld te geven: het is
mogelijk om de statusbar en menubar te copy-pasten. Het platform ondersteunt ook meer-
taligheid. Bovendien kunnen er voorgede�nieerde types gebruikt worden die elementen uit
de databank voorstellen om herbruikbaarheid te promoten. Eens het design afgerond is,
kan er Java code gegenereerd worden om de user interface te gebruiken in de code. Om
2.2 Scheduled en detached batch 5
de gegenereerde code in de juiste map te plaatsen wordt WinMerge gebruikt. Indien de
bestanden al bestonden, geeft dit de mogelijkheid om beide versies met elkaar te verge-
lijken. Doordat er in de gegenereerde code commentaarlijnen voorzien zijn waartussen de
code-behind geplaatst kan worden, is het eenvoudig om mogelijke wijzigingen samen te
voegen.
Vanuit TClient kunnen de ontwikkelde user interfaces gestart worden. TServer is het
platform waarop de schermtransacties draaien.
2.2 Scheduled en detached batch
Scheduled batch programma's zijn programma's die op bepaalde tijdstippen worden opge-
start door externe triggers. Het programma erft steeds over van AbstractBatch. Dit zorgt
voor de juiste initialisatie van Spring en Hibernate en start de batch op via de executeBatch
methode.
Vcom batch programma's zijn programma's die continu in de achtergrond draaien. Ze rea-
geren op binnenkomende berichten van externe programma's. Ze verwerken deze berichten
en kunnen antwoorden terugsturen. De communicatie gebeurt via het messaging frame-
work VCOM. Vcom batches erven steeds over van AbstractVComBatch. Ook hier zorgt
dit voor de juiste initialisatie en wordt de batch gestart via de executeBatch methode.
In tegenstelling tot Scheduled batches blijven deze programma's draaien tot ze manueel
gestopt of herstart worden.
2.3 ValueList
Om programma's eenvoudig te con�gureren, worden con�guratieparameters opgeslagen in
de databank. Deze parameters kunnen dan gemakkelijk aangepast worden via een TClient-
scherm zonder in de programmacode te moeten neuzen.
2.4 Taalonafhankelijkheid
Door in programma's gebruik te maken van errorcodes wordt er taalonafhankelijkheid on-
dersteund. In TClient kunnen de errorcodes gelinkt worden met een bericht in verschillende
talen. Er kunnen berichtjes voorzien worden in vier talen: Nederlands, Engels, Zweeds en
2.5 Beperkingen 6
Chinees. Al naargelang de pro�elinstellingen van de eindgebruiker worden de foutmeldin-
gen dan in één van deze talen op het scherm weergegeven.
2.5 Beperkingen
Bij het deployen van de programma's moet er rekening mee gehouden worden dat de servers
OpenVMS draaien. Bovendien zal ook het aantal threads beperkt moeten blijven. Verder
wordt er bij de implementatie Java 6 gebruikt met Oracle 10.2g als databank.
ONTWERP 7
Hoofdstuk 3
Ontwerp
Voor het ontwerp van het framework werd er begonnen met een actief-historiek model. Na
grondig onderzoek werd er overgeschakeld naar partitioning. Dit hoofdstuk verduidelijkt
wat deze twee ontwerpen precies inhouden en waarom er uiteindelijk gekozen werd voor
partitioning.
Er zijn componenten die in beide ontwerpen voorkomen:
� een loadbalancer die webservicecalls verdeelt over meerdere processen,
� een webservice die de logberichten verwerkt,
� een beheerscherm om parsers te con�gureren,
� een beheerscherm om logberichten te bekijken,
� een programma dat periodiek oude data verwijdert.
3.1 Actief-historiek ontwerp 8
3.1 Actief-historiek ontwerp
Dit ontwerp steunt op het gebruik van twee tabellen om de logberichten op te slaan, een
actieve tabel en een historiektabel. De actieve tabel houdt alle records van de huidige
shift1 bij. De historiektabel houdt alle oudere logberichten bij. Dit wordt afgebeeld in
�guur 3.1 om duidelijk het verschil aan te tonen met het andere ontwerp. Het voordeel
van dit concept schuilt in het opvragen van informatie uit de huidige shift. Dit zal zeer
performant gebeuren doordat er slechts een beperkt aantal data voorkomen in vergelijking
met het totale pakket. Door het gebruik van twee tabellen ontstaan er wel enkele nadelen.
Ten eerste moet er een apart programma voorzien worden om de records te verplaatsen van
de actieve tabel naar de historiektabel. Bovendien blijft de performantie om records op te
vragen uit de historiektabel ongeveer even slecht. Het komt dus neer op een verplaatsing
van het probleem, hoewel er meestal enkel in de huidige shift zal gezocht worden. Ten
tweede zal er ook een UNION gebruikt moeten worden indien er over meerdere shifts
gezocht wordt.
Figuur 3.1: Tabeloverzicht voor het actief-historiek ontwerp
Bij dit concept zouden de webserviceprocessen de logberichten onveranderd opslaan in de
databank. Hierbij wordt geen parsing van het binnenkomend bericht toegepast, zodat
maximale performantie van het webserviceproces bekomen wordt. Figuur 3.2 geeft een
overzicht van de benodigde componenten.
1tijdsspanne van acht uur in een ploegensysteem
3.2 Partitioning ontwerp 9
Figuur 3.2: Eerste ontwerp via twee tabellen
3.2 Partitioning ontwerp
Het voordeel van dit ontwerp schuilt in het gebruik van slechts één tabel. Hierdoor is
het niet meer nodig om records te verplaatsen van de ene naar de andere tabel. Zoals
weergegeven in �guur 3.3, worden de gegevens opgedeeld in groepen, elke groep is één
werkshift. Dit zorgt ervoor dat verwijdering van de oude gegevens vereenvoudigt omdat
deze nu per groep (partitie) verwijderd kunnen worden. Bovendien zal Oracle proberen de
query's te optimaliseren door gebruik te maken van partition pruning (zie hoofdstuk 5).
Hierdoor kan bij opzoekingen in de huidige shift dezelfde performantie bekomen worden als
bij het andere ontwerp. Daarenboven zullen opzoekingen in vorige shifts sneller verlopen,
doordat er slechts een beperkt deel van de tabel bekeken zal worden.
Figuur 3.3: Tabeloverzicht voor het partitioning ontwerp
3.2 Partitioning ontwerp 10
Dit ontwerp ziet er op het eerste zicht eenvoudig uit, maar door de grote waaier aan
mogelijkheden brengt het heel wat onderzoek met zich mee. Een bijkomstig nadeel is dat
de partities op voorhand aangemaakt moeten worden. Dit kan samen met het verwijderen
van oude gegevens gebeuren. Bij dit ontwerp wordt er voor gekozen om het parsen van
de logberichten toch in de webservice uit te voeren. Zo wordt het eindeloos updaten van
de gepartitioneerde tabel voorkomen. Figuur 3.4 geeft een overzicht van de benodigde
componenten.
Figuur 3.4: Tweede ontwerp via partitioning
3.3 Samenvatting 11
3.3 Samenvatting
Uiteindelijk werd er gekozen voor het tweede ontwerp door de betere performantie, de grote
waaier aan mogelijkheden en het eenvoudig verwijderen van oude records. Tabel 3.1 zet
alles nog even op een rijtje.
Tabel 3.1: Vergelijking Actief-historiek ontwerp met partitioning ontwerp
Actief-Historiek Partitioning
Voordelen-e�ciënt opzoeken in de hui-
dige shift
-minder belasting op de web-
service
-e�ciënt opzoeken in de hui-
dige shift en de oudere shifts
(partition pruning)
-eenvoudig om oude gegevens
te verwijderen
-veel mogelijkheden
-volledig transparant
Nadelen-UNION
-verplaatsing van de records
uit de huidige shift noodzake-
lijk
-opzoekingen buiten de hui-
dige shift zijn traag
-meer belasting op de webser-
vice
-partities moeten op voorhand
bestaan
Verschil
componenten-update-programma
-move-programma
-programma nodig om nieuwe
partities aan te maken
FUNCTIONELE ANALYSE 12
Hoofdstuk 4
Functionele analyse
Dit hoofdstuk beschrijft wat er precies allemaal moet ontwikkeld worden zonder diep in
te gaan op de technische details. De details en de uitwerking worden in de volgende
hoofdstukken besproken. Concreet wordt er bij elke component wat meer uitleg gegeven
en wordt er verduidelijkt wat er verwacht wordt. De componenten worden in dezelfde
volgorde doorlopen als het pad dat het bericht a�egt.
4.1 Verwerken van binnenkomende berichten
Om de binnenkomende logberichten in de databank te krijgen, werden er twee concepten
gebruikt, meer bepaald een loadbalancer en een webservice. De webservice zorgt ervoor dat
de logberichten eenvoudig naar een url verstuurd kunnen worden, terwijl de loadbalancer
de werklast zal verdelen over meerdere instanties van de webservice.
4.1.1 Loadbalancing
De VEM1 doet een webservicecall waarbij het logbericht, het stationnummer2, het bo-
dynummer3 en het berichttype worden doorgegeven als JSON-string. JSON staat voor
JavaScript Object Notation en zorgt ervoor dat objecten voorgesteld kunnen worden in
tekstformaat. De loadbalancer vangt de webservicecall op en verwijst hem door naar een
ander proces op dezelfde computer. Dit zorgt ervoor dat de werklast verdeeld wordt over
de verschillende processen en bijgevolg sneller webservicecalls verwerkt kunnen worden
1Virtual Escort Memory , vormt de brug tussen de shop�oordevices en de Volvoprogramma's2identi�catie van de machine3identi�catie van de wagen
4.1 Verwerken van binnenkomende berichten 13
(Figuur 4.1). De loadbalancer is transparant voor de client. Dit betekent dat de gebrui-
ker niets merkt van de loadbalancer en het gevoel heeft dat hij rechtstreeks een van de
webservices aanspreekt.
Figuur 4.1: Schematisch overzicht van het principe van loadbalancing
4.1.2 Webservice
De webservice verwerkt de JSON-string door het station, het bodynummer, het berichttype
en het logbericht eruit te �lteren. Het berichttype geeft aan welke parser er gebruikt moet
worden. Vervolgens wordt het logbericht geparset met de juiste parser. De velden van
het verwerkte logbericht worden opgeslagen in de databank. Dit proces wordt schematisch
voorgesteld in �guur 4.2. De webservice omvat verschillende threads om de logberichten te
verwerken. Het aantal threads kan via een constante gewijzigd worden. De parsers kunnen
beheerd worden via een user interface (sectie 4.2).
Figuur 4.2: Schematisch overzicht van de verwerking van een binnenkomend bericht
4.2 Gebruikersscherm voor parserbeheer 14
4.2 Gebruikersscherm voor parserbeheer
Dit scherm biedt de mogelijkheid om nieuwe parsers aan te maken en reeds bestaande te
wijzigen of te verwijderen. In een spreadsheet worden alle bestaande parsers weergegeven.
De omschrijving van de kolommen is terug te vinden in tabel 4.1. Het is belangrijk dat het
type uniek is zodat er slechts één parser geassocieerd kan worden met een bepaald type.
Het unieke type wordt afgedwongen door het beheerscherm. Voor de eenvoud worden enkel
voorgede�nieerde formaten gebruikt die opgehaald worden uit een ValueList. Figuur 4.3
toont hoe het hoofdscherm eruitziet.
Figuur 4.3: Hoofdscherm van het parserbeheer
Het is mogelijk te zoeken op parsernaam, type en formaat. Op het hoofdscherm staat er ook
een validatieknop die het mogelijk maakt om een gede�nieerde parser te testen. Door een
parser te selecteren en op de validatieknop te klikken, wordt het validatiescherm geopend.
Dit nieuw venster bevat een textarea, een validatieknop en een lijst van gede�nieerde
itemParsers. In de textarea kan een voorbeeldbericht ingegeven worden om te controleren
of de itemParsers het gewenste resultaat leveren. De validatieknop genereert de resultaten
naast elk lijstitem. Het resultaat van een voorbeeldbericht is zichtbaar in �guur 4.4.
Het detailscherm wordt weergegeven in �guur 4.5. Hierin kunnen de itemParsers gede�-
4.2 Gebruikersscherm voor parserbeheer 15
Tabel 4.1: Omschrijving van spreadsheetvelden in beheerscherm voor parsers
Eigenschap Omschrijving
type het type waarvoor de parser gebruikt wordt
parser de naam van de parser
format het formaat van de parser, XPATH of STRING
updatedTimestamp het tijdstip waarop het logbericht is gewijzigd
updatedBy het personeelsnummer van de gebruiker die het laatst
wijzigingen aanbracht
Figuur 4.4: Validatescherm voor parsers
nieerd worden met behulp van een XPathexpressie4. De XPathexpressie wordt dynamisch
ingeladen bij het opstarten of refreshen van de webservices. Het refreshen gebeurt door
gebruik te maken van JMX (sectie 6.2.1). Zowel voor het XPATH-formaat als voor het
STRING-formaat wordt er achter de schermen XPATH gebruikt. Het STRING-formaat
wordt omgezet in XPATH door het logbericht te omvatten in een STRING-tag en ver-
volgens de substring-functie5 toe te passen. Een voorbeeld van een expressie voor het
STRING-formaat ziet er dan als volgt uit: substring(/STRING/text(),startindex,lengte).
4XML Path Language (XPATH) is een manier om velden uit een XML-bestand te �lteren5functie om een deelstring uit een string te halen
4.2 Gebruikersscherm voor parserbeheer 16
Hierbij geeft de startindex aan waar het eerste karakter van de te zoeken string zich be-
vindt (1-based). Een voorbeeld van een expressie voor het XPATH-formaat kan er als
volgt uitzien: /TAG/text(). De itemParsersleutels in het detailscherm komen overeen met
de attributen die gebruikt worden in de verpakkingsklasse van het logbericht. Bij het op-
slaan van een itemParser wordt er gecontroleerd als de XPathexpressie wel geldig is en als
de juiste expressie gebruikt werd afhankelijk van het formaat. Met andere woorden als
bij het STRING-formaat het eerste deel bestaat uit 'substring(/STRING/text(),' en het
XPATH-formaat eindigt op '/text()'.
In volgende gevallen wordt er een foutboodschap getoond:
� indien er een parser opgeslagen wordt voor een type die al een parser heeft,
� als er een itemParserveld leeg gelaten wordt,
� als de parser in tussentijd reeds aangepast werd,
� als de opgegeven XPathexpressie niet geldig is.
Figuur 4.5: Detailscherm van het parserbeheer
4.3 Gebruikersscherm om logberichten te bekijken 17
4.3 Gebruikersscherm om logberichten te bekijken
Deze user interface biedt de mogelijkheid om logberichten op te zoeken in de databank aan
de hand van enkele �lters. Er kan ge�lterd worden op station, bodynummer, berichttype
en werkshift. Veelal zullen enkel de records uit de huidige werkshift van belang zijn, maar
verder zoeken moest ook mogelijk zijn. Figuur 4.6 toont hoe het scherm er uitziet.
Figuur 4.6: Overzichtsscherm voor de logberichten
Filteren op station en bodynummer gebeurt via een editeerbare combobox die gevuld is
met alle bestaande stations, respectievelijk bodynummers. Als de gebruiker iets invult
in de combobox, wordt er versprongen in de suggestielijst. Het is ook mogelijk om te
zoeken op items die niet in de lijst voorkomen. Deze implementatie zorgt ervoor dat
zoeken via een 'equals' kan gebeuren en dat er bijgevolg sneller resultaten weergegeven
kunnen worden. Er werden radioknoppen voorzien om niet telkens de tijdsgrenzen te
moeten ingeven. De radioknoppen zorgen automatisch voor de juiste tijden in de twee
tijdslabels, start- en stoptijd. Ze bieden ook de mogelijkheid om de huidige shift tot en
met drie shiften daarvoor te selecteren. Er is ook een radioknop om de tijdslabels zelf
aan te passen. Deze tijdslabels kunnen eenvoudig ingesteld worden door op de knoppen
4.3 Gebruikersscherm om logberichten te bekijken 18
naast de tijdsaanduidingen te klikken. Er verschijnt dan een kalender waarop een tijdstip
kan geselecteerd worden (Figuur 4.7). De kalender is een bestaande component van Volvo
waarin de shiftinformatie reeds verwerkt is. De component bevat ook reeds testen op
begin- en eindtijdstip. Het formaat van de weergegeven tijd is, yyyywwEE$HH_mm_ss.
Deze voorstelling toont de week en de dag van de week. Zo duidt '2014071 22:00:00' op
maandag, week 7, om 22 uur. Het zoeken kan gestart worden door op ENTER te drukken
of handmatig op de zoekknop te klikken.
Figuur 4.7: Kalenderscherm om een tijdsspanne in te stellen
In een spreadsheet worden alle records weergegeven die voldoen aan de zoekcriteria. Tabel
4.2 geeft een omschrijving van de kolommen. Alle velden zijn read-only. Door eerst één of
meerdere records te selecteren en vervolgens op de detailknop te klikken, of te dubbelklikken
op een record, verschijnt er een detailvenster waar ook het logbericht in getoond wordt.
Tabel 4.2: Omschrijving van de spreadsheetkolommen in het logberichtenscherm
Naam Omschrijving
station de alfanumerieke naam van het werkstation
bodyNummer het bodynummer van de wagen
berichttype type van het bericht
timestamp het tijdstip waarop het logbericht is aangemaakt
Het aantal resultaten wordt beperkt via paginering omwille van de performantie en het
gebruikersgemak. Het maximum aantal records per pagina kan ingesteld worden in de
voorziene pagineringcomponent. Hierdoor zal het minder lang duren voordat de gebruiker
de resultaten te zien krijgt. Er werd ook een bovengrens ingesteld voor het aantal resultaten
4.3 Gebruikersscherm om logberichten te bekijken 19
per pagina en afgedwongen dat er op z'n minst één �lter ingevuld wordt, om te voorkomen
dat het scherm overspoeld wordt met data. In de statusbar rechtsonder wordt het aantal
opgehaalde records weergegeven.
In het detailscherm, �guur 4.8, worden dezelfde velden weergegeven als in het hoofdscherm:
station, bodynummer, berichttype en het tijdstip van aanmaak. Er is ook één bijkomend
veld, het logbericht. Het logbericht wordt indien mogelijk geformatteerd voorgeschoteld.
Alle velden zijn eveneens read-only. Ook hier wordt de standaard menubar voorzien, maar
zonder de knoppen voor toevoegen, opslaan en verwijderen. Aanpassen van logberichten
is namelijk nutteloos. Indien er meerdere records geselecteerd werden bij het oproepen
van het detailscherm, kan er tussen deze records genavigeerd worden door op de pijltjes
bovenaan de menubar te klikken.
Figuur 4.8: Detailscherm voor het tonen van logberichten
4.4 Periodiek partities verwijderen en toevoegen 20
4.4 Periodiek partities verwijderen en toevoegen
Om gemakkelijk nieuwe partities aan te maken werd er eerst een veld 'aantal toekomstige
partities' voorzien. Aan de hand van dit aantal kon WhatShift6 gebruikt worden om dy-
namisch de partitiegrenzen op te halen van de komende werkshifts. Deze implementatie
werd uiteindelijk aan de kant geschoven doordat weekendwerk niet ruim op voorhand ge-
pland kan worden. Ongepland weekendwerk zou als gevolg hebben dat logberichten van het
weekendwerk in de eerste partitie van de maandag zouden terechtkomen indien de week-
endpartitie niet bestaat. Om dit te voorkomen werden meteen alle mogelijke shifts voor
het weekend aangemaakt. Hierdoor kon WhatShift niet meer aangewend worden en was
er nood aan vaste tijdsgrenzen om nieuwe partities aan te maken. Deze grenzen konden
ingesteld worden in het gebruikersscherm voor partitiecon�guraties (sectie 4.5) en waren
voor elke con�guratie hetzelfde.
Ook dit concept werd uiteindelijk niet benut doordat de vaste grenzen niet overeenkwa-
men met de echte shiftgrenzen. Om toch grenzen te hebben die overeenkomen met de
shiftgrenzen, werd het werkschema gebruikt die alle mogelijke werkploegen vastlegt. Door-
dat er meerdere de�nities beschikbaar zijn, moet er een referentiepunt opgegeven worden
en rekening gehouden worden met de meest recentste versie.
Het is de bedoeling dat dit programma elk weekend loopt om de gede�nieerde partitie-
con�guraties uit te voeren. In deze con�guraties staat aangegeven hoeveel partities er
bijgehouden moeten worden. Er wordt altijd een week aan partities toegevoegd aan de
tabel, met de optie om meerdere weken toe te voegen door het veranderen van een con-
stante. Indien het programma afgevuurd wordt tijdens de week, creëert het programma
indien nodig de partities vanaf de maandag in de huidige week. Als het programma in het
weekend loopt, zullen de partities voor de komende week aangemaakt worden, startende
vanaf maandag.
Volgende eigenschappen zijn con�gureerbaar:
� Systeem
� Tabelnaam
� Optie om de aanmaak van nieuwe partities te forceren
� Optie om het verwijderen van oude partities te forceren
6dit is een programma om shiftinformatie op te vragen
4.4 Periodiek partities verwijderen en toevoegen 21
� Tijd die het programma maximaal nodig heeft om het programma vlekkeloos uit te
voeren
� Gewijzigd door
� Gewijzigd op
Dit programma werd zo generiek mogelijk gemaakt door de mogelijkheid om een systeem en
een tabel te kiezen. Bijgevolg kan dit programma later gebruikt worden om alle bestaande
gepartitioneerde tabellen te voorzien van partities en om verouderde data te verwijderen.
Partities moeten op voorhand bestaan alvorens er een record in die tijdsspanne wordt
toegevoegd. Indien dit niet het geval zou zijn, komt het record in de MAXVALUE-partitie
terecht. Hierin worden alle records verzameld die niet binnen een partitiegrens vallen. In
het geval dat alle records in de MAXVALUE-partitie zouden terechtkomen, verliezen we
het voordeel van partitioning, zijnde partition pruning (zie hoofdstuk 5).
Als voorzorgsmaatregel voor locking7 worden con�guraties enkel uitgevoerd als er geen
shift bezig is en als er nog genoeg tijd over is tot de aanvang van de eerstvolgende shift. De
shiftinformatie kan opgevraagd worden met WhatShift en bijgevolg kan ook de resterende
tijd berekend worden en aan de hand van de opgegeven maximale tijd bepaald worden als
de con�guratie al dan niet uitgevoerd mag worden. Om toch de mogelijkheid te bieden om
een con�guratie uit te voeren zonder deze voorwaarden, werden de forceeropties voorzien.
Bij het verwijderen van de partities wordt WhatShift wel gebruikt. Hierdoor wordt er
rekening gehouden met vakantiedagen zodat gegevens van voor de vakantie nog steeds
beschikbaar zijn na de vakantie, zelfs na het lopen van het programma. Dit komt doordat
enkel gewerkte shifts meegeteld worden en bijgevolg lege partities genegeerd worden.
De volledige con�guratie die uitgevoerd moet worden, wordt opgeslagen in een ini-bestand
die onderveeld is in verschillende secties. De namen van de secties in het ini-bestand kunnen
aangepast worden in de constantenklasse van de ePartition-services-module. Alle secties
zijn con�gureerbaar via het gebruikersscherm (Sectie 4.5). Indien er fouten optreden bij
het laden of het opslaan van het ini-bestand of bij het gebruik van WhatShift, worden de
nodige errors gelogd.
7toegang tot de databank blokkeren tot de operatie klaar is
4.5 Beheerscherm voor partitiecon�guraties 22
4.5 Beheerscherm voor partitiecon�guraties
4.5.1 Hoofdscherm
Dit scherm biedt de mogelijkheid om partitiecon�guraties en systeemmappings te behe-
ren. Het hoofdscherm, �guur 4.9, geeft aan als het con�guratiebestand gevonden werd.
Indien dit niet het geval is, wordt dit aangegeven in het rood en worden de zoek- en de-
tailknop onbeschikbaar gemaakt. Als het con�guratiebestand gevonden werd, wordt er
gecontroleerd als er reeds een systeemmapping aanwezig is. Pas als deze sectie bestaat,
zullen de knoppen beschikbaar worden. Om de systeemmapping te beheren, is er een apart
menu-item 'beheer' voorzien. Verder is het mogelijk om te �lteren op systeem en tabel.
In een spreadsheet worden alle bestaande con�guraties weergegeven. De kolommen zijn de
eigenschappen die vermeld werden in sectie 4.4.
Figuur 4.9: Hoofdscherm om partitiecon�guraties te beheren
4.5 Beheerscherm voor partitiecon�guraties 23
4.5.2 Mappingscherm
Dit scherm, �guur 4.10, biedt de mogelijkheid om de systeemmapping te beheren. Door
nieuwe mappings toe te voegen, zullen ze beschikbaar worden in het detailscherm. Er
werd een extra veld 'Rdb driver' voorzien voor systemen die de databank van OpenVMS
gebruiken.
Figuur 4.10: Scherm om systeemmapping te beheren voor partitiecon�guraties
4.5.3 Detailscherm
In het detailscherm, �guur 4.11, wordt er gebruikgemaakt van comboboxen om de systeem-
en tabelwaarden in te vullen. Dit zorgt ervoor dat de waarden zeker juist zijn. Indien
een bestaande con�guratie geopend wordt, zullen beide comboboxen ingeschakeld zijn.
Indien er een nieuwe con�guratie aangemaakt wordt, zal enkel de systeembox beschikbaar
zijn. Van zodra er hier iets in geselecteerd wordt, zal de tabelbox beschikbaar worden
met alle gepartitioneerde tabellen in het geselecteerde systeem als waarde. Eens de tabel
geselecteerd werd, zullen alle namen van de reeds bestaande partities getoond worden
in een spreadsheet. Eerst kwamen enkel de kolommen van het type TIMESTAMP in
aanmerking voor de kolombox, maar doordat het opvragen van metadata8 traag verliep,
wordt er hierop niet meer gecontroleerd. De overige velden - de maximum tijd en het
aantal shifts - worden gecontroleerd op numerieke waarden. De 'Force'-checkbox'en zorgen
ervoor dat de aangevinkte operatie voor die con�guratie toch uitgevoerd kan worden op
momenten dat er wel een shift aan de gang is, ook als er onvoldoende tijd is. Wijzigingen
of nieuwe con�guraties worden weggeschreven naar het ini-bestand.
8tabel- en kolomeigenschappen
4.5 Beheerscherm voor partitiecon�guraties 24
Figuur 4.11: Detailscherm om partitiecon�guraties te beheren
PARTITIONING 25
Hoofdstuk 5
Partitioning
Dit hoofdstuk geeft wat meer uitleg over het partitioning concept van Oracle, meer bepaald
de werking en de gevolgen. Partitioning houdt in dat records verdeeld worden in groepen
zodat opzoekingen zich kunnen beperken tot een aantal partities. Anders gezegd, de sql
statements worden geoptimaliseerd om slechts de nodige partities op te vragen en over-
bodige partities te elimineren. Dit principe heet partition pruning en wordt transparant
uitgevoerd. Door partition pruning zullen query's performanter uitgevoerd worden. Met
transparantie wordt bedoeld dat er geen extra toeters of bellen nodig zijn bij het uitvoeren
van een normale query.[10]
5.1 Algemeen
Hoewel het niet noodzakelijk is om elke partitie in een aparte tablespace1 op te slaan, heeft
het toch zijn voordelen. Partities opslaan in verschillende tablespaces zorgt voor:
� een verminderde kans op data corruption in meerdere partities,
� onafhankelijke back-ups en herstelling van elke partitie,
� controle over de mapping van partities over verschillende disks,
� een verbetering in beheerbaarheid, beschikbaarheid en performantie.
1abstractielaag tussen fysieke en logische data, via tablespaces kunnen data verdeeld worden over ver-
schillende harde schijven
5.1 Algemeen 26
Het maximum aantal partities dat een tabel kan hebben, is 1024K-1. Oracle biedt ver-
schillende methodes aan om een opdeling te maken in partities:
� Range partitioning
� List partitioning
� Hash partitioning
� Composite partitioning
Range partitioning verdeelt de tabel op in verschillende ranges. Dit is het meest gebruikte
type en maakt een opdeling aan de hand van een datum eenvoudig. Bij List partitio-
ning wordt de opdeling in partities gemaakt door een lijst van waarden op te geven. Dit
heeft als voordeel dat ongeordende en onafhankelijke data gegroepeerd kunnen worden.
Hash partitioning zorgt voor de opdeling van data waarbij er geen goede opdeling kan
gemaakt worden in ranges of lists. Hash partitioning kan gebruikt worden in het geval
dat de gegevens te zwaar geclusterd worden bij range partitioning of om performantiere-
denen. Composite partitioning is een combinatie van range partitioning met list of hash
partitioning als subpartitionering. Hierdoor kunnen de voordelen gecombineerd worden.
Er wordt gebruikgemaakt van RANGE partitioning doordat de shifts zorgen voor een ge-
lijke opdeling aan de hand van een timestamp. Ze zijn ook eenvoudig aan te maken door
een bovengrens en een partitiesleutel op te geven. In ons geval is de partitiesleutel de time-
stampkolom en de bovengrens telkens de stoptijd van een shift. Enkel tabellen die LONG
of LONG RAW kolommen bevatten, kunnen niet gepartitioneerd worden. Dit komt omdat
LONG data types enkel nog ondersteund worden voor neerwaartse compatibiliteit2. Oracle
raadt dan ook aan om bestaande LONG kolommen om te zetten in LOB kolommen3.[14]
Vervolgens bestaat de mogelijkheid om verschillende soorten indexen te gebruiken: non-
partitioned global indexes , range of hash-partitioned global indexes , en local indexes . Non-
partitioned global indexes zijn de standaard indexes die gebruikt worden en hebben invloed
op de hele tabel. Local indexes zijn indexes die op dezelfde manier gepartitioneerd zijn
als de gepartitioneerde tabel. Dit zijn de eenvoudigste indexes aangezien je niet zelf de
indexpartities moet beheren. Ze worden automatisch aangemaakt door de databank. Een
2ondersteunen van oudere versies3LOB kolommen zijn de nieuwe LONG kolommen en staan in voor het opslaan van grote blokken ruwe
data
5.2 Impact van onderhoudsoperaties 27
gepartitioneerde global index is een index gegroepeerd op een kolom verschillend van de ge-
partitioneerde tabel. Dit is met andere woorden een index waarop partitioning is toegepast.
Deze moeten dan wel zelf beheerd worden. De tabeloptie ENABLE ROW MOVEMENT
schakelt de optie aan om records te updaten, dit zou geen nevene�ecten mogen hebben
buiten de verandering in ROWID.[17] Deze optie is standaard uitgeschakeld.
5.2 Impact van onderhoudsoperaties
Met onderhoudsoperaties worden operaties bedoeld die aanpassingen aanbrengen aan de
tabel, zoals het verwijderen of het aanmaken van partities. Veel onderhoudsoperaties op
gepartitioneerde tabellen markeren de overeenkomstige indexen als 'UNUSABLE'. Vervol-
gens moet de volledige index herbouwd worden of in het geval van gepartitioneerde globale
indexen, elk van zijn partities. Door 'UPDATE INDEXES' te gebruiken bij een ALTER
TABLE opdracht zal er geen reconstructie van de indexen meer moeten gebeuren. De in-
dexen worden dan geüpdatet op het moment dat de onderhoudsopdracht wordt uitgevoerd.
Dit heeft volgende voordelen:
� De index is meer beschikbaar aangezien hij niet gemarkeerd wordt als 'UNUSABLE'
en bijgevolg moet de index ook niet herbouwd worden.
� Er is geen opzoeking nodig naar de namen van alle onbeschikbare indexes om ze te
herconstrueren.
Nog enkele opmerkingen:
� Door 'UPDATE INDEXES' te gebruiken zal de partition DDL statement4 langer
duren. Dit komt doordat de onbruikbare indexen rechtstreeks geüpdatet worden. De
extra tijd die hiervoor nodig is, moet vergeleken worden met de benodigde tijd om
beide acties apart uit te voeren. Een vuistregel hierbij is dat het sneller is om de
indexen te updaten als de grootte van de partitie kleiner is dan 5% van de grootte
van de tabel.
� De DROP, TRUNCATE en EXCHANGE operaties met 'UPDATE INDEXES' zijn
niet langer snelle operaties doordat de indexen meteen mee geüpdatet worden. Hier
moet er eveneens vergeleken worden met de uitvoertijd om de indexen te herbouwen.
4Data De�nition language, hiermee worden de CREATE, DROP en ALTER statements bedoeld.
5.3 Partities toevoegen 28
5.3 Partities toevoegen
Er kunnen enkel nieuwe partities toegevoegd worden na de hoogst gede�nieerde partitie
door middel van 'ALTER TABLE tabelnaam ADD PARTITION'. Hierbij worden de local
en global indexes niet beïnvloed en blijven ze bruikbaar. Er kan ook een nieuwe partitie
tussenin toegevoegd worden met 'ALTER TABLE tabelnaam SPLIT PARTITION'. Als de
te splitsen partitie data bevat, zullen de indexen als 'UNUSABLE' gemarkeerd worden. Dit
is zowel het geval bij local indexes, als bij global indexes. De 'UNUSABLE' markering kan
voorkomen worden door 'UPDATE INDEXES' toe te voegen aan de 'SPLIT PARTITION'-
statement. SPLIT PARTITION zorgt ervoor dat de records verdeeld worden over de twee
nieuwe partities volgens de opgegeven grens. Alles onder de grens komt in de eerste partitie
terecht, alles erboven in de tweede. Als er geen namen voor de nieuwe partities opgegeven
worden, zullen ze automatisch een naam krijgen (SYS_Pn, waarbij n een willekeurig getal
is).
1 ALTER TABLE vem_log_message SPLIT PARTITION other
2 at ( grenstimestamp ) INTO ( PARTITION "20140722" , PARTITION other )
3 UPDATE INDEXES;
Codefragment 5.1: Partitie toevoegen met SPLIT PARTITION en ALTER INDEX
Optimalizeren van SPLIT PARTITION
SPLIT PARTITION is een dure operatie doordat alle records van de op te splitsen partitie
gescand moeten worden en één voor één toegevoegd moeten worden aan de nieuwe parti-
ties. Bovendien moeten zowel local als global indexes geherconstrueerd worden als er geen
'UPDATE INDEXES' gebruikt wordt. In het geval dat één van de nieuwe partities alle
records bevat, kunnen er optimalisaties plaatsvinden. Dit zorgt voor een snelle splitsing,
vergelijkbaar met een ADD PARTITION operatie. Deze optimalisatie gebeurt enkel als
onderstaande voorwaarden voldaan zijn:
� Eén van de twee nieuwe partities moet leeg zijn.
� De niet-lege nieuwe partitie moet dezelfde opslagkarakteristieken5 hebben als de oor-
spronkelijke partitie.
5Dit heeft betrekking op composite partitioning (dezelfde subpartitionering hebben) en het gebruik van
LOB kolommen(tablespace, caching, logging,...)
5.4 Partities verwijderen 29
Als deze voorwaarden voldaan zijn, dan zullen alle global indexes bruikbaar blijven, zelfs
zonder 'UPDATE INDEXES' te vermelden. De local index partities overeenkomstig met de
nieuwe partities blijven ook bruikbaar zolang ze voor het splitsen ook bruikbaar waren.[12]
5.4 Partities verwijderen
Dit kan gebeuren met 'ALTER TABLE tabelnaam DROP PARTITION partitienaam'. Als
er local indexes aanwezig zijn dan wordt de overeenkomstige partitie van de local index
automatisch mee verwijderd. Alle global indexes, zowel gewone als partitioned global
indexes, worden als 'UNUSABLE' gemarkeerd tenzij 'UPDATE INDEXES' bij de operatie
gebruikt werd of als de te droppen partitie leeg is. Er zijn drie verschillende methodes voor
het verwijderen van partities van een tabel met global indexes:[11]
Methode 1
'ALTER TABLE tabelnaam DROP PARTITION partitienaam' gevolgd door 'rebuild'. In
het geval er range-partitioned global indexes gebruikt worden, moet elke index per partitie
herbouwd worden.
ALTER INDEX sales_area_ix REBUILD PARTITION jan99_ix;
ALTER INDEX sales_area_ix REBUILD PARTITION feb99_ix;
Deze methode is het meest geschikt voor grote tabellen waar de gedropte partitie een groot
deel van het geheel aan data bevat.
Methode 2
Gebruik het DELETE statement om eerst alle records van de partitie te verwijderen voor-
dat een 'ALTER TABLE tabelnaam DROP PARTITION partitienaam' opgeroepen wordt.
DELETE zorgt ervoor dat global indexes geüpdatet worden. Deze methode kan gebruikt
worden bij kleine tabellen, of voor grote tabellen waar de te droppen partities slechts een
klein deel van de totale data bevatten.
Methode 3
Gebruik 'UPDATE INDEXES' zodat de globale indexes automatisch geüpdatet worden.
5.5 Create table 30
5.5 Create table
Om partities te gebruiken moeten we bij het aanmaken van de tabel de partities de�niëren
(codefragment 5.2). Een bestaande tabel kan niet rechtstreeks omgezet worden in een
gepartitioneerde tabel. Er zijn meerdere stappen vereist indien dit gewenst zou zijn. Dit
moet dan gebeuren via ofwel DBMS_REDEFINITION ofwel 'EXCHANGE PARTITION'.
1 CREATE TABLE vem_log_message
2 ( messageId NUMBER(6)
3 , message VARCHAR2(100)
4 , t s TIMESTAMP
5 )
6 PARTITION BY RANGE ( t s )
7 ( PARTITION "20140711" VALUES LESS THAN (TO_DATE( ' 2014−02−10 08 : 00 : 00 ' , '
yyyy−MM−dd HH24 :MI : s s ' ) )
8 , PARTITION "20140712" VALUES LESS THAN (TO_DATE( ' 2014−02−10 16 : 00 : 00 ' , '
yyyy−MM−dd HH24 :MI : s s ' ) )
9 , PARTITION "20140713" VALUES LESS THAN (TO_DATE( ' 2014−02−11 00 : 00 : 00 ' , '
yyyy−MM−dd HH24 :MI : s s ' ) )
10 ) ;
Codefragment 5.2: Aanmaken van een tabel met partities
Codefragment 5.2 toont hoe een tabel met drie partities aangemaakt wordt. Elke partitie
de�nieert een bovengrens via VALUES LESS THAN ... . Dit wil zeggen dat alles wat
onder deze grens valt (exclusief grens zelf), maar boven een andere partitiegrens, in die
partitie terechtkomt. De naamgeving van de partities is vrij te kiezen, maar aangezien
de partities in ons geval een shift voorstellen, wordt dit op de standaard Volvomanier
voorgesteld onder de vorm van yyyywwEX waarbij yyyy het jaartal, ww de week van het
jaar, E de dag van de week en X de shift op een bepaalde dag voorstelt. Bv. '2014-02-
10 15:00:00' valt in partitie '20140712', want deze waarde is kleiner dan de bovengrens
van partitie '20140712' en groter dan de bovengrens van partitie '20140711'. Indien een
waarde boven de hoogst gede�nieerde bovengrens valt, in dit geval '2014-02-11 00:00:00'
van partitie '20140713', dan zal er een fout optreden. Bijgevolg zal bij het starten van een
nieuwe shift de volgende partitie reeds moeten bestaan. Door een partitie te de�niëren
met als bovengrens MAXVALUE, zal er geen fout optreden. De MAXVALUE-partitie zal
voor de zekerheid altijd aanwezig zijn. Toch is het beter dat de partities al op voorhand
bestaan zodat records meteen in de juiste partitie terechtkomen en achteraf geen onnodig
werk meer verricht moet worden.
5.6 Fetching 31
5.6 Fetching
Opvragen van records gebeurt op de gebruikelijke manier zoals bijvoorbeeld codefragment
5.3. Hierbij speci�ceren we dus best een tijdsinterval dat een shift voorstelt. Dit zorgt
ervoor dat er slechts in één partitie zal bezocht worden. Dit is dus perfect van toepassing
op het zoeken van logberichten aangezien daar altijd shiftintervallen meegegeven worden.
Oracle regelt het opzoeken van de juiste partitie achter de schermen, dit principe is dus
volledig transparant. Omdat de bovengrens niet in de partitie is meegerekend, kan het zoe-
ken met 'BETWEEN' in de 'WHERE'-clausule ervoor zorgen dat er meer dan één partitie
bekeken wordt. Dit komt doordat 'BETWEEN' zowel de ondergrens als de bovengrens
includeert.
1 s e l e c t *
2 from vem_log_message
3 where t s >= TO_DATE( ' 2014−02−10 16 : 00 : 00 ' , ' yyyy−MM−dd HH24 :MI : s s ' )
4 and t s < TO_DATE( ' 2014−02−11 00 : 00 : 00 ' , ' yyyy−MM−dd HH24 :MI : s s ' )
Codefragment 5.3: Fetchen van records uit partitioned table
5.7 Opvolging
Om na te gaan of er wel degelijk maar in één partitie gezocht werd, kan er gebruikgemaakt
worden van 'EXPLAIN PLAN FOR' (codefragment 5.4). Vervolgens kan het explainplan
opgevraagd worden met: select * from table(dbms_xplan.display).
1 exp la in plan f o r
2 s e l e c t * from vem_log_message where . . .
Codefragment 5.4: Explainplan opvragen van een query
Figuur 5.1 toont de uitvoer bij het opvragen van een explainplan. De uitvoer is afhankelijk
van hoeveel partities er aanwezig zijn en wat de zoekquery precies was. Hierbij geeft
'PARTITION RANGE SINGLE' aan dat er slechts één partitie gebruikt werd. Pstart en
Pstop geven ook aan welke partities er gebruikt werden. In het geval alle partities gebruikt
zouden worden, zou er 'PARTITION RANGE ALL' staan.
5.7 Opvolging 32
Figuur 5.1: Voorbeelden van de uitvoer van 'EXPLAIN PLAN FOR'
TECHNISCHE ANALYSE 33
Hoofdstuk 6
Technische analyse
Dit hoofdstuk gaat dieper in op elk van de besproken componenten. Het is de bedoeling
om de verschillende mogelijke strategieën op te lijsten en te besluiten welke strategie er
uiteindelijk gebruikt werd. Om dit te bereiken, zullen er codefragmenten voorzien worden
om de principes te verduidelijken.
6.1 Verwerking van binnenkomende berichten
6.1.1 Loadbalancing
Als loadbalancer wordt het pakket 'Malabarista' gebruikt. Dit pakket werd reeds in het
Volvo-framework geïntegreerd voor een ander project en kan dus handig herbruikt worden.
Malabarista heeft tot nog toe feilloos gewerkt en is stabiel onder zware belasting. Bovendien
is het ook mogelijk om zelf een strategie te schrijven voor het verdelen van de werklast.
Werking
Het programma Cpr981 wordt zowel gebruikt voor de webservice, als voor de loadbalan-
cer. Dit is dus één en dezelfde klasse, waarbij de opstartparameters bepalen welke functie
de instantie precies uitvoert. Indien de systeemeigenschap1 'type=server' wordt meegege-
ven, start de loadbalancer op. In het geval deze variabele weggelaten wordt, start er een
webservice op. Codefragment 6.1 beschrijft het opzetten en starten van de loadbalancer.
Voor het opzetten van de loadbalancer wordt de loadbalancerbuilder 2 van het Framework
1een variable die op het classpath meegegeven wordt bv. �Deigenschap=waarde2hulpklasse die de loadbalancer aanmaakt via de meegegeven parameters
6.1 Verwerking van binnenkomende berichten 34
gebruikt. Deze builder roept achter de schermen de main van Malabarista aan. Vooraleer
de loadbalancer aangemaakt kan worden, moeten er eerst nog enkele parameters meege-
geven worden. Als eerste moeten er poorten ingesteld worden waarnaar de loadbalancer
de webservicecalls mag doorgeven. Deze poorten worden gescheiden door een komma op
het classpath gezet via de variabele 'clients'. De waarde van deze variabele kan vervol-
gens opgehaald worden met System.getProperty('clients') en ontbonden worden met een
StringTokenizer. Bij het aanmaken van de loadbalancer moet ook een strategie meegegeven
worden. Round Robin is momenteel de enige techniek van loadbalancing die geïmplemen-
teerd is. Deze techniek zal elke client a�open in eenzelfde cyclus, m.a.w. als een client aan
de beurt was, dan zullen eerst alle andere clients een call moeten verkrijgen voordat hij
zelf weer aan de beurt komt.[16] Dit principe zorgt voor een gelijkmatige verdeling over
de webservices. Als uitbreiding zou er getest kunnen worden als een strategie die rekening
houdt met het aantal berichten in de queue van elke webservice, performanter is dan de
'Round Robin'-strategie. Het hostadres en de poort waarop de loadbalancer draait, wordt
opgehaald uit een ValueList.
1 Bui lder b = new Loadbalancer . Bui lder ( ) ;
2 Str ingToken ize r t ok en i z e r = new Str ingToken ize r ( c l i e n t s , " , " ) ;
3 /* add c l i e n tPo r t s to l oadba lance rBu i lde r */
4 whi le ( t ok en i z e r . hasMoreTokens ( ) ) {
5 t ry {
6 i n t c l i e n tPo r t = In t eg e r . pa r s e In t ( t ok en i z e r . nextToken ( ) ) ;
7 b . addDest inat ion ( InetAddress . getLoca lHost ( ) . getHostAddress ( ) , c l i e n tPo r t ) ;
8 l o gg e r . i n f o ( " loadba lanc ing s t a r t ed from "+serve rPor t+" to "+c l i e n tPo r t ) ;
9 } catch ( Exception e ) {
10 // . . .
11 }
12 }
13 /* use Round Robin */
14 b . s e tS t r a t egy ( LoadbalancerStrategy .ROUND_ROBIN) ;
15 /* bu i ld and s t a r t */
16 Loadbalancer l oadba lance r = b . bu i ld ( "*" , s e rve rPor t ) ;
17 l oadba lance r . s t a r t ( ) ;
Codefragment 6.1: Aanmaken van de loadbalancer
6.1 Verwerking van binnenkomende berichten 35
6.1.2 Webservice
Zoals eerder vermeld, wordt een webservice ook opgestart met Cpr981. Hiervoor wordt de
systeemeigenschap 'type' weggelaten en wordt er een poort meegegeven waarop de webser-
vice moet draaien (systeemeigenschap 'port'). Om de webservice te draaien wordt de Jetty
server gebruikt. Dit moet bij het opstarten vermeld worden door een systeemeigenschap
in te vullen (codefragment 6.2). Het aantal webservices kan vermeerderd worden door
meerdere instanties op te starten, elk op een andere poort.
1 System . se tProper ty ( "com . sun . net . h t tp s e rv e r . HttpServerProvider " , " org . mortbay
. j e t t y . j 2 s e 6 . Jet tyHttpServerProv ider " ) ;
Codefragment 6.2: Gebruik Jetty server vermelden
De Jetty server moet ook nog expliciet opgestart worden. Hierdoor zal de webservice
gepubliceerd worden zodat ze beschikbaar wordt op de voorgede�nieerde poort. Er werd
gekozen voor Jetty omdat deze server zeer lightweight is en bovendien eenvoudig is in
gebruik. Deze server doet ook zeker niet onder op vlak van performantie vergeleken met
andere applicatieservers.[5]
RemoteLoggingServer
De webservice zelf zit verpakt in een bestaande component, de RemoteLoggingServer. Het
opstarten van de webservice staat beschreven in codefragment 6.3. Vooraleer de Remo-
teloggingServer gestart wordt, wordt er een BlockingQueue geabonneerd aan de server.
Hierdoor zullen de binnenkomende berichten, geëencodeerd met JSON, op de queue te-
rechtkomen. De queue houdt rekening met concurrentie. De berichtjes in de queue worden
systematisch verwerkt door een aantal RecordInserters. RecordInserters zijn threads die
het logbericht verwerken en opslaan op de databank (codefragment 6.4). Het aantal threads
kan aangepast worden in een constante.
1 /** c r e a t e j e t t y s e r v e r */
2 Server j e t t yS e r v e r = new Server ( port ) ;
3
4 /* only l i s t e n to VEM type */
5 BlockingQueue<Str ing> queue = new LinkedBlockingQueue<Str ing >() ;
6 s e r v e r . sub s c r i b e (RemoteLoggingType .VEM, queue ) ;
7 i f ( l o gg e r . i s In foEnab l ed ( ) ) l o gg e r . i n f o ( " Server subsc r ibed ( type = VEM) . " ) ;
6.1 Verwerking van binnenkomende berichten 36
8
9 /* s t a r t RemoteLoggingServer */
10 s e r v e r . s t a r t ( host , port ) ;
11 i f ( l o gg e r . i s In foEnab l ed ( ) ) l o gg e r . i n f o ( "RemoteLoggingServer s t a r t ed at "+
host + " : " + port + " . " ) ;
12
13 /* s t a r t j e t t y s e r v e r */
14 j e t t yS e r v e r . s t a r t ( ) ;
15 i f ( l o gg e r . i s In foEnab l ed ( ) ) l o gg e r . i n f o ( " Je t tySe rve r s t a r t ed at "+port+" . " ) ;
16
17 /* s t a r t THREADS Reco rd In s e r t e r s to parse logmessages */
18 i f ( l o gg e r . i s In foEnab l ed ( ) ) l o gg e r . i n f o ( " S ta r t i ng "+ Cpr981Constants .THREADS
+" Reco rd In s e r t e r s . . . " ) ;
19 ExecutorServ i ce pool=Executors . newFixedThreadPool ( Cpr981Constants .THREADS) ;
20 i n s e r t e r s = new ArrayList<RecordInser ter >() ;
21 f o r ( i n t i = 0 ; i < Cpr981Constants .THREADS ; i++) {
22 Record Inse r t e r r i = new Record Inse r t e r ( queue ) ;
23 i n s e r t e r s . add ( r i ) ;
24 pool . execute ( r i ) ;
25 }
26 i f ( l o gg e r . i s In foEnab l ed ( ) ) l o gg e r . i n f o ( Cpr981Constants .THREADS +"
Reco rd In s e r t e r s s t a r t ed . " ) ;
Codefragment 6.3: Opstarten van de webservice
De take() methode in codefragment 6.4 zorgt ervoor dat slechts één thread het bericht
krijgt, de andere RecordInserters blokkeren tot ze zelf een bericht kunnen bemachtigen.[13]
Dit wordt visueel voorgesteld in �guur 6.1. Vervolgens wordt het bericht verwerkt en
opgeslagen op de databank door gebruik te maken van Hibernate.
1 @Override
2 pub l i c void run ( ) {
3 whi le ( t rue ) {
4 t ry {
5 /* wait t i l l message i s a v a i l a b l e */
6 St r ing message = queue . take ( ) ;
7 /* parse and s t o r e message on db */
8 } catch ( Inter ruptedExcept ion e ) {// . . .
9 }
10 }
11 }
Codefragment 6.4: RecordInserter
6.1 Verwerking van binnenkomende berichten 37
Figuur 6.1: Visualisatie van RemoteLoggingServer
6.1.3 Overzicht
Figuur 6.2 geeft het algemeen overzicht van de samenwerking tussen de loadbalancer en de
webservices. Ook de opstartparameters van elk proces worden aangegeven.
Figuur 6.2: Technisch overzicht van loadbalancer en webservices
6.1.4 Parsing
In elke RecordInserter wordt een Map voorzien met parsers. Eenzelfde map voor alle th-
reads is niet mogelijk omdat javax.xml.transform.Transformer.transform niet concurrent is.
Doordat de RecordInserter zich in een oneindige lus bevindt, zal deze Map slechts eenmalig
6.1 Verwerking van binnenkomende berichten 38
geïnitialiseerd worden. Op deze manier wordt er zo weinig mogelijk tijd verloren. Bij het
binnenkomen van het bericht wordt er gekeken naar het berichttype. Door dit veld als
sleutel te gebruiken in de parserMap, wordt de juiste parser verkregen. Het binnenkomend
bericht kan vervolgens verwerkt worden door de 'parse'-methode van de verkregen parser
op te roepen. Deze methode geeft eveneens een Map terug die de geparsete waarden bevat.
Hiermee worden alle lege velden van de verpakkingsklasse 'VemLogMessage' aangevuld,
maar er wordt niets overschreven.
Door gebruik te maken van het parserbeheerscherm, kunnen er eenvoudig nieuwe types
en parsers bijgevoegd worden. Achter de schermen wordt er gebruikgemaakt van een
XPathParser die bestaat uit een lijst van XPathItemParsers. Elke itemParser heeft zijn
eigen XPathexpressie. Codefragment 6.5 toont hoe een XPathItemParser aangemaakt
wordt en hoe er geparset wordt. Door een lijst van itemParsers te gebruiken staat het
concept open voor uitbreiding. Op de databank worden hiervoor twee verschillende tabellen
voorzien, VEM_PARSERCONFIG en VEM_PARSERITEMCONFIG. De beschrijving
van deze tabellen is te vinden in bijlage A. Om bij het STRING-formaat ook XPath te
kunnen gebruiken, werd er een STRINGTAG rond de geleverde string geplaatst.
1 pub l i c XPathItemParser ( S t r ing name , S t r ing expr e s s i on ) {
2 super ( ) ;
3 super . setName (name) ;
4 XPathFactory xPathfactory = XPathFactory . newInstance ( ) ;
5 XPath xpath = xPathfactory . newXPath ( ) ;
6 t ry {
7 t h i s . e xp r e s s i on = xpath . compi le ( exp r e s s i on ) ;
8 } catch ( XPathExpressionException e ) { // . . .
9 }
10 }
11 @Override
12 pub l i c S t r ing parse (Document document ) {
13 t ry {
14 re turn ( St r ing ) exp r e s s i on . eva luate ( document , XPathConstants .STRING) ;
15 } catch ( XPathExpressionException e ) { // . . .
16 re turn "" ;
17 }
18 }
Codefragment 6.5: Voorbeeld gebruik van XPath parsing
6.2 Gebruikersscherm voor parsers 39
6.2 Gebruikersscherm voor parsers
Om er voor te zorgen dat de itemParsersleutels overeenkomen met de sleutels voorzien
in de verpakkingsklasse 'VemLogMessage', werd er gebruikgemaakt van re�ectie. Vem-
LogMessage gebruikt intern een Map waarin de eigenschappen van het logbericht opgesla-
gen worden. De sleutels van de eigenschappen worden opgeslagen in constanten waarvan de
naam begint met 'KEY_'. Om de eigenschappen uit de Map op te vragen, zijn er getters
voorzien die gebruikmaken van de gede�nieerde constanten. Om er dus voor te zorgen dat
bij het parsen de juiste keys gebruikt worden, zodat dat de getters de juiste informatie
weergeven, werd er beroep gedaan op re�ectie om alle gede�nieerde sleutels dynamisch te
laden.
6.2.1 Dynamisch herladen van de parsers
Voor het dynamisch herladen van de parsers werd er gebruikgemaakt van Java Management
Extensions (JMX). JMX biedt de mogelijkheid om resources , onder de vorm van Managed
Beans (MBeans), te beheren terwijl ze aan het draaien zijn. Om dit te verwezenlijken
moeten de MBeans geregistreerd zijn bij een MBeanserver. De MBeanserver wordt beheerd
door een JMX agent . Het de�niëren van een MBean gebeurt door een interface te maken
met de naam van de implementerende klasse, gevolgd door 'MBean' (codefragment 6.6).
Om te voorkomen dat de interface en de implementor zich in dezelfde package moeten
bevinden, werd er een MXBean gebruikt.
1 pub l i c i n t e r f a c e WebserviceMXBean {
2 pub l i c void r e l o adA l lPa r s e r s ( ) ;
3 }
Codefragment 6.6: JMX: bean interface voor het herladen van parsers
Het registreren van de bean gebeurt in codefragment 6.7. In dit geval speelt de klasse
'Webservice' zowel de rol van MBean, als van JMX agent. De ObjectName is vrij te kiezen,
enkel het type moet overeen komen met de JMX-interfacenaam zonder 'MBean'. Door het
registeren zal de bean beschikbaar worden buiten de Java Virtual Machine (Java VM)
onder de opgegeven objectName waardoor de bean aanspreekbaar wordt via RMI3, HTTP
en SNMP4. In dit geval wordt er RMI gebruikt om de bean aan te spreken. Onderstaande
3Remote Method Invocation4Simple Network Management Protocol, protocol voor het beheer van apparaten in een netwerk
6.2 Gebruikersscherm voor parsers 40
parameters worden op het classpath meegeven om de MBeanserver op een vaste poort te
laten draaien (codefragment 6.8).
1 pub l i c c l a s s Webservice implements IWebservice , WebserviceMXBean{
2 @Override
3 pub l i c void s t a r t ( ) throws Exception {
4 // . . .
5 /* s t a r t up MBeanServer */
6 MBeanServer mbs = ManagementFactory . getPlatformMBeanServer ( ) ;
7 ObjectName name = new ObjectName ( "e_ngids . Cpr981 : type=Webservice " ) ;
8 mbs . registerMBean ( th i s , name) ;
9 }
10 }
Codefragment 6.7: Implementeren en registreren van bean voor JMX
1 −Dcom. sun . management . jmxremote . port=8451
2 −Dcom. sun . management . jmxremote . au thent i ca t e=f a l s e
3 −Dcom. sun . management . jmxremote . s s l=f a l s e
Codefragment 6.8: Classpath variabelen voor het draaien van de MBean server
Codefragment 6.9 beschrijft hoe de beans van buitenaf aangesproken worden. Er wordt
eerst een JMXServiceURL opgesteld met de hostnaam en de poort van de MBeanserver.
Eens er connectie gemaakt werd met de server, kan er een proxy-object opgezet worden om
de nodige methodes uit te voeren. In het geval van het herladen van de parsers, werden
alle MBeanservers op dezelfde poort van de webservice gestart plus 100. Een webservice
die bijvoorbeeld op poort 8351 draait, zal een MBeanserver op poort 8451 draaien hebben.
Het herladen van de parsers komt er op neer dat elke RecordInserter om beurt de oude
parsers verwijdert en vervolgens de parsers opnieuw inlaadt. Het om beurt herladen van
de parsers moet ervoor zorgen dat er zeker geen berichten verloren gaan.
Doordat enkel de loadbalancer weet op welke poorten de webservices draaien, werden de
JMX ports hardgecodeerd. Om dit op te lossen kan er eerst een JMX call gedaan worden
naar de loadbalancer, die op zijn beurt een JMX call doet naar elke webservice.[8]
6.3 Periodiek partities verwijderen en toevoegen 41
1 // f e t ch webserv ice por t s
2 va lu eL i s t = Va lueL i s tUt i l . g e tVa lue sForVa lueL i s tDe f in i t i on ( NextGenIdConstants
.DATABASE, NextGenIdConstants .VALUELIST_PARSER_JMX_PORTS) ;
3 f o r ( S t r ing port : v a l u eL i s t ) {
4 t ry {
5 JMXServiceURL ur l = new JMXServiceURL( " s e r v i c e : jmx : rmi : // "+HOST+"/
jnd i /rmi : // " + HOST + " : " + port + "/jmxrmi" ) ;
6
7 JMXConnector jmxConnector = JMXConnectorFactory . connect ( u r l ) ;
8 MBeanServerConnection mbeanServerConnection = jmxConnector .
getMBeanServerConnection ( ) ;
9 //ObjectName should be same as your MBean name
10 ObjectName mbeanName = new ObjectName ( "e_ngids . Cpr981 : type=
Webservice " ) ;
11
12 //Get MBean proxy ins tance , used to make c a l l s to r e g i s t e r e d MBean
13 WebserviceMXBean mbeanProxy = (WebserviceMXBean )
MBeanServerInvocationHandler . newProxyInstance ( mbeanServerConnection ,
mbeanName , WebserviceMXBean . c l a s s , f a l s e ) ;
14
15 mbeanProxy . r e l o adA l lPa r s e r s ( ) ;
16 jmxConnector . c l o s e ( ) ;
17 su c c e s s . add ( port ) ;
18 }
19 catch ( Exception ex ) {
20 // . . .
21 }
22 }
Codefragment 6.9: Herladen van parsers met behulp van een JMX bean
6.3 Periodiek partities verwijderen en toevoegen
De con�guraties voor het beheer van de partities worden opgeslagen in een ini-bestand. De
naam van het ini-bestand wordt opgeslagen in een ValueList. Bij het starten van Syk750,
het programma dat de partities toevoegt en/of verwijdert, zullen alle con�guraties overlo-
pen worden. Indien er momenteel een shift aan de gang is, zullen enkel de con�guraties
met een 'Force'-optie op true uitgevoerd worden. Indien er geen shift bezig is, worden alle
partitiecon�guraties uitgevoerd met voldoende tijd tot de volgende shift. Om het behe-
ren van het ini-bestand te vereenvoudigen, werd de 'PartitionIni'-klasse geschreven. Deze
6.3 Periodiek partities verwijderen en toevoegen 42
klasse gebruikt achter de schermen IniFramework. IniFramework werd in het Framework
van Volvo ingebouwd zodat toekomstig gebruik van ini-bestanden vereenvoudigd wordt.
De 'PartitionIni'-klasse is een wrapper rond het IniFramework met extra functionaliteit
voor het beheren van de partities. IniFramework maakt gebruik van Ini4j. Alle methodes
voor het verwijderen en toevoegen van partities zit verpakt in de 'PartitionService'-klasse,
ook de eerste implementaties - die uiteindelijk verworpen werden - zitten hier in verweven.
6.3.1 Resterende tijd opvragen
Om na te gaan hoeveel tijd er nog rest tot de volgende shift wordt WhatShift gebruikt.
Dit is een reeds bestaande component in een ander Volvo-project. WhatShift maakt het
mogelijk om shiftinformatie op te vragen door middel van een registratiepunt en een start-
tijdstip. Na een berekening zal WhatShift een ShiftInfo-object teruggeven waaruit de
status, de starttijd, de stoptijd en de naam van de shift afgeleid kan worden. Indien de
statuscode gelijk is aan 'OUT_OF_SHIFT', is er momenteel geen shift bezig. De start-
en stoptijd die hier meegegeven worden, zijn de tijden van de volgende shift. Hierdoor kan
de resterende tijd berekend worden en kan er bepaald worden of er nog genoeg tijd over is
om de con�guratie uit te voeren.
6.3.2 Oude partities verwijderen
Een bestaande partitie verwijderen, samen met al zijn records, gebeurt als volgt: ALTER
TABLE vem_log_message DROP PARTITION partitienaam. Om te weten welke partities
er precies gedropt moeten worden, waren er twee mogelijkheden:
� Gebruikmaken van WhatShift om de naam te achterhalen van X aantal shifts terug
en vervolgens alle vroegere shifts verwijderen door de namen op te zoeken.
� Gebruikmaken van SQL en alle shifts opvragen kleiner dan de huidige shift.
Oudste shifts opvragen met WhatShift
WhatShift kan gebruikt worden om de partitienaam van de shift op te vragen die X aantal
shifts in het verleden voorkwam. Dit kan gebeuren volgens codefragment 6.10. De terugge-
geven waarde wordt vervolgens gebruikt als parameter in de SQLquery van codefragment
6.11 om alle oudere shifts op te vragen. Het voordeel van WhatShift te gebruiken is dat er
ook rekening gehouden wordt met vakantieperiodes.
6.3 Periodiek partities verwijderen en toevoegen 43
1 pub l i c S t r ing getOldestPart it ionName ( i n t keepNShi f t s ) throws
SemanticException {
2 // s t a r t at cur rent s h i f t
3 Date tempRegDate = cu r r e n t Sh i f t I n f o . getStartTime ( ) ;
4 Sh i f t I n f o s h i f t I n f o= cu r r e n t Sh i f t I n f o ;
5 f o r ( i n t i =0; i< keepNShi f t s +1; i++) {
6 // f e t ch prev ious s h i f t s t a r t i n g from given time
7 Vor i g eSh i f t v o r i g e Sh i f t = new Vor i g eSh i f t ( regPoint , tempRegDate ) ;
8 // save s h i f t in fo rmat ion
9 s h i f t I n f o = vo r i g e Sh i f t . g e t S h i f t I n f o ( ) ;
10 // subt rac t 30 minutes to make sure the prev ious s h i f t i s f e t ched i f
i t e r a t e d again
11 tempRegDate= subtract30min ( s h i f t I n f o . getStartTime ( ) ) ;
12 }
13 // return p a r t i t i o n name
14 re turn s h i f t I n f o . getVolvoDate ( )+s h i f t I n f o . g e t Sh i f t ( ) ;
15 }
Codefragment 6.10: Shiftinformatie opvragen van X aantal shifts terug
1 SELECT PARTITION_NAME
2 FROM USER_TAB_PARTITIONS
3 WHERE TABLE_NAME = UPPER( ' vem_log_message ' ) and PARTITION_NAME < ?
Codefragment 6.11: Opvragen oudste shifts via shiftinformatie van WhatShift
Oudste shifts opvragen met enkel SQL
Deze methode maakt enkel gebruik van SQL. De query wordt weergegeven in codefragment
6.12. Hierbij zijn de eerste twee parameters de partitienaam van de huidige shift en is de
derde parameter het aantal shifts dat bewaard moet blijven. Dit levert hetzelfde resultaat
op als het vorige, maar er is slechts één query nodig. Deze methode heeft als belangrijk
nadeel dat er geen rekening gehouden wordt met vakantieperiodes. Dit wil zeggen dat als er
partities aangemaakt werden in de vakantieperiode dat er veel kans is dat de logberichten
van voor de vakantie verwijderd worden bij het lopen van het programma. Dit is niet het
geval bij het gebruik van WhatShift, aangezien WhatShift de vakantieperiode niet meetelt
als werkshifts. Het is wel mogelijk om de query uit te breiden zodat lege partities genegeerd
worden. Dit zou ongeveer hetzelfde e�ect moeten hebben als de WhatShift-methode.
6.3 Periodiek partities verwijderen en toevoegen 44
1 s e l e c t x .PARTITION_NAME
2 from USER_TAB_PARTITIONS x
3 j o i n USER_TAB_PARTITIONS y on x .PARTITION_NAME < y .PARTITION_NAME
4 and x .TABLE_NAME=y .TABLE_NAME
5 where x .PARTITION_NAME < ? and y .PARTITION_NAME < ?
6 and x .TABLE_NAME = UPPER( ' vem_log_message ' )
7 group by x .PARTITION_NAME
8 having count (1 ) > ?
Codefragment 6.12: Opvragen oudste shifts via SQL
Droppen
Eens de partitienamen opgehaald zijn, kunnen ze één voor één gedropt worden met volgende
query: ALTER TABLE vem_log_message DROP PARTITION ?.
6.3.3 Toevoegen van nieuwe partities
Doordat we gebruikmaken van een MAXVALUE-partitie kunnen we niet rechtstreeks een
ADD PARTITION uitvoeren. De nieuwe partitiegrens moet namelijk altijd hoger zijn dan
de laatst toegevoegde. Dit is niet mogelijk door de MAXVALUE-partitie. Om toch de
nieuwe partities klaar te zetten gebruiken we SPLIT PARTITION. Hierdoor zal Oracle de
bestaande records in de op te splitsen partitie verdelen over de twee nieuwe partities. Aan-
gezien de MAXVALUE-partitie normaal gezien leeg is, zal er altijd een geoptimaliseerde
split-operatie plaatsvinden.
Voor het uitvoeren van alle query's werd de 'PartitionCon�gDaoImpl'-klasse geschreven
die overerft van JdbcDaoSupport. Omdat de dao5 toegang moest hebben tot verschillende
databanken, wordt bij elke query de dataSource uit de cache gehaald of aangemaakt. Dit
mechanisme wordt getoond in codefragment 6.13.
5Data Access Object
6.3 Periodiek partities verwijderen en toevoegen 45
1 pr i va t e DataSource getDataSource ( S t r ing db) {
2 DataSource dataSource = dataSources . get (db) ;
3 i f ( dataSource == nu l l ) {
4 // c r ea t e new dataSource
5 dataSource = new HibernateBasicDataSource (db) ;
6 dataSources . put (db , dataSource ) ;
7 }
8 re turn dataSource ;
9 }
Codefragment 6.13: Opvragen van de juiste dataSource
Ophalen van het werkschema
Voor het ophalen van het huidige werkschema moet er rekening gehouden worden met het
registratiepunt (kursprd) en met de geldigheidsdatum. Een registratiepunt is een knoop-
punt of post op de weg die een auto a�egt gedurende het productieproces. Het is belangrijk
dat enkel de meest recentste gegevens opgevraagd worden. De gebruikte query wordt ge-
toond in codefragment 6.14. Hierbij is de parameter het gebruikte registratiepunt.
1 s e l e c t *
2 from tcp007 x
3 where x . kursprd= ? and x . dy4tkgeldv = ( s e l e c t max(y . dy4tkgeldv )
4 from tcp007 y
5 where x . idagweek = y . idagweek
6 and x . k s h i f t = y . k s h i f t
7 and x . kursprd = y . kursprd
8 )
9 order by x . kursprd , x . idagweek , x . k s h i f t
Codefragment 6.14: Opvragen recentste werkschema
Eens de 'WorkSchedule'-objecten opgevraagd zijn, kunnen de nieuwe partities aangemaakt
worden. Het is hierbij mogelijk dat een bovengrens van een partitie op de volgende dag
valt. Dit moet gecontroleerd worden door de begin- en eindtijd van het 'WorkSchedule'-
object met elkaar te vergelijken. Bij elke partitie die toegevoegd moet worden, wordt er
nagegaan als ze nog niet bestaat. Bovendien moet opgepast worden bij het werken met
Calendar-dagen aangezien zaterdag de eerste dag van de week is in plaats van maandag.
6.4 Beheerscherm voor partitiecon�guraties 46
6.4 Beheerscherm voor partitiecon�guraties
Het ini-bestand dat gebruikt wordt voor de partitiecon�guraties wordt opgedeeld in ver-
schillende secties. Codefragment 6.15 geeft een voorbeeld van een ini-bestand. De eerste
sectie is een systeemsectie. Hierin staan alle bestaande systemen die gemapt worden op
databanknaam. Deze mapping wordt gebruikt zodat de gebruiker de systeemnaam kan ge-
bruiken in plaats van de databanknaam. Alle secties erna stellen de verschillende partities
voor, samengenomen in een 'PartitionCon�guration'-sectie. De hoofdsectienamen kunnen
eenvoudig aangepast worden door de klasseconstanten van de 'PartitionIni'-klasse te wij-
zigen. De sectienaam van een con�guratie heeft volgend formaat: systeem.tabelnaam. Dit
vereenvoudigt het opzoeken (subsectie 6.4.2).
1 [ SystemConf igurat ion ]
2 CIP = DCP
3 SYS = DSY_NATIVE
4
5 [ Pa r t i t i onCon f i gu ra t i on /CIP .VEM_LOG_MESSAGE]
6 system = CIP
7 t ab l e = VEM_LOG_MESSAGE
8 f o r c e S p l i t = f a l s e
9 forceDrop = f a l s e
10 keepNprevShi f t s = 30
11 timeNeeded = 5
12 updatedBy = jvandec2
13 updatedTimestamp = . . .
Codefragment 6.15: Voorbeeld ini-bestand voor partitiebeheer
6.4.1 Werking
Om de werking van ini4j eenvoudiger te maken, werd er een wrapperklasse voorzien die
intern ini4j gebruikt, IniFramework. Deze klasse werd vervolgens in het Framework van
Volvo opgenomen zodat ook andere projecten er gebruik van kunnen maken. Vervolgens
werd er nog een klasse voorzien die intern de wrapperklasse gebruikt, PartitionIni. Deze
klasse zorgt voor een eenvoudige interface voor het beheren van de partitiecon�guraties.
6.4 Beheerscherm voor partitiecon�guraties 47
6.4.2 Zoeken
Het zoeken gebeurt in de PartitionCon�guration-sectie. De gede�nieerde partitiecon�gu-
raties kunnen opgevraagd worden met getPartition(). Aangezien de naam van de partities
alle �lterinformatie bevat, kan er eenvoudig gezocht worden naar de juiste partities. Dit
kan gebeuren door de sectienaam te splitsen op de plek waar het punt staat. Het linkerdeel
wordt dan vergeleken met het opgegeven systeem en het rechterdeel met de tabelnaam.
Het zoekalgoritme staat uitgewerkt in codefragment 6.16.
1 pub l i c Li s t<Part i t i onConf ig> search ( St r ing system , St r ing tab l e ) {
2 List<Part i t i onConf ig> l i s t = new ArrayList<Part i t i onConf ig >() ;
3 i f ( S t r i n gUt i l . i sStr ingEmpty ( system ) && St r i n gUt i l . i sStr ingEmpty ( tab l e ) ) {
4 // no f i l t e r s , f e t ch a l l
5 l i s t = g e tPa r t i t i o n s ( ) ;
6 }
7 e l s e i f ( ! S t r i n gUt i l . i sStr ingEmpty ( system ) && ! S t r i n gUt i l . i sStr ingEmpty (
t ab l e ) ) { // both f i l t e r s , f e t ch s e c t i o n
8 Par t i t i onCon f i g c on f i g = ge tPa r t i t i o n ( system+" . "+tab l e ) ;
9 i f ( c on f i g != nu l l ) l i s t . add ( c on f i g ) ;
10 }
11 e l s e {
12 f o r ( S t r ing partit ionName : getPart it ionNames ( ) ) {
13 /* s p l i t on point , reminder : regexp po int i s d i f f e r e n t , \\ needed */
14 St r ing [ ] s p l i t = partit ionName . s p l i t ( " \\ . " ) ;
15 i f ( ( ! S t r i n gUt i l . i sStr ingEmpty ( system ) && system . equa l s IgnoreCase (
s p l i t [ 0 ] ) ) | |
16 ( ! S t r i n gUt i l . i sStr ingEmpty ( t ab l e ) && tab l e . equa l s IgnoreCase ( s p l i t
[ 1 ] ) ) ) {
17 // match
18 l i s t . add ( g e tPa r t i t i o n ( partit ionName ) ) ;
19 }
20 }
21 }
22 re turn l i s t ;
23 }
Codefragment 6.16: Zoeken naar de juiste partitiecon�guratie
TESTING 48
Hoofdstuk 7
Testing
Dit hoofdstuk beschrijft wat er precies gebruikt werd bij het testen en hoe bepaalde zaken
getest werden. Allereerst werden de user interfaces handmatig getest. Verder werd DbUnit
en Liquibase gebruikt om code te testen die gebruikmaakt van een databankconnectie. Voor
het kiezen van de indexen werd er speciaal een testomgeving opgezet.
7.1 DbUnit
Om de DAO's te testen werd DbUnit gebruikt. DbUnit biedt de mogelijkheid om tijdelijk
enkele records toe te voegen zonder dat ze blijvend op de databank worden opgeslagen.
Deze records zijn gekenmerkt door een zelfopgegeven negatieve id. Vervolgens kunnen alle
methodes van de dao getest worden. Eens alles getest is, wordt de tabel hersteld naar zijn
oorspronkelijke staat. De toe te voegen records worden aangereikt in een xml-bestand.
7.2 Liquibase
Omdat DbUnit geen 'ALTER TABLE'-statements ondersteunt, werd er gebruikgemaakt
van Liquibase. Liquibase gebruikt changeSets om wijzigingen op de databank aan te
geven. Hierdoor kan er tijdelijk een gepartitioneerde tabel aangemaakt worden om alle
'ALTER TABLE'-statements en functionaliteit van Syk750 te testen. Na het uitvoeren
van de tests kan de aanmaak van de tabel ongedaan gemaakt worden door een rollback 1.
Het gebruik van Liquibase wordt getoond in codefragment 7.1. Doordat de 'createTable'-
tag van Liquibase geen partitionering ondersteunt, werd de 'sql'-tag gebruikt. Ook de
1tabel herstellen naar oorspronkelijke staat
7.2 Liquibase 49
'dropTable'-tag werd vervangen door een 'sql'-tag om de 'purge'-optie mee te geven. 'Purge'
zorgt ervoor dat de verwijderde gegevens niet in de prullenbak terechtkomen, maar direct
verwijderd worden. Codefragment 7.2 toont de changeSet die Liquibase inlaadt voor het
opzetten en afbreken van de tabel.
1 @Override
2 protec ted void onSetUp ( ) throws Exception {
3 super . onSetUp ( ) ;
4 pa r t i t i o n S e r v i c e = Par t i t i onSe rv i c eLoca t o r . g e tPa r t i t i o nS e r v i c e ( ) ;
5
6 conn = th i s . dataSource . getConnect ion ( ) ;
7
8 Database database = DatabaseFactory . g e t In s tance ( )
9 . f indCorrectDatabaseImplementat ion (new JdbcConnection ( conn ) ) ;
10 l i q u i b a s e = new Liqu ibase ( Pa r t i t i onSe rv i c eTe s t . c l a s s . getResource ( "/db−t e s tda ta . xml" ) . g e tF i l e ( ) . t oS t r i ng ( ) ,new Fi leSystemResourceAccessor ( ) ,
database ) ;
11 Contexts cont = nu l l ;
12 l i q u i b a s e . update ( cont ) ;
13 }
14
15 @Override
16 protec ted void onTearDown ( ) throws Exception {
17 super . onTearDown ( ) ;
18 Contexts cont = nu l l ;
19 l i q u i b a s e . r o l l b a ck (1000 , cont ) ;
20 conn . c l o s e ( ) ;
21 }
Codefragment 7.1: Opzetten van Liquibase
1 <databaseChangeLog
2 xmlns=" ht tp : //www. l i q u i b a s e . org /xml/ns/dbchangelog "
3 xmlns :x s i=" ht tp : //www.w3 . org /2001/XMLSchema−i n s t ance "
4 xs i : s chemaLocat ion=" ht tp : //www. l i q u i b a s e . org /xml/ns/dbchangelog
5 ht tp : //www. l i q u i b a s e . org /xml/ns/dbchangelog /dbchangelog −3.1 . xsd">
6 <changeSet author=" jvandec2 " id=" createTab leWithPart i t ionsv1 ">
7 <sq l>
8 c r e a t e t ab l e Pa r t i t i onSe rv i c eTe s t 2
9 (
10 VEM_MESSAGE_ID number (22) NOT NULL
11 , DATAPOINT VARCHAR2(32)
12 , IBODYNR VARCHAR2(32)
7.3 EasyMock 50
13 , TSREG TIMESTAMP
14 , MSG VARCHAR2(4000)
15 , MSG_TYPE VARCHAR2(100)
16 , PRIMARY KEY (VEM_MESSAGE_ID)
17 )
18 PARTITION BY RANGE (TSREG)
19 (
20 PARTITION "20141541" VALUES LESS THAN (TO_DATE( ' 2014−04−10 13 : 2 9 : 5 9 '
, ' yyyy−MM−dd HH24:MI:ss ' ) )
21 , PARTITION "20141542" VALUES LESS THAN (TO_DATE( ' 2014−04−10 21 : 2 9 : 5 9 '
, ' yyyy−MM−dd HH24:MI:ss ' ) )
22 , PARTITION "20141543" VALUES LESS THAN (TO_DATE( ' 2014−04−11 05 : 1 4 : 5 9 '
, ' yyyy−MM−dd HH24:MI:ss ' ) )
23 , PARTITION "20141551" VALUES LESS THAN (TO_DATE( ' 2014−04−11 12 : 1 4 : 5 9 '
, ' yyyy−MM−dd HH24:MI:ss ' ) )
24 , PARTITION "20141552" VALUES LESS THAN (TO_DATE( ' 2014−04−11 18 : 5 9 : 5 9 '
, ' yyyy−MM−dd HH24:MI:ss ' ) )
25 , PARTITION other VALUES LESS THAN (MAXVALUE)
26 )
27 </ sq l>
28 <ro l l b a ck>
29 <sq l>drop tab l e Pa r t i t i onSe rv i c eTe s t 2 purge</ sq l>
30 </ ro l l b a ck>
31 </changeSet>
32 </databaseChangeLog>
Codefragment 7.2: XML met changeSet gebruikt door liquibase
7.3 EasyMock
Om de te testen klasses onafhankelijk te maken van hun dependencies werd EasyMock
gebruikt. Hiermee kunnen methodes of klasses gemockt worden. Dit wil zeggen dat er op
voorhand ingesteld wordt wat een bepaalde methode of klasse moet teruggeven. Codefrag-
ment 7.3 toont hoe de resterende tijd gemockt wordt voor het testen van ParititonService.
1 pa r t i t i o n S e r v i c e = createMockBui lder ( Pa r t i t i o nS e r v i c e . c l a s s ) . addMockedMethod
( " ca l cu la t eT imeLe f t " ) . createMock ( ) ;
2 expect ( p a r t i t i o n S e r v i c e . ca l cu la t eT imeLe f t ( ) ) . andReturn ( 4 . 9 ) . t imes (1 ) ;
3 expect ( p a r t i t i o n S e r v i c e . ca l cu la t eT imeLe f t ( ) ) . andReturn ( 5 . 1 ) ;
4 r ep lay ( p a r t i t i o n S e r v i c e ) ;
Codefragment 7.3: Voorbeeld gebruik van EasyMock
7.4 Partitioning testomgeving 51
7.4 Partitioning testomgeving
7.4.1 Opstelling
Voor de testomgeving werd er een gepartitioneerde tabel aangemaakt met dezelfde struc-
tuur als de echte tabel. Hierbij werd een week aan partities aangemaakt waarbij de partities
op de weekdagen gevuld werden met 500.000 records. Dit komt neer op precies 7,5 miljoen
records. Vervolgens werden allerlei query's uitgevoerd om na te gaan wat de beste metho-
des zijn om in het uiteindelijke ontwerp te gebruiken. Om de gegevens aan te maken en
om snel opnieuw de gedropte records in te voeren werd er PL/SQL gebruikt. De scripts
werden uitgevoerd in Toad en de sqlquery's werden uitgevoerd in Squirrel. De bedoeling
was om een beeld te krijgen van de uitvoertijden van de verschillende soorten operaties.
De verschillende testcases worden beschreven in tabel 7.1. Enkel de 'equals'-operator werd
onder de loep genomen omdat enkel deze operator in de eindopstelling wordt gebruikt.
Tabel 7.1: Omschrijving verschillende testcases voor testomgeving
Omschrijving
TESTCASE 1 enkel een primary key (unique index)
TESTCASE 2 een index op de kolom waarop GEEN partitioning wordt uitgevoerd
TESTCASE 3 twee indexen op de kolommen waarop GEEN partitioning wordt
uitgevoerd
TESTCASE 4 een index op de kolom waarop WEL partitioning wordt uitgevoerd
TESTCASE 5 een gecombineerde index met de kolom waarop WEL partitioning
wordt uitgevoerd
TESTCASE 6 een local index op de kolom waarop GEEN partitioning wordt uit-
gevoerd
TESTCASE 7 een gecombineerde local index met de kolom waarop WEL partiti-
oning wordt uitgevoerd
TESTCASE 8 een local index op de kolom waarop WEL partitioning wordt uit-
gevoerd
7.4 Partitioning testomgeving 52
7.4.2 Resultaten
Tabel 7.2 vat de resultaten samen voor het verwijderen van een partitie. De andere resul-
taten werden ondergebracht in bijlage D.
Tabel 7.2: Verwijderen van een partitie: gemiddelden naast elkaar
UPDATE INDEXES ALTER INDEX
TESTCASE 1 21,681 62,276
TESTCASE 2 50,255 141,422
TESTCASE 3 61,932 257,254
TESTCASE 4 59,731 209,767
TESTCASE 5 39,323 154,148
TESTCASE 6 21,546 64,960
TESTCASE 7 22,291 64,750
TESTCASE 8 24,026 65,309
7.4.3 Bevindingen
De resultaten bij insert en delete komen bij elke testcase ongeveer overeen. Toch zorgt de
aanwezigheid van een index voor een lichte verhoging in uitvoertijd, maar dit is miniem.
Ook bij het splitsen van een partitie is er nauwelijks verschil in uitvoertijd. Het grootste
verschil zit vooral in het droppen van de partities. Hieruit kan geconcludeerd worden dat
het gebruik van 'UPDATE INDEXES' merkelijk beter presteert in vergelijking met de
'ALTER INDEX'- en de 'DELETE'-methode. Concreet kan vastgesteld worden dat de
methode met 'UPDATE INDEXES' ongeveer drie keer sneller is als de 'ALTER INDEX'-
methode. Er kan ook afgeleid worden dat elke extra index ongeveer evenveel extra tijd in
beslag neemt, 60 tot 80 seconden. Een gecombineerde index duurt ongeveer anderhalve
keer zo lang als een enkele index. Doordat local indexes niet onbeschikbaar worden bij
het droppen van een partitie, is de uitvoertijd vergelijkbaar met een opstelling waar enkel
een primary key constraint aanwezig is. Verder is er bij de opzoekingen ook niet echt veel
verschil op te merken tussen de performantie van globale en lokale indexen. Het verschil
in performantie bij aanwezigheid van een index is echter enorm.
7.4 Partitioning testomgeving 53
7.4.4 Conclusie
Local indexes genieten de voorkeur bij het beheren van de partities. Door 'UPDATE IN-
DEXES' te gebruiken zullen de indexen nooit onbruikbaar worden. De combinatie local
indexes met 'UPDATE INDEXES' is dan ook de uiteindelijk gebruikte combinatie. Aan-
gezien er bij de uitvoertijden van de selectiequery's relatief weinig verschil merkbaar was
tussen lokale en globale indexen, zullen lokale indexen ook op dit vlak voldoen. Bij de
uiteindelijke opstelling werd er dus gekozen voor lokale indexen op station, bodynummer
en berichttype.
RESULTATEN 54
Hoofdstuk 8
Resultaten
8.1 Stresstest
Om de verwerkingskracht van de loadbalancer en de webservices te testen, werd er een
stresstest ontwikkeld. Dit komt neer op een programma die zo snel mogelijk een hele
hoop berichten verstuurd. Om geen onderscheid te maken in XPath-berichten en String-
berichten, werden er telkens even veel berichten van beide verstuurt. Ondertussen werd
er nagekeken welke opstelling er uiteindelijk best gebruikt zal worden. Dit werd nagegaan
door het aantal webservices en het aantal threads te laten wijzigen. Deze opstelling werd
twee maal uitgevoerd, éénmaal lokaal en éénmaal op OpenVMS. Het lokaal uitvoeren van
de opstelling geeft een idee van de maximale doorvoer zonder belasting van andere proces-
sen. Het testen op OpenVMS geeft aan wat het programma zal doen op de uiteindelijke
omgeving en onder meer belasting van andere processen. De productieserver en de testser-
ver zijn hetzelfde type toestel, maar in de productieomgeving worden er meerdere servers
gebruikt, waardoor er minder werklast is. De speci�caties van de gebruikte systemen wor-
den opgelijst in tabel 8.1. Het eindelijke doel voor de verwerking van de berichten werd
geschat op maximaal twintig berichten per seconde.
Tabel 8.1: Vergelijking systeemspeci�caties voor de stresstest
Lokaal Server
Besturingssysteem Windows 7 Enterprise 64-bit OpenVMS
Type Dell OPTIPLEX 790 HP RX6600
Processor i5-2400 @ 3.10 GHz Intel Itanium 2
Geheugen 4 GB 96 GB
8.1 Stresstest 55
8.1.1 Lokaal
De resultaten zijn te vinden in bijlage F. Het is meteen duidelijk dat te veel threads of
processen zorgt voor een overbelasting van het systeem, waardoor er ook berichten verloren
gaan. Verder blijkt een opstelling met twee of drie webservices het stabielst te draaien. Er
kan ook afgeleid worden dat het verwerken van 300 berichten per seconde geen probleem
blijkt te zijn. Vanaf 350 berichten per seconde begint het systeem echter onstabiel te
worden.
8.1.2 VMS
De resultaten zijn te vinden in bijlage G. Het eerste wat opvalt is het grote verschil in
het verwerkte aantal berichten per seconde. Dit aantal ligt beduidend lager dan bij de
lokale tests, afgerond 90 berichten per seconde vergeleken met 300 lokaal. Vermoedelijk
komt dit door de hogere belasting van andere processen, wat minder het geval zal zijn in
productieomgeving. Bovendien brengt het verhogen van het aantal processen slechts een
kleine verhoging van de performantie met zich mee. Het verhogen van het aantal threads
zorgt voor crashes. Gezien de server meer geheugen heeft, zal het uitbreiden in processen
beter zijn dan het verhogen van het aantal threads. Het is ook belangrijk op te merken
dat er geen berichten verloren gaan.
8.1.3 Conclusie
Ook al ligt de verwerking van het aantal berichten per seconde lager op de testomgeving,
het doel om twintig berichten per seconde te verwerken is ruim bereikt. De lokale uitvoering
geeft een idee van het potentieel van de opstelling. Verder zullen twee of drie webservices
met elk vijftien threads volstaan als startopstelling. Het verhogen van het aantal threads
wordt best achterwege gelaten om onstabiliteit te vermijden.
8.2 Opvragen logberichten 56
8.2 Opvragen logberichten
8.2.1 Opstelling
De performantie van de uiteindelijke opstelling werd getest door opnieuw een week aan
testdata te voorzien en vervolgens een hoop selectiequery's uit te voeren met behulp van
het VemLogViewer-scherm. Hierbij werd er gevarieerd in het aantal records per pagina, in
het gebruik van �lters en de opzoekgrenzen. Door telkens te zoeken op verschillende waar-
den, werd caching zoveel mogelijk tegengegaan. De resultaten zijn te vinden in tabel 8.2
en bijlage E. Tabel 8.2 zet de resultaten van Native SQL en Hibernate naast elkaar bij op-
zoekingen in verschillende partities met het aantal records gelimiteerd tot 500. Vervolgens
werden de resultaten zonder �lters nog eens uitgetekend in een staafdiagram, weergegeven
in �guur 8.1.
8.2.2 Bevindingen
De relatief hoge uitvoertijden zonder �lters bij een volledige tabelscan via Hibernate is het
eerste wat opvalt. Het gebruik van �lters geeft wel een enorme performantiewinst. Verder
heeft het wijzigen van het aantal resultaten per pagina van 50 naar 1000, weinig of geen
impact. Ook het opzoeken in slechts één shift geeft een gigantisch verschil bij Hibernate,
maar dit is begrijpelijk aangezien het aantal records ook aanzienlijk afneemt. De verhou-
ding van het aantal records met de snelheid blijft ongeveer gelijk, ongeveer twee seconden
per opgevraagde partitie. Als de uitvoertijden van Hibernate naast deze met native SQL
gelegd worden, kan vastgesteld worden dat er wel degelijk een performantieverschil is. De
opzoektijden van Native SQL blijven min of meer constant, onafhankelijk van het aantal
partities, terwijl de opzoektijden van Hibernate lineair stijgen met het aantal partities. Dit
heeft te maken met de extra query die uitgevoerd wordt om het aantal records op te vragen,
in vergelijking met Native SQL waarbij het limiteren van de records achter de schermen
gebeurt.
8.2.3 Conclusie
Aangezien er vooral opzoekingen zullen gebeuren in de huidige shift, of de twee recentste
shifts, voldoet de opzoeksnelheid aan de eisen. Het verschil in performantie met Native
SQL is behoorlijk, maar voor het zoeken in één of twee partities valt dit relatief mee. Het
omschakelen naar Native SQL via JDBC is in eerste instantie niet nodig, tenzij er in de
toekomst vaker in grotere tijdsspannes gezocht zal worden.
8.2 Opvragen logberichten 57
Tabel 8.2: Vergelijking opzoektijden van Native SQL met Hibernate in eindopstelling met resul-
taten gelimiteerd tot 500 records
Scope Filter Native SQL Hibernate
één partitie geen 2,249 2,809
één partitie station 0,006 0,037
één partitie bodyNr 0,006 0,028
twee partities geen 2,383 4,578
twee partities station 0,018 0,028
twee partities bodyNr 0,009 0,028
zes partities geen 2,572 13,012
volledig geen 2,226 31,363
volledig station 0,006 0,030
volledig bodyNr 0,006 0,115
Figuur 8.1: Native SQL vs Hibernate: staafdiagram die uitvoertijd toont bij het aantal partities
UITBREIDINGEN 58
Hoofdstuk 9
Uitbreidingen
Dit hoofdstuk gaat dieper in op mogelijke uitbreidingen. Als eerste wordt er gekeken om
de logberichten sneller op te vragen. Vervolgens wordt er onderzocht wat er moet gebeuren
indien de functionaliteit van het parsen verplaatst zou worden naar een update-programma.
Er wordt ook dieper ingegaan op een uitbreiding van range partitioning, waarbij partities
automatisch aangemaakt worden.
9.1 Gebruikersscherm voor logberichten
Momenteel wordt bij het ophalen van de logberichten Hibernate gebruikt met paginering.
Het zou natuurlijk geen kwaad kunnen om over te schakelen naar JDBC of enige aanpassin-
gen te doen aan Hibernate om de performantie wat op te krikken. De volgende paragrafen
geven aan wat de verschillende mogelijkheden zijn. Indien er JDBC gebruikt zou worden,
moet er wel rekening mee gehouden worden dat paginering beschikbaar moet blijven om
de wachttijden te drukken.
9.1.1 Aanpassingen aan Hibernate
Scrollable resultset
Een scrollable resultset zorgt ervoor dat elke entiteit1 apart behandeld wordt. De 'evict'-
methode in codefragment 9.1 voorkomt dat het geheugen volgeraakt. Hierbij moet er wel
rekening gehouden worden dat er geen aanpassingen meer kunnen gebeuren aan de entiteit
zelf, maar aangezien we enkel records opvragen, zal dat geen probleem zijn. Er kan ook
1omzetting van record naar object
9.1 Gebruikersscherm voor logberichten 59
geen lazy fetching gebruikt worden. Lazy fetching wil zeggen dat eigenschappen van een
object pas opgehaald worden als ze nodig zijn. Indien dit af staat, zullen de eigenschappen
direct ingeladen worden bij het ophalen van het record. Het is dan ook logisch dat lazy
fetchen niet gebruikt kan worden, aangezien het object uit de cache gehaald is met de
'evict'-methode. SetReadOnly optimaliseert de query.[4]
1 Ses s i on s e s s i o n = se s s i onFac to ry . ge tCurrentSes s i on ( ) ;
2 S c r o l l a b l eR e s u l t s s c r o l l a b l eR e s u l t s = s e s s i o n . createQuery ( " from DemoEntity" )
. setReadOnly ( t rue ) . s c r o l l ( Scrol lMode .FORWARD_ONLY) ;
3 i n t count = 0 ;
4 whi le ( s c r o l l a b l eR e s u l t s . next ( ) ) {
5 DemoEntity demoEntity = (DemoEntity ) s c r o l l a b l eR e s u l t s . get ( ) [ 0 ] ;
6 // Process and wr i t e r e s u l t
7 s e s s i o n . e v i c t ( demoEntity ) ;
8 }
Codefragment 9.1: Gebruik van een Scrollable resultset met Hibernate
Stateless session
Ook hier kan een scrollable resultset gebruikt worden, maar in plaats van een normale
sessie te gebruiken, wordt er geopteerd voor een statusloze sessie. Hierdoor zal Hibernate
min of meer al zijn features uitschakelen, dwz. geen dirty detection2, geen lazy loading, ...
. Het enige wat Hibernate nog doet is records omzetten naar objecten met als voordeel
dat er niet manueel ge�usht of evicted moet worden. De werking wordt beschreven in
codefragment 9.2.
Deze methode heeft als nadeel dat er geen sessionfactory gebruikt kan worden om de sessie
op te starten. Omdat openStatelessConnection() automatisch een nieuwe java.sql.Connection
start, moet er gebruikgemaakt worden van de Hibernate Work API om de huidige connectie
te kunnen benutten. Dit voorbeeld is enkel goed voor read-only toepassingen, maar dat is
opnieuw geen probleem aangezien aangezien logberichten aanpassen geen gewenst gedrag
is. Deze manier is mogelijk een beter alternatief dan het eerste.[4]
2controle of entiteit overeenkomt met het record op de databank
9.1 Gebruikersscherm voor logberichten 60
1 s e s s i onFac to ry . ge tCurrentSes s i on ( ) . doWork(new Work( ) {
2 @Override
3 pub l i c void execute ( Connection connect ion ) throws SQLException {
4 S t a t e l e s s S e s s i o n s t a t e l e s s S e s s i o n = se s s i onFac to ry . op enS ta t e l e s s S e s s i on (
connect ion ) ;
5 t ry {
6 S c r o l l a b l eR e s u l t s s c r o l l a b l eR e s u l t s = s t a t e l e s s S e s s i o n . createQuery ( "
from DemoEntity" ) . s c r o l l ( Scrol lMode .FORWARD_ONLY) ;
7 i n t count = 0 ;
8 whi le ( s c r o l l a b l eR e s u l t s . next ( ) ) {
9 DemoEntity demoEntity = (DemoEntity ) s c r o l l a b l eR e s u l t s . get ( ) [ 0 ] ;
10 // Process and wr i t e r e s u l t
11 }
12 } f i n a l l y {
13 s t a t e l e s s S e s s i o n . c l o s e ( ) ;
14 }
15 }
16 }) ;
Codefragment 9.2: Gebruik van een Stateless session met Hibernate
9.1.2 Opzoeken met JDBC
Het gebruik van paginering met JDBC steunt op dezelfde principes als bij Hibernate.
Deze methode gebruikt een zelfgemaakte klasse 'PaginationHelper<E>' voor het ophalen
van een pagina. Intern wordt hierbij een ResultSetExtractor gebruikt om een pagina op te
vullen. De ParameterizedRowMapper<E> voorziet in het omzetten van records in objecten
(codefragment 9.3). Om dit principe verder te optimaliseren kan het aantal records per
pagina gecachet worden. Dit vermijdt het nodeloos opzoeken van het aantal pagina's indien
het aantal resultaten per pagina niet aangepast werd. Bovendien kan er ook een scrollable
resultset gebruikt worden.[1]
9.1 Gebruikersscherm voor logberichten 61
1 pub l i c c l a s s Paginat ionHelper<E> {
2
3 pub l i c Page<E> fetchPage (
4 f i n a l JdbcTemplate j t ,
5 f i n a l S t r ing sqlCountRows ,
6 f i n a l S t r ing sqlFetchRows ,
7 f i n a l Object args [ ] ,
8 f i n a l i n t pageNo ,
9 f i n a l i n t pageSize ,
10 f i n a l ParameterizedRowMapper<E> rowMapper ) {
11
12 // determine how many rows are a v a i l a b l e
13 // c a l c u l a t e the number o f pages
14 // c r ea t e the page ob j e c t
15 // f e t ch a s i n g l e page o f r e s u l t s
16 f i n a l i n t startRow = (pageNo − 1) * pageS ize ;
17 j t . query ( sqlFetchRows , args , new Resu l tSetExtractor ( ) {
18 pub l i c Object extractData ( Resu l tSet r s ) throws SQLException ,
DataAccessException {
19 f i n a l L i s t pageItems = page . getPageItems ( ) ;
20 i n t currentRow = 0 ;
21 whi le ( r s . next ( ) && currentRow < startRow + pageS ize ) {
22 i f ( currentRow >= startRow ) {
23 pageItems . add ( rowMapper .mapRow( rs , currentRow ) ) ;
24 }
25 currentRow++;
26 }
27 re turn page ;
28 }
29 }) ;
30 re turn page ;
31 }
32
33 }
Codefragment 9.3: Paginering met behulp van JDBC
9.2 Slimmer maken van partities toevoegen 62
9.2 Slimmer maken van partities toevoegen
Als eerste zou er een functie voorzien kunnen worden die alle partities in de toekomst
verwijdert. Dit kan bijvoorbeeld handig zijn bij het instellen van een nieuw werkschema.
Als volgende feature zou er bij het verkrijgen van een fout bij het toevoegen van een
nieuwe partitie, gekeken kunnen worden of het werkschema veranderd is. Indien dit het
geval is, kan de bestaande partitie verwijderd worden en vervolgens opnieuw toegevoegd
worden met de nieuwe grens. Bovendien wordt momenteel enkel de MAXVALUE-partitie
gesplitst. Bij het toevoegen zou er bijgevolg gecontroleerd kunnen worden of er nog een
andere partitie tussen de nieuwe, toe te voegen partitie en de MAXVALUE-partitie ligt.
Als deze situatie zich voordoet, moet de tussenliggende partitie opgesplitst worden in plaats
van de MAXVALUE-partitie. Momenteel wordt ervan uitgegaan dat er nooit een partitie
tussen ligt.
9.3 Verplaatsing van de parsingfunctionaliteit
Indien er toch zou beslist worden om het parsen niet rechtstreeks in de webservice uit
te voeren, maar achteraf via een update, kan volgend onderzoek gebruikt worden. Hier-
bij wordt er wat meer uitleg gegeven over een ScheduledExecutorService, de verschillen
met java.util.Timer en een uitwerking van Quartz. Het verplaatsen van de functionaliteit
heeft als voordeel dat de webservice minder belast wordt en dat de berichten dus sneller
opgeslagen kunnen worden op de databank.
9.3.1 ScheduledExecutorService
De werking van een ScheduledExecutorService wordt beschreven in codefragment 9.4. Bij
initialisatie wordt een threadpool aangemaakt met het aantal threads als parameter.[9] De
MessageHandler erft over van Runnable om de logica af te scheiden. Spring kan eventueel
gebruikt worden om de handler te injecteren. Het volledige proces draait continu als Ab-
stractVComBatch. Het parsen van de opgehaalde records kan op dezelfde manier gebeuren
als bij de RecordInserter.
9.3 Verplaatsing van de parsingfunctionaliteit 63
1 import s t a t i c java . u t i l . concurrent . TimeUnit . * ;
2 pub l i c c l a s s MessageHandlerBatch extends AbstractVComBatch<Str ing , Str ing >{
3 @Override
4 protec ted void be foreExecute ( ) {
5 f i n a l ScheduledExecutorServ ice s chedu l e r = Executors .
newScheduledThreadPool (15) ;
6 f i n a l Runnable msgHandler = new MessageHandler ( ) ;
7 f i n a l long INITIAL_DELAY = 10 ;
8 f i n a l long PERIOD = 60 ;
9 f i n a l ScheduledFuture<?> con t r o l = schedu l e r . scheduleAtFixedRate (
msgHandler , INITIAL_DELAY , PERIOD, SECONDS) ;
10 }
11 }
12
13 pub l i c c l a s s MessageHandler extends Runnable{
14 @Override
15 pub l i c void run ( ) {
16 // f e t ch XX reco rd s
17 // parse r e co rd s
18 // i n s e r t r e co rd s
19 }
20 }
Codefragment 9.4: Uitwerking met ScheduledExecutorService
9.3.2 Java.util.Timer
Het gebruik van de java.util.Timer is ook een mogelijkheid, maar die heeft zo zijn tekortko-
mingen ten opzichte van ScheduledExecutor.[3] Het voordeel is dat ze beide built-in3 zijn
en dat ze weinig con�guratie vergen om te gebruiken. Tabel 9.1 zet de verschillen even op
een rijtje.
9.3.3 Quartz scheduler
Met een eenvoudige con�guratie zoals beschreven in codefragment 9.5, zou hetzelfde re-
sultaat bekomen moeten worden als met de ScheduledExecutor. Quartz staat wel meer
open voor con�guratie.[6] Het biedt wel een alternatief voor latere uitbreiding, indien meer
con�guratie zou nodig zijn. Bijkomend is wel dat de nodige dependencies moeten voorzien
3ingebouwd in Java
9.3 Verplaatsing van de parsingfunctionaliteit 64
Tabel 9.1: Java.util.Timer versus ScheduledExecutor
Situatie Java.util.Timer ScheduledExecutor
Aanpassen van systeemklok Kan problemen opleveren. Geen probleem.
Aantal threads Slechts één, bijgevolg kun-
nen lang draaiende taken
andere taken ophouden.
Een con�gureerbaar aantal
die elk volledig controleer-
baar zijn.
RuntimeException Beëindigt de timer zodat er
geen taken meer uitgevoerd
worden.
Vangt de exceptie op met
de mogelijkheid om ze zelf
te verwerken. Andere ta-
ken blijven gewoon doorlo-
pen. (threadpool)
worden, wat niet het geval is bij de Executors, aangezien deze standaard verweven zitten
in Java 6. Voor de eenvoud worden er dus best ScheduledExecutors gebruikt.
1 pub l i c c l a s s MessageHandlerBatch extends AbstractVComBatch<Str ing , Str ing >{
2 @Override
3 protec ted void be foreExecute ( ) {
4 f i n a l long INITIAL_DELAY = 10000; //ms
5 f i n a l long PERIOD = 30000; //ms
6 JobDeta i l job = new JobDeta i l ( ) ;
7 job . setName ( "messageHandler " ) ;
8 job . s e tJobClas s (MessageHandler . c l a s s ) ;
9
10 // con f i gu r e the s chedu l e r time
11 SimpleTr igger t r i g g e r = new SimpleTr igger ( ) ;
12 t r i g g e r . setStartTime (new Date ( System . cur r entT imeMi l l i s ( ) +
INITIAL_DELAY) ) ;
13 t r i g g e r . setRepeatCount ( S impleTr igger .REPEAT_INDEFINITELY) ;
14 t r i g g e r . s e tRepea t In t e rva l (PERIOD) ;
15
16 // schedu le i t
17 Scheduler s chedu l e r = new StdSchedulerFactory ( ) . ge tSchedu le r ( ) ;
18 s chedu l e r . s t a r t ( ) ;
19 s chedu l e r . scheduleJob ( job , t r i g g e r ) ;
20 }
21 }
22
23
9.4 Interval partitioning bij Oracle 11g 65
24 pub l i c c l a s s MessageHandler implements Job{
25 @Override
26 pub l i c void execute ( JobExecutionContext context )
27 throws JobExecutionException {
28 // f e t ch XX reco rd s
29 // parse r e co rd s
30 // i n s e r t r e co rd s
31 }
32 }
Codefragment 9.5: Voorbeelduitwerking van Quartz scheduler
9.4 Interval partitioning bij Oracle 11g
Interval partitioning is een uitbreiding van Range partitioning en werd pas in Oracle 11g ge-
ïntroduceerd. Dit concept heeft als voordeel dat partities automatisch aangemaakt worden
bij het toevoegen van een record buiten de bestaande grenzen. Er moet op z'n minst één
range partitie aanwezig zijn. Om een bestaande range gepartitioneerde tabel te upgraden
naar een interval partitionering zullen er twee problemen opgelost moeten worden.[7]
� ORA-14759: SET INTERVAL is not legal on this table.
� ORA-14758: Last partition in the range section cannot be dropped.
Om de eerste error te vermijden moet de MAXVALUE-partitie eerst verwijderd worden.
Dit gebeurt als volgt:
1 ALTER TABLE vem_log_message DROP PARTITION other
Vervolgens kan de bestaande tabel omgezet worden naar een interval partitionering:
1 ALTER TABLE vem_log_message s e t INTERVAL (NUMTODSINTERVAL(8 , 'HOUR' ) )
Door de partities automatisch te laten genereren, krijgen ze een gegenereerde partitienaam.
De grens is gebaseerd op het vaste interval waardoor het niet mogelijk is om hier gebruik te
maken van WhatShift grenzen. De partitienaam is geen probleem omdat de partitie een-
voudig hernoemt kan worden, maar zelfs dit is niet nodig. De partities zijn aanspreekbaar
met de 'PARTITION for'-statement, zoals aangegeven in codefragment 9.6.
9.4 Interval partitioning bij Oracle 11g 66
1 ALTER TABLE vem_log_message DROP PARTITION f o r
2 (TO_DATE ( ' 2014−07−01 00 : 00 : 00 ' , 'YYYY−MM−DD HH24 :MI : SS ' ) )
Codefragment 9.6: 'PARTITION for'-statement
Met behulp van codefragment 9.6 kan ook de oudste partitie gedropt worden. Dit zal echter
falen als het de laatste zelfgemaakte partitie is. Zelfgemaakte partities worden gekenmerkt
door de kolom 'INT'. Deze kolom staat voor INTERVAL en bevat 'NO' indien de partitie
zelfgemaakt is. Dit is het tweede probleem dat hierboven vermeld werd. Er zijn drie
manieren om dit probleem te omzeilen.
Als eerste kan het probleem opgelost worden door twee partities te mergen. Dit heeft tot
gevolg dat alle records van beide partities gekopieerd moeten worden in een nieuwe partitie.
Omdat het de bedoeling is de oudste partitie te droppen, kan deze partitie eenvoudigweg
eerst gewist worden. De andere manier houdt in dat er tijdelijk naar range partitioning
overgeschakeld wordt zodat de automatisch aangemaakte partities nu ook een 'NO' krijgen
in de INT-kolom. Deze methoden genieten echter niet de voorkeur omdat ze ofwel data
verplaatsen ofwel een tijdsspanne van onbeschikbaarheid veroorzaken.[15] De laatste mo-
gelijkheid bestaat erin om de laatste partitie gewoon te behouden zonder data. We kunnen
deze partitie dan ook hernoemen naar MINVALUE. Vervolgens kunnen de gegenereerde
partities met DROP PARTITION verwijderd worden.
ALGEMENE CONCLUSIE 67
Hoofdstuk 10
Algemene conclusie
Uit de eerste vergelijking werd al snel duidelijk dat partitioning een verantwoorde keuze
was. Deze keuze opende ook het pad naar een uitgebreid onderzoek. Dit had natuurlijk
gevolgen voor het kiezen van de indexen. Daarom werd elke index uitvoerig getest en
werd er vastgesteld dat lokale indexen de beste keuze waren. Niet alleen op vlak van
opzoekperformantie voldoen lokale indexen, maar ook op vlak van beheer zijn ze eenvoudig
en snel in gebruik. Er werd ook vastgesteld dat het statement 'UPDATE INDEXES' veel
betere resultaten oplevert dan de andere besproken methodes voor het verwijderen van
partities.
Verder kan er besloten worden dat de loadbalancer voldoet aan de eis om twintig berichten
per seconde te verwerken. Met een snelheid van ongeveer 90 berichten per seconde op
de testserver en ongeveer 300 berichten per seconde lokaal, is er meer dan genoeg ruimte
voorzien. Hierbij moesten de resultaten van de testserver beschouwd worden als een situatie
met hoge belasting van andere processen en de lokale resultaten als een bovengrens voor
de performantie. Bovendien werd er ook geconcludeerd dat een stabiele oplossing een
opstelling is met drie webservices en elk vijftien threads. Om de performantie van de
loadbalancer mogelijk nog te verhogen kan er in de toekomst zelf een verdeelstrategie
geschreven worden.
Het performant opvragen van de logberichten was een belangrijk punt van deze masterproef.
Er werd dan ook vastgesteld dat Hibernate als eerste versie voldeed aan de eisen. Toch werd
er opgemerkt dat de uitvoertijden met Hibernate lineair oplopen per opgevraagde partitie
met ongeveer twee seconden, maar dat - doordat er vooral in één, maximaal twee, partities
tegelijk gezocht wordt - de wachttijden relatief meevallen. Met dit in het achterhoofd werd
er toch al wat onderzoek gedaan om het opvragen te optimaliseren. Hierbij werden er twee
ALGEMENE CONCLUSIE 68
paden onderzocht, zijnde optimaliseren van Hibernate via een scrollable resultset en het
overschakelen naar JDBC met behoud van paginering.
Over het algemeen kan besloten worden dat het ontworpen systeem voldoet aan de eisen.
Toch zijn er hier en daar nog wat verbeteringen en uitbreidingen mogelijk. Het belangrijkste
is dat er veel bijgeleerd is en dat het een verrijkende ervaring was om te proeven van het
bedrijfsleven.
DATABANKTABELLEN 69
B¼lage A
Databanktabellen
Tabel A.1: Databankkolommen van VEM_LOG_MESSAGE
Logical name Physical name Type Length
id VEM_MESSAGE_ID NUMBER 22
station DATAPOINTID VARCHAR2 50
bodyNr IBODY VARCHAR2 7
timestamp CHANGETS TIMESTAMP 6
message MESSAGECONTENTDESC VARCHAR2 4000
messageType MESSAGETYPEID VARCHAR2 20
Tabel A.2: Databankkolommen van VEM_PARSERCONFIG
Logical name Physical name Type Length
id VEM_PARSERCONFIG_ID NUMBER 22
messageType MESSAGETYPEID VARCHAR 20
description PARSERDESC VARCHAR2 50
format FORMATCODE VARCHAR2 32
updatedTimestamp CHANGETS TIMESTAMP 6
updatedBy ICDSID CHAR 15
nlock NLOCK NUMBER 22
DATABANKTABELLEN 70
Tabel A.3: Databankkolommen van VEM_PARSERITEMCONFIG
Logical name Physical name Type Length
id VEM_PARSERITEMCONFIG_ID NUMBER 22
vemParserCon�gId VEM_PARSERCONFIG_ID NUMBER 22
key PROPERTYKEYCODE VARCHAR2 32
value PROPERTYVALUEDESC VARCHAR2 1024
updatedTimestamp CHANGETS TIMESTAMP 6
updatedBy ICDSID VARCHAR2 15
nlock NLOCK NUMBER 22
KLASSENDIAGRAM VAN WEBSERVICE EN LOADBALANCER 71
B¼lage B
Klassendiagram van webservice en
loadbalancer
Figuur B.1: Klassendiagram van eNextGenId-module-sCpr981
PL/SQL INSERT TESTOMGEVING 72
B¼lage C
PL/SQL insert testomgeving
1 de c l a r e
2 idNumber i n t ;
3 dag i n t ;
4 s t a r t Index i n t ;
5 s h i f t s t a r t i n t ;
6 sh i f tReco rd s i n t ;
7 strName varchar (32) ;
8 strName2 varchar (32) ;
9 msgcontent varchar (4000) ;
10 t s r e g timestamp ;
11 begin
12 msgcontent := ' . . . ' ;
13 f o r dag in 6 . . 10
14 loop
15 sh i f tReco rd s := 500000;
16 s t a r t Index := (dag−1)*3* sh i f tReco rd s ;
17 f o r j in 1 . . 3
18 loop
19 s h i f t s t a r t :=( j−1)* sh i f tReco rd s ;
20 f o r i in 1 . . s h i f tReco rd s
21 loop
22 idNumber := s ta r t Index + s h i f t s t a r t + i ;
23 strName := subs t r (DBMS_RANDOM.RANDOM,0 , 7 ) ;
24 strName2 := dbms_random . random ;
25 i f j = 1 then
26 t s r e g := TO_DATE ( '2014−01− ' | | lpad ( dag , 2 , ' 0 ' ) | | '
0 0 : 00 : 00 ' , 'YYYY−MM−DD HH24 :MI : SS ' ) + DBMS_RANDOM.VALUE
(0 ,8/24−1/(24*60*60) ) ;
PL/SQL INSERT TESTOMGEVING 73
27 e l s i f j= 2 then
28 t s r e g := TO_DATE ( '2014−01− ' | | lpad ( dag , 2 , ' 0 ' ) | | '
0 8 : 00 : 00 ' , 'YYYY−MM−DD HH24 :MI : SS ' ) + DBMS_RANDOM.VALUE
(0 ,8/24−1/(24*60*60) ) ;
29 e l s i f j =3 then
30 t s r e g := TO_DATE ( '2014−01− ' | | lpad ( dag , 2 , ' 0 ' ) | | '
1 6 : 00 : 00 ' , 'YYYY−MM−DD HH24 :MI : SS ' ) + DBMS_RANDOM.VALUE
(0 ,8/24−1/(24*60*60) ) ;
31 end i f ;
32 i n s e r t i n to vem_log_message va lue s ( idNumber , strName2 ,
strName , t s r eg , msgcontent ,NULL) ;
33 i f mod( i , 1 00 ) = 0 then
34 commit ;
35 end i f ;
36 end loop ;
37 end loop ;
38 end loop ;
39 commit ;
40 end ;
Codefragment C.1: PL/SQL insert testomgeving
RESULTATEN TESTOMGEVING 74
B¼lage D
Resultaten testomgeving
De indexen werden geplaatst op station en bodynummer die bij de testomgevingtabel
elk een VARCHAR2 van 32 byte waren. De uiteindelijk tabel heeft voor station een
VARCHAR2 van 50 byte en bodynummer heeft een VARCHAR2 van 7 byte. Dit kan
mogelijk voor wat verschillen zorgen tussen beide opstellingen.
Tabel D.1: Native SQL: zoeken op station in één partitie (eq)
#1 #2 #3 Gem.
TESTCASE 1 0,717 0,537 0,555 0,603
TESTCASE 2 0,019 0,006 0,006 0,010
TESTCASE 3 0,017 0,004 0,004 0,008
TESTCASE 4 0,182 0,637 0,519 0,446
TESTCASE 5 0,014 0,004 0,007 0,008
TESTCASE 6 0,020 0,023 0,013 0,019
TESTCASE 7 0,026 0,011 0,020 0,019
TESTCASE 8 0,170 0,668 0,505 0,448
RESULTATEN TESTOMGEVING 75
Tabel D.2: Native SQL: zoeken op bodynummer in één partitie (eq)
#1 #2 #3 Gem.
TESTCASE 1 0,659 0,495 0,607 0,587
TESTCASE 2 0,330 0,581 0,503 0,471
TESTCASE 3 0,029 0,013 0,004 0,015
TESTCASE 4 0,699 0,349 0,344 0,464
TESTCASE 5 0,193 0,767 0,502 0,487
TESTCASE 6 0,151 0,656 0,508 0,438
TESTCASE 7 0,187 0,701 0,552 0,480
TESTCASE 8 0,618 0,388 0,348 0,451
Tabel D.3: Native SQL: zoeken op station over ganse tabel (eq)
#1 #2 #3 Gem.
TESTCASE 1 9,049 8,901 8,615 8,855
TESTCASE 2 0,009 0,004 0,003 0,005
TESTCASE 3 0,004 0,002 0,003 0,003
TESTCASE 4 8,871 8,937 8,859 8,889
TESTCASE 5 0,006 0,002 0,002 0,003
TESTCASE 6 0,004 0,006 0,003 0,004
TESTCASE 7 0,003 0,006 0,002 0,004
TESTCASE 8 9,645 9,940 9,957 9,847
Tabel D.4: Native SQL: zoeken op bodynummer over ganse tabel (eq)
#1 #2 #3 Gem.
TESTCASE 1 9,138 8,980 8,477 8,865
TESTCASE 2 8,860 9,007 8,469 8,779
TESTCASE 3 0,005 0,003 0,001 0,003
TESTCASE 4 8,937 8,859 8,436 8,744
TESTCASE 5 8,751 8,676 8,868 8,765
TESTCASE 6 8,881 8,839 9,123 8,948
TESTCASE 7 10,501 10,879 10,689 10,690
TESTCASE 8 9,572 9,309 8,756 9,212
RESULTATEN TESTOMGEVING 76
Tabel D.5: Uitvoertijden van INSERT bij de verschillende testcases
#1 #2 #3 #4 #5 Gem.
TESTCASE 1 0,017 0,020 0,024 0,015 0,012 0,018
TESTCASE 2 0,021 0,026 0,022 0,040 0,021 0,026
TESTCASE 3 0,036 0,039 0,040 0,059 0,030 0,041
TESTCASE 4 0,080 0,034 0,028 0,039 0,028 0,042
TESTCASE 5 0,073 0,031 0,021 0,029 0,033 0,037
TESTCASE 6 0,059 0,018 0,028 0,018 0,015 0,028
TESTCASE 7 0,038 0,038 0,018 0,025 0,021 0,028
TESTCASE 8 0,045 0,031 0,026 0,027 0,020 0,030
Tabel D.6: Uitvoertijden van DELETE bij de verschillende testcases
#1 #2 #3 #4 #5 Gem.
TESTCASE 1 0,007 0,005 0,005 0,005 0,005 0,005
TESTCASE 2 0,006 0,005 0,005 0,005 0,005 0,005
TESTCASE 3 0,005 0,005 0,005 0,005 0,006 0,005
TESTCASE 4 0,007 0,006 0,006 0,019 0,005 0,009
TESTCASE 5 0,009 0,006 0,005 0,005 0,005 0,006
TESTCASE 6 0,007 0,006 0,005 0,005 0,004 0,005
TESTCASE 7 0,008 0,005 0,005 0,005 0,006 0,006
TESTCASE 8 0,006 0,005 0,005 0,005 0,007 0,006
RESULTATEN TESTOMGEVING 77
Tabel D.7: Uitvoertijden DROP PARTITION met update indexes en alter index (case 1-4)
#1 #2 #3 Gem.
TESTCASE 1 update indexes 22,160 21,658 21,226 21,681
drop 0,170 0,138 0,156 0,155
alter index 61,142 64,330 60,893 62,122
totaal 61,312 64,468 61,049 62,276
TESTCASE 2 update indexes 37,455 49,723 57,724 50,255
drop 0,290 0,294 0,335 0,306
alter index pk 60,629 59,568 62,579 60,925
alter index dp 75,742 78,314 86,514 80,190
totaal 136,661 138,176 149,428 141,422
TESTCASE 3 update indexes 55,000 74,000 56,795 61,932
drop 0,159 0,363 0,261
alter index pk 84,000 89,498 86,749
alter index bd 86,000 87,880 86,940
alter index dp 92,000 74,607 83,304
totaal 262,159 252,348 257,254
TESTCASE 4 updates indexes 49,108 64,623 65,463 59,731
drop 0,624 0,180 0,362 0,389
alter index pk 62,826 58,918 60,402 60,715
alter index dp 78,676 76,736 82,234 79,215
alter index ts 76,451 65,431 66,460 69,447
totaal 218,577 201,265 209,458 209,767
RESULTATEN TESTOMGEVING 78
Tabel D.8: Uitvoertijden DROP PARTITION met update indexes en alter index (case 5-8)
#1 #2 #3 Gem.
TESTCASE 5 update indexes 38,182 39,281 40,505 39,323
drop 0,147 0,294 0,286 0,242
alter index pk 60,692 63,614 58,878 61,061
alter index dp_ts 101,497 90,081 86,956 92,845
totaal 162,336 153,989 146,120 154,148
TESTCASE 6 update indexes 23,417 21,564 19,657 21,546
drop 0,141 0,060 0,371 0,191
alter index pk 62,351 66,434 65,522 64,769
totaal 62,492 66,494 65,893 64,95967
TESTCASE 7 update indexes 22,291 22,291
drop 0,133 0,133
alter index pk 64,617 64,617
totaal 64,750 64,750
TESTCASE 8 update indexes 24,026 24,026
drop 0,114 0,114
alter index pk 65,195 65,195
totaal 65,309 65,309
RESULTATEN HIBERNATE 79
B¼lage E
Resultaten Hibernate
Dit zijn oudere testresultaten dan die uit hoofdstuk 8, maar zijn zeker de moeite waard om
te bekijken. De opzoektijden zijn lager omdat er in deze resultaten geen rekening gehouden
werd met uitvoertijd om het aantal records op te vragen.
Tabel E.1: Hibernate: zoeken zonder �lters in volledige tabel
Resultset #1 #2 #3 Gem.
50 22,031 22,144 22,275 22,150
200 21,928 22,538 22,611 22,359
1000 22,440 22,404 22,044 22,296
Tabel E.2: Hibernate: zoeken met �lters in volledige tabel (resultset 50)
Filter #1 #2 #3 Gem.
station (like) 10,696 10,650 10,652 10,666
bodyNr (eq) 0,014 0,012 0,013 0,013
station+bodyNr 0,009 0,008 0,008 0,008
RESULTATEN HIBERNATE 80
Tabel E.3: Hibernate: zoeken zonder �lters in één partitie
Resultset #1 #2 #3 Gem.
50 1,535 1,663 1,683 1,627
200 1,665 1,645 1,734 1,681
1000 1,928 1,829 1,938 1,898
Tabel E.4: Hibernate: zoeken met �lters in één partitie (resultset 1000)
Filter #1 #2 #3 Gem.
station (like) 1,254 1,096 1,213 1,188
bodyNr (eq) 0,006 0,006 0,006 0,006
station+bodyNr 0,007 0,035 0,011 0,018
Tabel E.5: Hibernate: zoeken zonder �lters in twee partities
Resultset #1 #2 #3 Gem.
50 3,297 3,197 3,077 3,190
200 3,277 3,375 3,089 3,247
1000 3,175 3,122 3,226 3,174
Tabel E.6: Hibernate: zoeken met �lters in twee partities (resultset 1000)
Filter #1 #2 #3 Gem.
station (like) 1,909 2,067 1,968 1,981
bodyNr (eq) 0,006 0,006 0,007 0,006
station+bodyNr 0,006 0,006 0,007 0,006
RESULTATEN LOKALE STRESSTEST 81
B¼lage F
Resultaten lokale stresstest
Tabel F.1: Lokale opstelling met 15 webservice threads en 2 webservices
Webservice #1 #2 #3 #4
8351 11617 39065 57376 23838
8352 8383 20935 42624 26036
total 20000 60000 100000 498741
tijd 01:30 03:14 04:40 02:39
berichten/s 222 309 357 313
1120.000 berichten verstuurd
Tabel F.2: Lokale opstelling met 30 webservice threads en 2 webservices
Webservice #1 #2 #3 #4
8351 11617 38903 58306 23838
8352 8383 21097 41694 26036
totaal 20000 60000 100000 498741
tijd 01:30 03:12 05:07 02:39
berichten/s 222 312 326 313
1120.000 berichten verstuurd
RESULTATEN LOKALE STRESSTEST 82
Tabel F.3: Lokale opstelling met 50 webservice threads en 2 webservices
Webservice #1
8351 55649
8352 44351
totaal 100000
tijd 04:58
berichten/s 335
Tabel F.4: Lokale opstelling met 15 webservice threads en 3 webservices
Webservice #1 #2 #3 #4
8351 14128 30131 50054 30501
8352 3364 12557 26989 12639
8353 2508 17312 22957 14625
total 20000 60000 100000 577651
tijd 01:40 03:04 04:52 02:37
berichten/s 200 326 342 367
1120.000 berichten verstuurd
Tabel F.5: Lokale opstelling met 30 webservice threads en 3 webservices
Webservice #1 #2 #3 #4
8351 10385 30274 26410 15247
8352 4315 14818 10576 16758
8353 5300 14908 13460 9537
totaal 20000 60000 504461 415422
tijd 01:36 03:13 02:34 02:39
berichten/s 208 310 327 261
1100.000 berichten verstuurd2120.000 berichten verstuurd
RESULTATEN LOKALE STRESSTEST 83
Tabel F.6: Lokale opstelling met 15 webservice threads en 4 webservices
Webservice #1 #2 #3
8351 11873 27162 11093
8352 2201 10455 6235
8353 2508 10454 12629
8354 3418 11897 5579
totaal 20000 599681 355362
tijd 01:46 03:18 01:54
berichten/s 188 302 311
160.000 berichten verstuurd2120.000 berichten verstuurd
RESULTATEN OPENVMS STRESSTEST 84
B¼lage G
Resultaten OpenVMS stresstest
Bij het gebruik van 50 threads crasht het programma.
Tabel G.1: VMS-opstelling met 15 webservice threads en 3 webservices
Webservice #1 #2 #3
8351 18086 54051 41648
8352 1106 32550 28903
8353 808 33399 29449
totaal 20000 120000 100000
tijd 10:05 27:56 19:44
berichten/s 33 71 84
Tabel G.2: VMS-opstelling met 15 webservice threads en 4 webservices
Webservice #1 #2 #3
8351 3398 7843 27976
8352 1897 3581 22803
8353 2755 3684 25100
8354 1950 4892 24121
totaal 10000 20000 100000
tijd 01:53 04:35 19:11
berichten/s 88 72 86
BIBLIOGRAFIE 85
Bibliogra�e
[1] codefutures. Spring JDBC Pagination Tutorial. http://www.codefutures.com/
spring-pagination/. Meerdere keren geraadpleegd in mei 2014.
[2] craig. Why you shouldn't use Quartz Scheduler. http://www.carfey.com/blog/
why-you-shouldnt-use-quartz-scheduler/, 2012. Geraadpleegd op: 19/02/2014.
[3] Java Concurrency in Practice. Java timer vs executorservice? http://
stackoverflow.com/questions/409932/java-timer-vs-executorservice. Ge-
raadpleegd op: 19/02/2014.
[4] Koen Serneels. Bulk fetching with Hibernate. http://koenserneels.blogspot.
be/2013/03/bulk-fetching-with-hibernate.html, 2013. Geraadpleegd op:
19/02/2014.
[5] Simon Maple. The Great Java Application Server Debate with Tomcat, JBoss,
GlassFish, Jetty and Liberty Pro�le. http://zeroturnaround.com/rebellabs/
the-great-java-application-server-debate-with-tomcat-jboss-glassfish-jetty-and-liberty-profile/,
2013. Meerdere keren geraadpleegd in maart,april en mei 2014.
[6] mkyong. Quartz 1.6 Scheduler Tutorial. http://www.mkyong.com/java/
quartz-scheduler-example/, 2012. Geraadpleegd op: 19/02/2014.
[7] Jaromir D.B. Nemec. Rolling Window Using Interval Partitioning. http://
www.db-nemec.com/rolling_window_interval_part.html, 2011. Geraadpleegd op:
27/04/2014.
[8] Oracle. Java management extensions (jmx). http://docs.oracle.com/javase/
tutorial/jmx/index.html. Meerdere keren geraadpleegd in mei 2014.
[9] Oracle. Scheduled Executor. http://docs.oracle.com/javase/6/docs/api/
java/util/concurrent/ScheduledExecutorService.html. Geraadpleegd op:
19/02/2014.
BIBLIOGRAFIE 86
[10] Oracle. Partition Pruning. http://docs.oracle.com/cd/B19306_01/server.102/
b14220/partconc.htm#i462783, 2005. Meerdere keren geraadpleegd in april en mei
2014.
[11] Oracle. Dropping Partitions. http://docs.oracle.com/cd/B19306_01/server.
102/b14231/partiti.htm#i1007479, 2006. Meerdere keren geraadpleegd in april en
mei 2014.
[12] Oracle. Optimizing SPLIT PARTITION and SPLIT SUBPARTITION Operati-
ons. http://docs.oracle.com/cd/B19306_01/server.102/b14231/partiti.htm#
i1008028, 2006. Meerdere keren geraadpleegd in april en mei 2014.
[13] Oracle. Blockingqueue. http://docs.oracle.com/javase/6/docs/api/java/util/
concurrent/BlockingQueue.html, 2011. Meerdere keren geraadpleegd in maart,april
en mei 2014.
[14] Oracle. Oracle Data Types. http://docs.oracle.com/cd/B28359_01/server.111/
b28318/datatype.htm, 2011. Geraadpleegd op: 24/05/2014.
[15] Harald van Breederode. Dropping interval partitions. http://prutser.
wordpress.com/2010/01/11/dropping-interval-partitions/, 2010. Geraad-
pleegd op: 27/04/2014.
[16] Wikipedia. Round-robin dns. http://en.wikipedia.org/wiki/Round-robin_DNS,
2014. Meerdere keren geraadpleegd in maart 2014.
[17] Markus Winand. Row Migration and Row Movement. http://blog.fatalmind.
com/2010/02/23/row-migration-and-row-movement/, 2010. Meerdere keren ge-
raadpleegd in april 2014.
L�ST VAN FIGUREN 87
L¼st van �guren
2.1 Projectstructuur in Volvo-omgeving . . . . . . . . . . . . . . . . . . . . . . 4
3.1 Tabeloverzicht voor het actief-historiek ontwerp . . . . . . . . . . . . . . . 8
3.2 Eerste ontwerp via twee tabellen . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Tabeloverzicht voor het partitioning ontwerp . . . . . . . . . . . . . . . . . 9
3.4 Tweede ontwerp via partitioning . . . . . . . . . . . . . . . . . . . . . . . . 10
4.1 Schematisch overzicht van het principe van loadbalancing . . . . . . . . . . 13
4.2 Schematisch overzicht van de verwerking van een binnenkomend bericht . . 13
4.3 Hoofdscherm van het parserbeheer . . . . . . . . . . . . . . . . . . . . . . . 14
4.4 Validatescherm voor parsers . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.5 Detailscherm van het parserbeheer . . . . . . . . . . . . . . . . . . . . . . 16
4.6 Overzichtsscherm voor de logberichten . . . . . . . . . . . . . . . . . . . . 17
4.7 Kalenderscherm om een tijdsspanne in te stellen . . . . . . . . . . . . . . . 18
4.8 Detailscherm voor het tonen van logberichten . . . . . . . . . . . . . . . . 19
4.9 Hoofdscherm om partitiecon�guraties te beheren . . . . . . . . . . . . . . . 22
4.10 Scherm om systeemmapping te beheren voor partitiecon�guraties . . . . . 23
4.11 Detailscherm om partitiecon�guraties te beheren . . . . . . . . . . . . . . . 24
5.1 Voorbeelden van de uitvoer van 'EXPLAIN PLAN FOR' . . . . . . . . . . 32
6.1 Visualisatie van RemoteLoggingServer . . . . . . . . . . . . . . . . . . . . 37
6.2 Technisch overzicht van loadbalancer en webservices . . . . . . . . . . . . . 37
8.1 Native SQL vs Hibernate: staafdiagram die uitvoertijd toont bij het aantal
partities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
B.1 Klassendiagram van eNextGenId-module-sCpr981 . . . . . . . . . . . . . . 71
L�ST VAN TABELLEN 88
L¼st van tabellen
3.1 Vergelijking Actief-historiek ontwerp met partitioning ontwerp . . . . . . . 11
4.1 Omschrijving van spreadsheetvelden in beheerscherm voor parsers . . . . . 15
4.2 Omschrijving van de spreadsheetkolommen in het logberichtenscherm . . . 18
7.1 Omschrijving verschillende testcases voor testomgeving . . . . . . . . . . . 51
7.2 Verwijderen van een partitie: gemiddelden naast elkaar . . . . . . . . . . . 52
8.1 Vergelijking systeemspeci�caties voor de stresstest . . . . . . . . . . . . . . 54
8.2 Vergelijking opzoektijden van Native SQL met Hibernate in eindopstelling
met resultaten gelimiteerd tot 500 records . . . . . . . . . . . . . . . . . . 57
9.1 Java.util.Timer versus ScheduledExecutor . . . . . . . . . . . . . . . . . . 64
A.1 Databankkolommen van VEM_LOG_MESSAGE . . . . . . . . . . . . . . 69
A.2 Databankkolommen van VEM_PARSERCONFIG . . . . . . . . . . . . . . 69
A.3 Databankkolommen van VEM_PARSERITEMCONFIG . . . . . . . . . . 70
D.1 Native SQL: zoeken op station in één partitie (eq) . . . . . . . . . . . . . . 74
D.2 Native SQL: zoeken op bodynummer in één partitie (eq) . . . . . . . . . . 75
D.3 Native SQL: zoeken op station over ganse tabel (eq) . . . . . . . . . . . . . 75
D.4 Native SQL: zoeken op bodynummer over ganse tabel (eq) . . . . . . . . . 75
D.5 Uitvoertijden van INSERT bij de verschillende testcases . . . . . . . . . . . 76
D.6 Uitvoertijden van DELETE bij de verschillende testcases . . . . . . . . . . 76
D.7 Uitvoertijden DROP PARTITION met update indexes en alter index (case
1-4) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
D.8 Uitvoertijden DROP PARTITION met update indexes en alter index (case
5-8) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
E.1 Hibernate: zoeken zonder �lters in volledige tabel . . . . . . . . . . . . . . 79
L�ST VAN TABELLEN 89
E.2 Hibernate: zoeken met �lters in volledige tabel (resultset 50) . . . . . . . . 79
E.3 Hibernate: zoeken zonder �lters in één partitie . . . . . . . . . . . . . . . . 80
E.4 Hibernate: zoeken met �lters in één partitie (resultset 1000) . . . . . . . . 80
E.5 Hibernate: zoeken zonder �lters in twee partities . . . . . . . . . . . . . . . 80
E.6 Hibernate: zoeken met �lters in twee partities (resultset 1000) . . . . . . . 80
F.1 Lokale opstelling met 15 webservice threads en 2 webservices . . . . . . . . 81
F.2 Lokale opstelling met 30 webservice threads en 2 webservices . . . . . . . . 81
F.3 Lokale opstelling met 50 webservice threads en 2 webservices . . . . . . . . 82
F.4 Lokale opstelling met 15 webservice threads en 3 webservices . . . . . . . . 82
F.5 Lokale opstelling met 30 webservice threads en 3 webservices . . . . . . . . 82
F.6 Lokale opstelling met 15 webservice threads en 4 webservices . . . . . . . . 83
G.1 VMS-opstelling met 15 webservice threads en 3 webservices . . . . . . . . . 84
G.2 VMS-opstelling met 15 webservice threads en 4 webservices . . . . . . . . . 84
LIJST VAN CODEFRAGMENTEN 90
Lijst van codefragmenten
5.1 Partitie toevoegen met SPLIT PARTITION en ALTER INDEX . . . . . . 28
5.2 Aanmaken van een tabel met partities . . . . . . . . . . . . . . . . . . . . 30
5.3 Fetchen van records uit partitioned table . . . . . . . . . . . . . . . . . . . 31
5.4 Explainplan opvragen van een query . . . . . . . . . . . . . . . . . . . . . 31
6.1 Aanmaken van de loadbalancer . . . . . . . . . . . . . . . . . . . . . . . . 34
6.2 Gebruik Jetty server vermelden . . . . . . . . . . . . . . . . . . . . . . . . 35
6.3 Opstarten van de webservice . . . . . . . . . . . . . . . . . . . . . . . . . . 35
6.4 RecordInserter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
6.5 Voorbeeld gebruik van XPath parsing . . . . . . . . . . . . . . . . . . . . . 38
6.6 JMX: bean interface voor het herladen van parsers . . . . . . . . . . . . . . 39
6.7 Implementeren en registreren van bean voor JMX . . . . . . . . . . . . . . 40
6.8 Classpath variabelen voor het draaien van de MBean server . . . . . . . . . 40
6.9 Herladen van parsers met behulp van een JMX bean . . . . . . . . . . . . 41
6.10 Shiftinformatie opvragen van X aantal shifts terug . . . . . . . . . . . . . . 43
6.11 Opvragen oudste shifts via shiftinformatie van WhatShift . . . . . . . . . . 43
6.12 Opvragen oudste shifts via SQL . . . . . . . . . . . . . . . . . . . . . . . . 44
6.13 Opvragen van de juiste dataSource . . . . . . . . . . . . . . . . . . . . . . 45
6.14 Opvragen recentste werkschema . . . . . . . . . . . . . . . . . . . . . . . . 45
6.15 Voorbeeld ini-bestand voor partitiebeheer . . . . . . . . . . . . . . . . . . . 46
6.16 Zoeken naar de juiste partitiecon�guratie . . . . . . . . . . . . . . . . . . . 47
7.1 Opzetten van Liquibase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
7.2 XML met changeSet gebruikt door liquibase . . . . . . . . . . . . . . . . . 49
7.3 Voorbeeld gebruik van EasyMock . . . . . . . . . . . . . . . . . . . . . . . 50
9.1 Gebruik van een Scrollable resultset met Hibernate . . . . . . . . . . . . . 59
9.2 Gebruik van een Stateless session met Hibernate . . . . . . . . . . . . . . . 60
9.3 Paginering met behulp van JDBC . . . . . . . . . . . . . . . . . . . . . . . 61
9.4 Uitwerking met ScheduledExecutorService . . . . . . . . . . . . . . . . . . 63
LIJST VAN CODEFRAGMENTEN 91
9.5 Voorbeelduitwerking van Quartz scheduler . . . . . . . . . . . . . . . . . . 64
9.6 'PARTITION for'-statement . . . . . . . . . . . . . . . . . . . . . . . . . . 66
C.1 PL/SQL insert testomgeving . . . . . . . . . . . . . . . . . . . . . . . . . . 72