In this example study, we will tune the initialization parameters of an Oracle Database server instance to minimize the memory required for running KonaKart, a popular Java e-commerce service, without significantly impacting the responsiveness of the whole system.
We’ll use Apache JMeter to stress the system for the test, while we will leverage the Oracle Prometheus exporter to extract the metrics.
For this study, we will use three dedicated machines:
oradb.mycompany.com, hosting an Oracle Database 19c instance
konakart.mycompany.com, running the KonaKart Community Edition service
akamas.mycompany.com, which generates the workload using JMeter and will host the OracleDB Prometheus exporter instance
Refer to the following links to install and configure KonaKart Community Edition:
Install KonaKart: install and configure the service
Manual Installation: install the demo dataset
For this use case, we provisioned the database on a VM on Oracle Cloud, which allows us to easily provision licensed instances on demand.
Through the OracleDB Prometheus exporter, we can publish as metrics the results of the arbitrary queries defined in the configuration file. In our case, we’ll use it to extract valuable performance metrics from Oracle’s Dynamic Performance (V$) Views.
We can spin up the exporter using the official Docker image using the following command, where cust-metrics.toml
is our custom metrics file:
docker run -d --name orabench_exporter --restart always \
-p 9161:9161 \
-v ~/oracledb_exporter/cust-metrics.toml:/cust-metrics.toml \
-e CUSTOM_METRICS=/cust-metrics.toml \
-e DATA_SOURCE_NAME='user/passwd@//oradb.mycompany.com:1521/konakart' \
iamseth/oracledb_exporter
The exporter will publish the metrics on the specified port 9161
.
Here’s the metrics file used to run the exporter:
[[metric]]
context= "memory"
labels= [ "component" ]
metricsdesc= { size="Component memory extracted from v$memory_dynamic_components in Oracle." }
request = '''
SELECT component, current_size as "size"
FROM V$MEMORY_DYNAMIC_COMPONENTS
UNION
SELECT name, bytes as "size"
FROM V$SGAINFO
WHERE name in ('Free SGA Memory Available', 'Redo Buffers', 'Maximum SGA Size')
'''
[[metric]]
context = "activity"
metricsdesc = { value="Generic counter metric from v$sysstat view in Oracle." }
fieldtoappend = "name"
request = '''
SELECT name, value
FROM V$SYSSTAT WHERE name IN (
'execute count',
'user commits', 'user rollbacks',
'db block gets from cache', 'consistent gets from cache', 'physical reads cache', /* CACHE */
'redo log space requests'
)
'''
[[metric]]
context = "system_event"
labels = [ "event", "wait_class" ]
request = '''
SELECT
event, wait_class,
total_waits, time_waited
FROM V$SYSTEM_EVENT
'''
[metric.metricsdesc]
total_waits= "Total number of waits for the event as per V$SYSTEM_EVENT in Oracle."
time_waited= "Total time waited for the event (in hundredths of seconds) as per V$SYSTEM_EVENT in Oracle."
Using the following snippet we configure Prometheus to fetch metrics from:
the JMeter exporter exposing the load-generator stats
the OracleDB exporter monitoring the database
scrape_configs:
- job_name: jmeter
static_configs:
- targets: ['akamas.mycompany.com:9270']
relabel_configs:
- source_labels: [__address__]
action: replace
regex: "(.*):.*"
target_label: instance
- job_name: oracle-exp
static_configs:
- targets: ['akamas.mycompany.com:9161']
labels:
instance: oradb
For a complete guide on how to configure and manage Prometheus refer to the official documentation.
The load generator runs containerized on the akamas.mycomopany.com instance using the attached Konakart_optimizePerf.jmx
configuration file.
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.3">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="HOST" elementType="Argument">
<stringProp name="Argument.name">HOST</stringProp>
<stringProp name="Argument.value">${__P(HOST,konakartoraclecloud.lab.akamas.io)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="VUSER" elementType="Argument">
<stringProp name="Argument.name">VUSER</stringProp>
<stringProp name="Argument.value">${__P(VUSER,60)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<kg.apc.jmeter.threads.UltimateThreadGroup guiclass="kg.apc.jmeter.threads.UltimateThreadGroupGui" testclass="kg.apc.jmeter.threads.UltimateThreadGroup" testname=" Thread Group" enabled="true">
<collectionProp name="ultimatethreadgroupdata">
<collectionProp name="682260423">
<stringProp name="-1375672883">${__P(VUSER,10)}</stringProp>
<stringProp name="0">0</stringProp>
<stringProp name="49586">200</stringProp>
<stringProp name="53430">600</stringProp>
<stringProp name="48">0</stringProp>
</collectionProp>
</collectionProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.on_sample_error">startnextloop</stringProp>
</kg.apc.jmeter.threads.UltimateThreadGroup>
<hashTree>
<RandomVariableConfig guiclass="TestBeanGUI" testclass="RandomVariableConfig" testname="Random catId" enabled="true">
<stringProp name="maximumValue">30</stringProp>
<stringProp name="minimumValue">1</stringProp>
<stringProp name="outputFormat"></stringProp>
<boolProp name="perThread">true</boolProp>
<stringProp name="randomSeed">666</stringProp>
<stringProp name="variableName">catId</stringProp>
</RandomVariableConfig>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Welcome" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${HOST}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/Welcome.action</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SelectCat" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${HOST}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/SelectCat.action?catId=${catId}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<com.github.johrstrom.listener.PrometheusListener guiclass="com.github.johrstrom.listener.gui.PrometheusListenerGui" testclass="com.github.johrstrom.listener.PrometheusListener" testname="Prometheus Listener" enabled="true">
<collectionProp name="prometheus.collector_definitions">
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Sampler Response Time</stringProp>
<stringProp name="collector.metric_name">ResponseTime</stringProp>
<stringProp name="collector.type">SUMMARY</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets">0.5,0.01|0.85,0.01|0.9,0.01|0.99,0.01;60</stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">ResponseTime</stringProp>
</elementProp>
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Success and failure ratio</stringProp>
<stringProp name="collector.metric_name">Ratio</stringProp>
<stringProp name="collector.type">SUCCESS_RATIO</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets"></stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">SuccessRatio</stringProp>
</elementProp>
</collectionProp>
</com.github.johrstrom.listener.PrometheusListener>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<kg.apc.jmeter.threads.UltimateThreadGroup guiclass="kg.apc.jmeter.threads.UltimateThreadGroupGui" testclass="kg.apc.jmeter.threads.UltimateThreadGroup" testname="jp@gc - Ultimate Thread Group - slow slope" enabled="false">
<collectionProp name="ultimatethreadgroupdata">
<collectionProp name="-811668396">
<stringProp name="50">2</stringProp>
<stringProp name="48">0</stringProp>
<stringProp name="1722">60</stringProp>
<stringProp name="1508508">1140</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="1110318546">
<stringProp name="50">2</stringProp>
<stringProp name="50547">300</stringProp>
<stringProp name="1722">60</stringProp>
<stringProp name="55476">840</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="-277728215">
<stringProp name="50">2</stringProp>
<stringProp name="53430">600</stringProp>
<stringProp name="1722">60</stringProp>
<stringProp name="52593">540</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="1889215593">
<stringProp name="50">2</stringProp>
<stringProp name="56313">900</stringProp>
<stringProp name="1722">60</stringProp>
<stringProp name="49710">240</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
</collectionProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
</kg.apc.jmeter.threads.UltimateThreadGroup>
<hashTree>
<RandomVariableConfig guiclass="TestBeanGUI" testclass="RandomVariableConfig" testname="Random catId" enabled="true">
<stringProp name="maximumValue">30</stringProp>
<stringProp name="minimumValue">1</stringProp>
<stringProp name="outputFormat"></stringProp>
<boolProp name="perThread">true</boolProp>
<stringProp name="randomSeed">666</stringProp>
<stringProp name="variableName">catId</stringProp>
</RandomVariableConfig>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Welcome" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(HOST,konakartoraclecloud.lab.akamas.io)}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/Welcome.action</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<TestAction guiclass="TestActionGui" testclass="TestAction" testname="think time" enabled="true">
<intProp name="ActionProcessor.action">1</intProp>
<intProp name="ActionProcessor.target">0</intProp>
<stringProp name="ActionProcessor.duration">100</stringProp>
</TestAction>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SelectCat" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(HOST,konakartoraclecloud.lab.akamas.io)}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/SelectCat.action?catId=${catId}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<TestAction guiclass="TestActionGui" testclass="TestAction" testname="think time" enabled="true">
<intProp name="ActionProcessor.action">1</intProp>
<intProp name="ActionProcessor.target">0</intProp>
<stringProp name="ActionProcessor.duration">100</stringProp>
</TestAction>
<hashTree/>
<com.github.johrstrom.listener.PrometheusListener guiclass="com.github.johrstrom.listener.gui.PrometheusListenerGui" testclass="com.github.johrstrom.listener.PrometheusListener" testname="Prometheus Listener" enabled="true">
<collectionProp name="prometheus.collector_definitions">
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Sampler Response Time</stringProp>
<stringProp name="collector.metric_name">ResponseTime</stringProp>
<stringProp name="collector.type">SUMMARY</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets">0.5,0.01|0.85,0.01|0.9,0.01|0.99,0.01;60</stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">ResponseTime</stringProp>
</elementProp>
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Success and failure ratio</stringProp>
<stringProp name="collector.metric_name">Ratio</stringProp>
<stringProp name="collector.type">SUCCESS_RATIO</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets"></stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">SuccessRatio</stringProp>
</elementProp>
</collectionProp>
</com.github.johrstrom.listener.PrometheusListener>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<kg.apc.jmeter.threads.UltimateThreadGroup guiclass="kg.apc.jmeter.threads.UltimateThreadGroupGui" testclass="kg.apc.jmeter.threads.UltimateThreadGroup" testname="jp@gc - Ultimate Thread Group - steep slope" enabled="false">
<collectionProp name="ultimatethreadgroupdata">
<collectionProp name="-1293805452">
<stringProp name="1696">55</stringProp>
<stringProp name="48">0</stringProp>
<stringProp name="50547">300</stringProp>
<stringProp name="1507521">1035</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="-1729043283">
<stringProp name="52">4</stringProp>
<stringProp name="52593">540</stringProp>
<stringProp name="1598">20</stringProp>
<stringProp name="54613">775</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="110060986">
<stringProp name="53">5</stringProp>
<stringProp name="55352">800</stringProp>
<stringProp name="1603">25</stringProp>
<stringProp name="52500">510</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="-1520762789">
<stringProp name="54">6</stringProp>
<stringProp name="1507614">1065</stringProp>
<stringProp name="1629">30</stringProp>
<stringProp name="49710">240</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
</collectionProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
</kg.apc.jmeter.threads.UltimateThreadGroup>
<hashTree>
<RandomVariableConfig guiclass="TestBeanGUI" testclass="RandomVariableConfig" testname="Random catId" enabled="true">
<stringProp name="maximumValue">30</stringProp>
<stringProp name="minimumValue">1</stringProp>
<stringProp name="outputFormat"></stringProp>
<boolProp name="perThread">true</boolProp>
<stringProp name="randomSeed">666</stringProp>
<stringProp name="variableName">catId</stringProp>
</RandomVariableConfig>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Welcome" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(HOST,konakartoraclecloud.lab.akamas.io)}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/Welcome.action</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SelectCat" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(HOST,konakartoraclecloud.lab.akamas.io)}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/SelectCat.action?catId=${catId}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<com.github.johrstrom.listener.PrometheusListener guiclass="com.github.johrstrom.listener.gui.PrometheusListenerGui" testclass="com.github.johrstrom.listener.PrometheusListener" testname="Prometheus Listener" enabled="true">
<collectionProp name="prometheus.collector_definitions">
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Sampler Response Time</stringProp>
<stringProp name="collector.metric_name">ResponseTime</stringProp>
<stringProp name="collector.type">SUMMARY</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets">0.5,0.01|0.85,0.01|0.9,0.01|0.99,0.01;60</stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">ResponseTime</stringProp>
</elementProp>
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Success and failure ratio</stringProp>
<stringProp name="collector.metric_name">Ratio</stringProp>
<stringProp name="collector.type">SUCCESS_RATIO</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets"></stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">SuccessRatio</stringProp>
</elementProp>
</collectionProp>
</com.github.johrstrom.listener.PrometheusListener>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<kg.apc.jmeter.threads.UltimateThreadGroup guiclass="kg.apc.jmeter.threads.UltimateThreadGroupGui" testclass="kg.apc.jmeter.threads.UltimateThreadGroup" testname="jp@gc - Ultimate Thread Group - Infinite slope" enabled="false">
<collectionProp name="ultimatethreadgroupdata">
<collectionProp name="-477682307">
<stringProp name="1722">60</stringProp>
<stringProp name="0">0</stringProp>
<stringProp name="1572771">3600</stringProp>
<stringProp name="0"></stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
</collectionProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
</kg.apc.jmeter.threads.UltimateThreadGroup>
<hashTree>
<RandomVariableConfig guiclass="TestBeanGUI" testclass="RandomVariableConfig" testname="Random catId" enabled="true">
<stringProp name="maximumValue">30</stringProp>
<stringProp name="minimumValue">1</stringProp>
<stringProp name="outputFormat"></stringProp>
<boolProp name="perThread">true</boolProp>
<stringProp name="randomSeed">666</stringProp>
<stringProp name="variableName">catId</stringProp>
</RandomVariableConfig>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Welcome" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(HOST,konakartoraclecloud.lab.akamas.io)}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/Welcome.action</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SelectCat" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(HOST,konakartoraclecloud.lab.akamas.io)}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/SelectCat.action?catId=${catId}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<com.github.johrstrom.listener.PrometheusListener guiclass="com.github.johrstrom.listener.gui.PrometheusListenerGui" testclass="com.github.johrstrom.listener.PrometheusListener" testname="Prometheus Listener" enabled="true">
<collectionProp name="prometheus.collector_definitions">
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Sampler Response Time</stringProp>
<stringProp name="collector.metric_name">ResponseTime</stringProp>
<stringProp name="collector.type">SUMMARY</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets">0.5,0.01|0.85,0.01|0.9,0.01|0.99,0.01;60</stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">ResponseTime</stringProp>
</elementProp>
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Success and failure ratio</stringProp>
<stringProp name="collector.metric_name">Ratio</stringProp>
<stringProp name="collector.type">SUCCESS_RATIO</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets"></stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">SuccessRatio</stringProp>
</elementProp>
</collectionProp>
</com.github.johrstrom.listener.PrometheusListener>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<kg.apc.jmeter.threads.UltimateThreadGroup guiclass="kg.apc.jmeter.threads.UltimateThreadGroupGui" testclass="kg.apc.jmeter.threads.UltimateThreadGroup" testname="jp@gc - Ultimate Thread Group - gibbo slope" enabled="false">
<collectionProp name="ultimatethreadgroupdata">
<collectionProp name="-80734132">
<stringProp name="1572">15</stringProp>
<stringProp name="48">0</stringProp>
<stringProp name="56313">900</stringProp>
<stringProp name="1509345">1200</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="-1213623823">
<stringProp name="50">2</stringProp>
<stringProp name="1509345">1200</stringProp>
<stringProp name="48687">120</stringProp>
<stringProp name="54639">780</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="-1560134527">
<stringProp name="51">3</stringProp>
<stringProp name="1513251">1620</stringProp>
<stringProp name="48873">180</stringProp>
<stringProp name="50547">300</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
</collectionProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
</kg.apc.jmeter.threads.UltimateThreadGroup>
<hashTree>
<RandomVariableConfig guiclass="TestBeanGUI" testclass="RandomVariableConfig" testname="Random catId" enabled="true">
<stringProp name="maximumValue">30</stringProp>
<stringProp name="minimumValue">1</stringProp>
<stringProp name="outputFormat"></stringProp>
<boolProp name="perThread">true</boolProp>
<stringProp name="randomSeed">666</stringProp>
<stringProp name="variableName">catId</stringProp>
</RandomVariableConfig>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Welcome" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(HOST,konakartoraclecloud.lab.akamas.io)}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/Welcome.action</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<TestAction guiclass="TestActionGui" testclass="TestAction" testname="think time" enabled="true">
<intProp name="ActionProcessor.action">1</intProp>
<intProp name="ActionProcessor.target">0</intProp>
<stringProp name="ActionProcessor.duration">100</stringProp>
</TestAction>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SelectCat" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${__P(HOST,konakartoraclecloud.lab.akamas.io)}</stringProp>
<stringProp name="HTTPSampler.port">8780</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">konakart/SelectCat.action?catId=${catId}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">true</boolProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<TestAction guiclass="TestActionGui" testclass="TestAction" testname="think time" enabled="true">
<intProp name="ActionProcessor.action">1</intProp>
<intProp name="ActionProcessor.target">0</intProp>
<stringProp name="ActionProcessor.duration">100</stringProp>
</TestAction>
<hashTree/>
<com.github.johrstrom.listener.PrometheusListener guiclass="com.github.johrstrom.listener.gui.PrometheusListenerGui" testclass="com.github.johrstrom.listener.PrometheusListener" testname="Prometheus Listener" enabled="true">
<collectionProp name="prometheus.collector_definitions">
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Sampler Response Time</stringProp>
<stringProp name="collector.metric_name">ResponseTime</stringProp>
<stringProp name="collector.type">SUMMARY</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets">0.5,0.01|0.85,0.01|0.9,0.01|0.99,0.01;60</stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">ResponseTime</stringProp>
</elementProp>
<elementProp name="" elementType="com.github.johrstrom.listener.ListenerCollectorConfig">
<stringProp name="collector.help">Success and failure ratio</stringProp>
<stringProp name="collector.metric_name">Ratio</stringProp>
<stringProp name="collector.type">SUCCESS_RATIO</stringProp>
<collectionProp name="collector.labels">
<stringProp name="102727412">label</stringProp>
<stringProp name="3059181">code</stringProp>
</collectionProp>
<stringProp name="collector.quantiles_or_buckets"></stringProp>
<stringProp name="listener.collector.listen_to">samples</stringProp>
<stringProp name="listener.collector.measuring">SuccessRatio</stringProp>
</elementProp>
</collectionProp>
</com.github.johrstrom.listener.PrometheusListener>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
The provided run_test.sh
wraps the command to execute the test, and requires as an argument the URL of the target KonaKart instance.
#!/bin/bash
set -e
TARGET="${1:-konakartoracle.lab.akamas.io}"
IMAGE=chiabre/jmeter_plugins
THIS_DIR=`dirname "${BASH_SOURCE[0]}"`
NOW=`date +'%Y%m%d%H%M%S'`
docker run --rm --name jmeter -i \
--network akamas \
-v /home/ubuntu/konakart-oracle/jmeter:/tmp \
-w /tmp \
-p 9270:9270 \
$IMAGE \
-t Konakart_optimizePerf.jmx -JTARGET_HOST=$TARGET
cp "${THIS_DIR}/jmeter.log" "${THIS_DIR}/jmeter.log.$NOW"
Our modeled system includes the following components:
The oracle
component that models the Oracle Database instance on oradb.mycompany.com, whose parameters are the targets of our optimization
The webapp
component that models the KonaKart service running on konakart.mycompany.com, providing the performance metrics used to validate the system’s SLOs
The first step is defining the system (system.yaml
):
name: oracle system
description: Multi-tier application model featuring Java technology and Oracle Database on cloud
Here’s the definition of our oracle
component (oracle.yaml
), including the parameters needed to connect to the database instances and the filters to fetch metrics from Prometheus.
name: oracle
description: Oracle DB for konakart
componentType: Oracle Database 19c
properties:
connection:
user: user
password: password
host: oradb.mycompany.com
service: konakart
port: 1521
prometheus:
instance: oradb
Notice: in order to update the init parameter the user requires the ALTER SYSTEM
privilege.
Here’s the definition of the konakart
component (konakart.yaml
), containing the filters to fetch the metrics from Prometheus:
name: konakart
description: Web application component for e2e metrics
componentType: Web Application
properties:
prometheus:
instance: jmeter
job: jmeter
We can create the system by running the following command:
akamas create system system.yaml
We can then create the components by running the following commands:
akamas create component oracle.yaml 'oracle system'
akamas create component konakart.yaml 'oracle system'
Since we are using Prometheus to extract the database metrics we can leverage the Prometheus provider, which already includes the queries needed for the Oracle and JMetric queries for the metrics we need. To use the Prometheus provider we need to define a telemetry instance (prom.yaml
):
provider: Prometheus
config:
address: akamas.mycompany.com
port: 9090
logLevel: DETAILED
We can now create the telemetry instance and attach it to our system by running:
akamas create telemetry-instance prom.yaml 'oracle system'
This section outlines the steps performed during the execution of the experiments.
Using an Executor operator we run a command to stop the KonaKart instance using the script provided with the installation, then check the service is not running anymore with a custom script:
- name: stop konakart
operator: Executor
arguments:
retries: 0
command: bash /opt/konakart/bin/stopkonakart.sh
host:
hostname: konakart.mycompany.com
username: ubuntu
key: keyfile
- name: check konakart stop
operator: Executor
arguments:
retries: 0
command: bash /opt/scripts/check_konakart_stop.sh
host:
hostname: konakart.mycompany.com
username: ubuntu
key: keyfile
Attached you can find the referenced script check_konakart_stop.sh:
#! /bin/bash
numOfKonakartRunning=$(ps aux | grep "/opt/konakart/bin" | grep -v "grep" | wc -l)
if [ $numOfKonakartRunning -eq 0 ] ;
then
echo "konakart not running"
exit 0
else
echo "konakart is still running"
exit 1
fi
Using the OracleConfigurator operator to update the Oracle initialization parameters with the new configuration. Then with the Executor operator, we run some custom scripts to restart the database instance to apply the new parameters and check for a successful startup. Additionally, in case of a failed startup, the script of the last task restores a backup of the default configuration file (spfile
), restarts the database, and returns an error code to notify Akamas that the tested configuration is invalid:
- name: update oracle parameters
operator: OracleConfigurator
arguments:
retries: 0
component: oracle
- name: restart oracle
operator: Executor
arguments:
retries: 0
command: sudo su -c 'bash /opt/scripts/restart_db.sh' - oracle
host:
hostname: oradb.mycompany.com
username: opc
key: oraclekey
- name: check oracle status
operator: Executor
arguments:
retries: 0
command: sudo su -c 'bash /opt/scripts/check_db.sh' - oracle
host:
hostname: oradb.mycompany.com
username: opc
key: oraclekey
Attached you can find the referenced script check_db.sh:
#!/bin/bash
ORA_OWNER=oracle
DB_NAME=kona_kart
BAK_FILE=~/akamas/spfilekona.ora
# [o] to avoid self-grep
PMON=[o]ra_pmon_kona
if ps -ef | grep -q $PMON ; then
echo "[INFO] - Database running"
else
if sudo test -f "$BAK_FILE"; then
echo "[INFO] - Trying to restore Oracle database using file $BAK_FILE"
echo "[INFO] - ORACLE_HOME value is: $ORACLE_HOME"
cp "$BAK_FILE" "$ORACLE_HOME/dbs/"
echo "[INFO] - Trying to restart oracle"
echo "[INFO] - Stopping the database"
$ORACLE_HOME/bin/dbshut $ORACLE_HOME
# $ORACLE_HOME/bin/srvctl stop database -d $DB_NAME
cat $ORACLE_HOME/rdbms/log/shutdown.log
echo "[INFO] - Starting the database"
$ORACLE_HOME/bin/dbstart $ORACLE_HOME
# $ORACLE_HOME/bin/srvctl start database -d $DB_NAME
cat $ORACLE_HOME/rdbms/log/startup.log
ps -ef | grep $PMON
else
echo "[ERROR] - The spfile does not exist in current folder!"
fi
exit 255
fi
exit 0
and restart_db.sh:
#!/bin/bash
ORA_OWNER=oracle
DB_NAME=kona_kart
# [o] to avoid self-grep
PMON=[o]ra_pmon_kona
echo "[INFO] - Stopping the database"
$ORACLE_HOME/bin/dbshut $ORACLE_HOME
# $ORACLE_HOME/bin/srvctl stop database -d $DB_NAME
cat $ORACLE_HOME/rdbms/log/shutdown.log
echo "[INFO] - Starting the database"
$ORACLE_HOME/bin/dbstart $ORACLE_HOME
# $ORACLE_HOME/bin/srvctl start database -d $DB_NAME
cat $ORACLE_HOME/rdbms/log/startup.log
ps -ef | grep $PMON
exit 0
We then define the Executor operator tasks that restart the KonaKart service and check it is running correctly:
- name: start konakart
operator: Executor
arguments:
retries: 0
command: /opt/konakart/bin/startkonakart.sh
host:
hostname: konakart.mycompany.com
username: ubuntu
key: keyfile
- name: wait for konakart
operator: Sleep
arguments:
retries: 0
seconds: 30
- name: check konakart service status
operator: Executor
arguments:
retries: 0
command: bash /opt/scripts/check_konakart_start.sh
host:
hostname: konakart.mycompany.com
username: ubuntu
key: keyfile
Attached you can find the referenced script:
#! /bin/bash
numOfKonakartRunning=$(ps aux | grep "/opt/konakart/bin" | grep -v "grep" | wc -l)
if [ $numOfKonakartRunning -eq 1 ] ;
then
echo "konakart running"
exit 0
else
echo "konakart not running"
exit 1
fi
Finally, we define a task that uses the Executor operator to run the JMeter load test against the KonaKart instance:
- name: test
operator: Executor
arguments:
retries: 0
command: bash /opt/scripts/run_test.sh konakart.mycompany.com
host:
hostname: akamas.mycompany.com
username: ubuntu
key: keyfile
By putting together all the tasks defined above we come up with the following workflow definition (workflow.yaml
):
name: konakart_workfl
tasks:
- name: stop konakart
operator: Executor
arguments:
retries: 0
command: bash /opt/konakart/bin/stopkonakart.sh
host:
hostname: konakart.mycompany.com
username: ubuntu
key: keyfile
- name: check konakart stop
operator: Executor
arguments:
retries: 0
command: bash /opt/scripts/check_konakart_stop.sh
host:
hostname: konakart.mycompany.com
username: ubuntu
key: keyfile
- name: update oracle parameters
operator: OracleConfigurator
arguments:
retries: 0
component: oracle
- name: restart oracle
operator: Executor
arguments:
retries: 0
command: sudo su -c 'bash /opt/scripts/restart_db.sh' - oracle
host:
hostname: oradb.mycompany.com
username: opc
key: oraclekey
- name: wait for oracle
operator: Sleep
arguments:
retries: 0
seconds: 60
- name: check oracle status
operator: Executor
arguments:
retries: 0
command: sudo su -c 'bash /opt/scripts/check_db.sh' - oracle
host:
hostname: oradb.mycompany.com
username: opc
key: oraclekey
- name: start konakart
operator: Executor
arguments:
retries: 0
command: /opt/konakart/bin/startkonakart.sh
host:
hostname: konakart.mycompany.com
username: ubuntu
key: keyfile
- name: wait for konakart
operator: Sleep
arguments:
retries: 0
seconds: 30
- name: check konakart service status
operator: Executor
arguments:
retries: 0
command: bash /opt/scripts/check_konakart_start.sh
host:
hostname: konakart.mycompany.com
username: ubuntu
key: keyfile
- name: test
operator: Executor
arguments:
retries: 0
command: bash /opt/scripts/run_test.sh konakart.mycompany.com
host:
hostname: akamas.mycompany.com
username: ubuntu
key: keyfile
We can create the workflow by running:
akamas create workflow workflow.yaml
This study aims to minimize the memory allocated for the Oracle database while under a simulated load of the typical traffic, without impacting the SLOs.
This section provides a step-by-step description of the study definition.
Here’s the definition of the goal for our study, which is to minimize the memory allocated by Oracle to the SGA and PGA memory areas. The constraints ensure that any tested configuration that does not operate within the defined SLOs is flagged as not valid. In particular, the followings are required:
the peak error rate must not exceed 5 errors per seconds
the transaction throughput must not decrease more than 10% with respect to the baseline
the response time must not increase more than 20% with respect to the baseline
goal:
objective: minimize
function:
formula: oracle.oracle_sga_total_size + oracle.oracle_pga_target_size
constraints:
absolute:
- konakart.transactions_error_rate <= 5.0
relativeToBaseline:
- konakart.transactions_throughput >= -10%
- konakart.transactions_response_time <= +20%
We define a window to consider only the data points after the ramp-up time of the load test:
windowing:
type: trim
trim: [200s, 30s]
task: testde
For this study, we are trying to optimize the size of the two main memory areas, meaning the Program Global Area and the Shared Global Area.
Given our goal, we set the domains of the parameters to explore only sizes smaller than the baseline.
parametersSelection:
- name: oracle.sga_target
domain: [256, 1536]
- name: oracle.sga_max_size
domain: [256, 1536]
- name: oracle.pga_aggregate_target
domain: [256, 512]
The following constraint prevents Akamas from exploring configurations that we already know Oracle won’t validate:
parameterConstraints:
- name: SGA
formula: oracle.sga_target <= oracle.sga_max_size
We are going to add to our study two steps:
A baseline step, in which we configure the default values for the memory parameters as discovered from previous manual executions.
An optimization step, where we perform 200 experiments to search the set of parameters that best satisfies our goal.
Here’s what these steps look like:
steps:
- name: Baseline step
type: baseline
values:
oracle.sga_target: 1536
oracle.pga_aggregate_target: 512
oracle.sga_max_size: 1536
oracle.pga_aggregate_limit: 2048
- name: Optimization step
type: optimize
numberOfExperiments: 200
maxFailedExperiments: 200
Here’s the study definition (study.yaml
) for optimizing the Oracle instance:
name: Minimize Oracle memory for KonaKart
system: oracle system
workflow: konakart_workfl
goal:
objective: minimize
function:
formula: oracle.oracle_sga_total_size + oracle.oracle_pga_target_size
constraints:
absolute:
- konakart.transactions_error_rate <= 5.0
relativeToBaseline:
- konakart.transactions_throughput >= -10%
- konakart.transactions_response_time <= +20%
windowing:
type: trim
trim: [200s, 30s]
task: test
parametersSelection:
- name: oracle.sga_target
domain: [256, 1536]
- name: oracle.sga_max_size
domain: [256, 1536]
- name: oracle.pga_aggregate_target
domain: [256, 512]
parameterConstraints:
- name: SGA
formula: oracle.sga_target <= oracle.sga_max_size
trialAggregation: AVG
numberOfTrials: 1
steps:
- name: Baseline step
type: baseline
values:
oracle.sga_target: 1536
oracle.pga_aggregate_target: 512
oracle.sga_max_size: 1536
oracle.pga_aggregate_limit: 2048
- name: Optimization step
type: optimize
numberOfExperiments: 200
maxFailedExperiments: 200
You can create the study by running:
akamas create study study.yaml
You can then start it by running:
akamas start study 'Minimize Oracle memory for KonaKart'