Apache JMeter Script
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="PostgreSQL Test Plan">
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath">C:\Users\sascha\Code\apache-jmeter-5.6.3\lib\postgresql-42.7.5.jar</stringProp>
</TestPlan>
<hashTree>
<Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables">
<collectionProp name="Arguments.arguments">
<elementProp name="mainuser" elementType="Argument">
<stringProp name="Argument.name">mainuser</stringProp>
<stringProp name="Argument.value">${__GetSecret(mainuser)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="mainpassword" elementType="Argument">
<stringProp name="Argument.name">mainpassword</stringProp>
<stringProp name="Argument.value">${__GetSecret(mainpassword)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="replicauser" elementType="Argument">
<stringProp name="Argument.name">replicauser</stringProp>
<stringProp name="Argument.value">${__GetSecret(replicauser)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="replicapassword" elementType="Argument">
<stringProp name="Argument.name">replicapassword</stringProp>
<stringProp name="Argument.value">${__GetSecret(replicapassword)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="main_threads" elementType="Argument">
<stringProp name="Argument.name">main_threads</stringProp>
<stringProp name="Argument.value">${__BeanShell(System.getenv("main_threads"))}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="main_loops" elementType="Argument">
<stringProp name="Argument.name">main_loops</stringProp>
<stringProp name="Argument.value">${__BeanShell(System.getenv("main_loops"))}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="main_database" elementType="Argument">
<stringProp name="Argument.name">main_database</stringProp>
<stringProp name="Argument.value">${__BeanShell(System.getenv("main_database"))}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="replica_threads" elementType="Argument">
<stringProp name="Argument.name">replica_threads</stringProp>
<stringProp name="Argument.value">${__BeanShell(System.getenv("replica_threads"))}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="replica_loops" elementType="Argument">
<stringProp name="Argument.name">replica_loops</stringProp>
<stringProp name="Argument.value">${__BeanShell(System.getenv("replica_loops"))}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="replica_database" elementType="Argument">
<stringProp name="Argument.name">replica_database</stringProp>
<stringProp name="Argument.value">${__BeanShell(System.getenv("replica_database"))}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</Arguments>
<hashTree/>
<JDBCDataSource guiclass="TestBeanGUI" testclass="JDBCDataSource" testname="PostgreSQL JDBC Connection Main">
<boolProp name="autocommit">true</boolProp>
<stringProp name="checkQuery">select 1</stringProp>
<stringProp name="connectionAge">5000</stringProp>
<stringProp name="connectionProperties"></stringProp>
<stringProp name="dataSource">primary_db</stringProp>
<stringProp name="dbUrl">${main_database}</stringProp>
<stringProp name="driver">org.postgresql.Driver</stringProp>
<stringProp name="initQuery"></stringProp>
<boolProp name="keepAlive">true</boolProp>
<stringProp name="password">${mainpassword}</stringProp>
<stringProp name="poolMax">100</stringProp>
<boolProp name="preinit">false</boolProp>
<stringProp name="timeout">10000</stringProp>
<stringProp name="transactionIsolation">DEFAULT</stringProp>
<stringProp name="trimInterval">60000</stringProp>
<stringProp name="username">${mainuser}</stringProp>
</JDBCDataSource>
<hashTree/>
<JDBCDataSource guiclass="TestBeanGUI" testclass="JDBCDataSource" testname="JDBC Connection Configuration Replica">
<stringProp name="dataSource">replica_db</stringProp>
<stringProp name="poolMax">100</stringProp>
<stringProp name="timeout">10000</stringProp>
<stringProp name="trimInterval">60000</stringProp>
<boolProp name="autocommit">true</boolProp>
<stringProp name="transactionIsolation">DEFAULT</stringProp>
<boolProp name="preinit">false</boolProp>
<stringProp name="initQuery"></stringProp>
<boolProp name="keepAlive">true</boolProp>
<stringProp name="connectionAge">5000</stringProp>
<stringProp name="checkQuery">select 1</stringProp>
<stringProp name="dbUrl">${replica_database}</stringProp>
<stringProp name="driver">org.postgresql.Driver</stringProp>
<stringProp name="username">${replicauser}</stringProp>
<stringProp name="password">${replicapassword}</stringProp>
<stringProp name="connectionProperties"></stringProp>
</JDBCDataSource>
<hashTree/>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group Write Main Db">
<stringProp name="ThreadGroup.num_threads">${main_threads}</stringProp>
<intProp name="ThreadGroup.ramp_time">10</intProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller">
<stringProp name="LoopController.loops">${main_loops}</stringProp>
<boolProp name="LoopController.continue_forever">false</boolProp>
</elementProp>
</ThreadGroup>
<hashTree>
<JDBCSampler guiclass="TestBeanGUI" testclass="JDBCSampler" testname="WRITE to Main Db">
<stringProp name="dataSource">primary_db</stringProp>
<stringProp name="query">UPDATE public.products
SET price = ${__Random(0000,9999)}
WHERE name = 'Product A';
UPDATE public.products
SET price = ${__Random(0000,9999)}
WHERE name = 'Product B';
UPDATE public.products
SET price = ${__Random(0000,9999)}
WHERE name = 'Product C';
UPDATE public.products
SET price = ${__Random(0000,9999)}
WHERE name = 'Product D';
UPDATE public.products
SET price = ${__Random(0000,9999)}
WHERE name = 'Product E';
</stringProp>
<stringProp name="queryArguments"></stringProp>
<stringProp name="queryArgumentsTypes"></stringProp>
<stringProp name="queryTimeout"></stringProp>
<stringProp name="queryType">Update Statement</stringProp>
<stringProp name="resultSetHandler">Store as String</stringProp>
<stringProp name="resultSetMaxRows"></stringProp>
<stringProp name="resultVariable"></stringProp>
<stringProp name="variableNames"></stringProp>
</JDBCSampler>
<hashTree/>
</hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group Read ReplicaDb">
<stringProp name="ThreadGroup.num_threads">${replica_threads}</stringProp>
<intProp name="ThreadGroup.ramp_time">10</intProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller">
<stringProp name="LoopController.loops">${replica_loops}</stringProp>
<boolProp name="LoopController.continue_forever">false</boolProp>
</elementProp>
</ThreadGroup>
<hashTree>
<JDBCSampler guiclass="TestBeanGUI" testclass="JDBCSampler" testname="READ from Replica Db">
<stringProp name="dataSource">replica_db</stringProp>
<stringProp name="queryType">Select Statement</stringProp>
<stringProp name="query">SELECT *
FROM public.products
ORDER by price DESC
</stringProp>
<stringProp name="queryArguments"></stringProp>
<stringProp name="queryArgumentsTypes"></stringProp>
<stringProp name="variableNames"></stringProp>
<stringProp name="resultVariable"></stringProp>
<stringProp name="queryTimeout"></stringProp>
<stringProp name="resultSetMaxRows"></stringProp>
<stringProp name="resultSetHandler">Store as String</stringProp>
</JDBCSampler>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
Azure Load Test Parameters
Azure Load Test Result
In App result:
Azure Load Test PostgreSQL Lag: