diff --git a/mysql-dual-conn/docker-compose.yml b/mysql-dual-conn/docker-compose.yml
new file mode 100644
index 00000000..60357611
--- /dev/null
+++ b/mysql-dual-conn/docker-compose.yml
@@ -0,0 +1,11 @@
+version: '3.8'
+services:
+ mysql:
+ image: mysql:8.0
+ container_name: mysql-dual-conn
+ environment:
+ MYSQL_ROOT_PASSWORD: rootpass
+ ports:
+ - "3306:3306"
+ volumes:
+ - ./init.sql:/docker-entrypoint-initdb.d/init.sql
diff --git a/mysql-dual-conn/init.sql b/mysql-dual-conn/init.sql
new file mode 100644
index 00000000..d1c2a791
--- /dev/null
+++ b/mysql-dual-conn/init.sql
@@ -0,0 +1,10 @@
+CREATE DATABASE IF NOT EXISTS myntra_oms;
+CREATE DATABASE IF NOT EXISTS camunda;
+
+CREATE USER IF NOT EXISTS 'omsAppUser'@'%' IDENTIFIED BY 'omsPassword';
+GRANT ALL PRIVILEGES ON myntra_oms.* TO 'omsAppUser'@'%';
+
+CREATE USER IF NOT EXISTS 'stagebuster'@'%' IDENTIFIED BY 'camundaPassword';
+GRANT ALL PRIVILEGES ON camunda.* TO 'stagebuster'@'%';
+
+FLUSH PRIVILEGES;
diff --git a/mysql-dual-conn/pom.xml b/mysql-dual-conn/pom.xml
new file mode 100644
index 00000000..00d95024
--- /dev/null
+++ b/mysql-dual-conn/pom.xml
@@ -0,0 +1,48 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.5
+
+
+
+ com.example
+ mysql-dual-conn
+ 0.0.1-SNAPSHOT
+ mysql-dual-conn
+ E2E test for Keploy MySQL multi-connection handshake matching
+
+
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/mysql-dual-conn/src/main/java/com/example/mysqlreplicate/DataSourceConfig.java b/mysql-dual-conn/src/main/java/com/example/mysqlreplicate/DataSourceConfig.java
new file mode 100644
index 00000000..00133952
--- /dev/null
+++ b/mysql-dual-conn/src/main/java/com/example/mysqlreplicate/DataSourceConfig.java
@@ -0,0 +1,108 @@
+package com.example.mysqlreplicate;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import javax.sql.DataSource;
+
+/**
+ * Replicates the multi-datasource setup that triggers the Keploy
+ * "no mysql mocks matched the HandshakeResponse41" error.
+ *
+ * Two HikariCP pools connect to the SAME MySQL server but with
+ * DIFFERENT usernames and databases. During Keploy test replay,
+ * each new TCP connection triggers simulateInitialHandshake which
+ * must match the client's HandshakeResponse41 against recorded
+ * config mocks. The mismatch occurs because:
+ *
+ * 1. mocks[0] (always omsAppUser/myntra_oms) is used to send the
+ * server greeting to ALL incoming connections.
+ * 2. When the camunda pool connects, its HandshakeResponse41 has
+ * username=stagebuster, database=camunda, and different
+ * capability_flags (423535119 vs 20881935).
+ * 3. The matcher loops all config mocks looking for a match on
+ * username + database + capability_flags + charset + filler.
+ * If no recorded config mock matches those exact fields, the
+ * error fires.
+ */
+@Configuration
+public class DataSourceConfig {
+
+ // ---- OMS pool (matches omsAppUser / myntra_oms mocks) ----
+
+ @Value("${datasource.oms.jdbc-url}")
+ private String omsJdbcUrl;
+
+ @Value("${datasource.oms.username}")
+ private String omsUsername;
+
+ @Value("${datasource.oms.password}")
+ private String omsPassword;
+
+ @Value("${datasource.oms.driver-class-name}")
+ private String omsDriverClass;
+
+ // ---- Camunda pool (matches stagebuster / camunda mocks) ----
+
+ @Value("${datasource.camunda.jdbc-url}")
+ private String camundaJdbcUrl;
+
+ @Value("${datasource.camunda.username}")
+ private String camundaUsername;
+
+ @Value("${datasource.camunda.password}")
+ private String camundaPassword;
+
+ @Value("${datasource.camunda.driver-class-name}")
+ private String camundaDriverClass;
+
+ @Bean(name = "omsDataSource", destroyMethod = "close")
+ @Primary
+ public HikariDataSource omsDataSource() {
+ HikariConfig config = new HikariConfig();
+ config.setPoolName("oms-dataSource");
+ config.setUsername(omsUsername);
+ config.setPassword(omsPassword);
+ return buildDataSource(config, 5, omsJdbcUrl, omsDriverClass);
+ }
+
+ @Bean(name = "camundaDataSource", destroyMethod = "close")
+ public HikariDataSource camundaDataSource() {
+ HikariConfig config = new HikariConfig();
+ config.setPoolName("camunda-dataSource");
+ config.setUsername(camundaUsername);
+ config.setPassword(camundaPassword);
+ return buildDataSource(config, 5, camundaJdbcUrl, camundaDriverClass);
+ }
+
+ @Bean(name = "omsJdbcTemplate")
+ @Primary
+ public JdbcTemplate omsJdbcTemplate() {
+ return new JdbcTemplate(omsDataSource());
+ }
+
+ @Bean(name = "camundaJdbcTemplate")
+ public JdbcTemplate camundaJdbcTemplate() {
+ return new JdbcTemplate(camundaDataSource());
+ }
+
+ private HikariDataSource buildDataSource(HikariConfig config, int maxConns,
+ String jdbcUrl, String driverClass) {
+ config.setMaximumPoolSize(maxConns);
+ config.setMinimumIdle(2);
+ config.setKeepaliveTime(5000);
+ config.setIdleTimeout(10000);
+ config.setConnectionTimeout(5000);
+ config.setValidationTimeout(2000);
+ config.setMaxLifetime(7200000);
+ config.setLeakDetectionThreshold(2000);
+ config.setDriverClassName(driverClass);
+ config.setJdbcUrl(jdbcUrl);
+ return new HikariDataSource(config);
+ }
+}
diff --git a/mysql-dual-conn/src/main/java/com/example/mysqlreplicate/MysqlReplicateApplication.java b/mysql-dual-conn/src/main/java/com/example/mysqlreplicate/MysqlReplicateApplication.java
new file mode 100644
index 00000000..ec6b77f9
--- /dev/null
+++ b/mysql-dual-conn/src/main/java/com/example/mysqlreplicate/MysqlReplicateApplication.java
@@ -0,0 +1,11 @@
+package com.example.mysqlreplicate;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class MysqlReplicateApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(MysqlReplicateApplication.class, args);
+ }
+}
diff --git a/mysql-dual-conn/src/main/java/com/example/mysqlreplicate/QueryController.java b/mysql-dual-conn/src/main/java/com/example/mysqlreplicate/QueryController.java
new file mode 100644
index 00000000..376ac6e0
--- /dev/null
+++ b/mysql-dual-conn/src/main/java/com/example/mysqlreplicate/QueryController.java
@@ -0,0 +1,59 @@
+package com.example.mysqlreplicate;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Simple REST controller that queries both datasources.
+ * Each endpoint triggers a DB query that forces a connection
+ * from the respective pool — reproducing the multi-handshake
+ * scenario during Keploy replay.
+ */
+@RestController
+public class QueryController {
+
+ private final JdbcTemplate omsJdbc;
+ private final JdbcTemplate camundaJdbc;
+
+ public QueryController(@Qualifier("omsJdbcTemplate") JdbcTemplate omsJdbc,
+ @Qualifier("camundaJdbcTemplate") JdbcTemplate camundaJdbc) {
+ this.omsJdbc = omsJdbc;
+ this.camundaJdbc = camundaJdbc;
+ }
+
+ /**
+ * Queries both databases, triggering connections from both pools.
+ * During Keploy test mode this forces two distinct HandshakeResponse41
+ * packets with different username/database/capability_flags values.
+ */
+ @GetMapping("/api/query-both")
+ public Map queryBoth() {
+ Map result = new HashMap<>();
+
+ // OMS query — user=omsAppUser, db=myntra_oms
+ List