Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
<module>quickfixj-all</module>
<module>quickfixj-distribution</module>
<module>quickfixj-perf-test</module>
</modules>
<module>quickfixj-stress-test</module>
</modules>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand All @@ -69,7 +70,7 @@
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<mainClass/>
<maven.version>3.9.11</maven.version> <!-- please also change version in .mvn/wrapper/maven-wrapper.properties -->
<maven.version>3.9.11</maven.version> <!-- please also change version in .mvn/wrapper/maven-wrapper.properties -->
<maven-libs-version>${maven.version}</maven-libs-version>
<maven-plugin-api-version>${maven.version}</maven-plugin-api-version>
<maven-resources-plugin-version>3.3.1</maven-resources-plugin-version>
Expand Down Expand Up @@ -102,7 +103,9 @@
<orchestra.file>OrchestraFIXLatest.xml</orchestra.file>
<org.quickfixj.orchestra.tools.version>1.0.3</org.quickfixj.orchestra.tools.version>
<jaxen.version>2.0.0</jaxen.version>
<jmh.version>1.37</jmh.version>
<jmh.version>1.37</jmh.version>
<jcstress.version>0.16</jcstress.version>
<hsqldb.version>1.8.0.10</hsqldb.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -273,7 +276,7 @@
<version>${maven-compiler-plugin-version}</version>
<inherited>true</inherited>
<configuration>
<forceJavacCompilerUse>true</forceJavacCompilerUse> <!-- https://bugs.openjdk.java.net/browse/JDK-8216202 -->
<forceLegacyJavacApi>true</forceLegacyJavacApi> <!-- https://bugs.openjdk.java.net/browse/JDK-8216202 -->
<meminitial>2g</meminitial>
<maxmem>4g</maxmem>
</configuration>
Expand Down
2 changes: 1 addition & 1 deletion quickfixj-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.10</version>
<version>${hsqldb.version}</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
89 changes: 55 additions & 34 deletions quickfixj-core/src/main/java/quickfix/JdbcStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,9 @@ class JdbcStore implements MessageStore {

public JdbcStore(SessionSettings settings, SessionID sessionID, DataSource ds) throws Exception {
this.sessionID = sessionID;
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME)) {
sessionTableName = settings
.getString(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME);
} else {
sessionTableName = DEFAULT_SESSION_TABLE_NAME;
}

if (settings.isSetting(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME)) {
messageTableName = settings
.getString(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME);
} else {
messageTableName = DEFAULT_MESSAGE_TABLE_NAME;
}
sessionTableName = getSessionTableName(settings, sessionID);
messageTableName = getMessageTableName(settings, sessionID);

if (settings.isSetting(sessionID, SETTING_JDBC_SESSION_ID_DEFAULT_PROPERTY_VALUE)) {
defaultSessionIdPropertyValue = settings.getString(sessionID,
Expand All @@ -90,34 +80,67 @@ public JdbcStore(SessionSettings settings, SessionID sessionID, DataSource ds) t
loadCache();
}

private void setSqlStrings() {
String idWhereClause = JdbcUtil.getIDWhereClause(extendedSessionIdSupported);
String idColumns = JdbcUtil.getIDColumns(extendedSessionIdSupported);
String idPlaceholders = JdbcUtil.getIDPlaceholders(extendedSessionIdSupported);
public static String getSessionTableName(SessionSettings settings, SessionID sessionID) throws ConfigError {
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME)) {
return settings.getString(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME);
} else {
return DEFAULT_SESSION_TABLE_NAME;
}
}

SQL_UPDATE_SEQNUMS = "UPDATE " + sessionTableName + " SET incoming_seqnum=?, "
+ "outgoing_seqnum=? WHERE " + idWhereClause;
public static String getMessageTableName(SessionSettings settings, SessionID sessionID) throws ConfigError {
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME)) {
return settings.getString(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME);
} else {
return DEFAULT_MESSAGE_TABLE_NAME;
}
}

SQL_INSERT_SESSION = "INSERT INTO " + sessionTableName + " (" + idColumns
+ ", creation_time,incoming_seqnum, outgoing_seqnum) VALUES (" + idPlaceholders
+ ",?,?,?)";
public static String getUpdateSequenceNumsSql(String sessionTableName, String idWhereClause) {
return "UPDATE " + sessionTableName + " SET incoming_seqnum=?, " + "outgoing_seqnum=? WHERE " + idWhereClause;
}

SQL_GET_SEQNUMS = "SELECT creation_time, incoming_seqnum, outgoing_seqnum FROM "
+ sessionTableName + " WHERE " + idWhereClause;
public static String getInsertSessionSql(String sessionTableName, String idColumns, String idPlaceholders) {
return "INSERT INTO " + sessionTableName + " (" + idColumns + ", creation_time,incoming_seqnum, outgoing_seqnum) VALUES (" + idPlaceholders + ",?,?,?)";
}

SQL_UPDATE_MESSAGE = "UPDATE " + messageTableName + " SET message=? " + "WHERE "
+ idWhereClause + " and msgseqnum=?";
public static String getSequenceNumsSql(String sessionTableName, String idWhereClause) {
return "SELECT creation_time, incoming_seqnum, outgoing_seqnum FROM " + sessionTableName + " WHERE " + idWhereClause;
}

SQL_INSERT_MESSAGE = "INSERT INTO " + messageTableName + " (" + idColumns
+ ", msgseqnum,message) VALUES (" + idPlaceholders + ",?,?)";
public static String getUpdateMessageSql(String messageTableName, String idWhereClause) {
return "UPDATE " + messageTableName + " SET message=? " + "WHERE " + idWhereClause + " and msgseqnum=?";
}

SQL_GET_MESSAGES = "SELECT message FROM " + messageTableName + " WHERE " + idWhereClause
+ " and msgseqnum>=? and msgseqnum<=? " + "ORDER BY msgseqnum";
public static String getInsertMessageSql(String messageTableName, String idColumns, String idPlaceholders) {
return "INSERT INTO " + messageTableName + " (" + idColumns + ", msgseqnum,message) VALUES (" + idPlaceholders + ",?,?)";
}

SQL_UPDATE_SESSION = "UPDATE " + sessionTableName + " SET creation_time=?, "
+ "incoming_seqnum=?, outgoing_seqnum=? " + "WHERE " + idWhereClause;
public static String getMessagesSql(String messageTableName, String idWhereClause) {
return "SELECT message FROM " + messageTableName + " WHERE " + idWhereClause + " and msgseqnum>=? and msgseqnum<=? " + "ORDER BY msgseqnum";
}

public static String getUpdateSessionSql(String sessionTableName, String idWhereClause) {
return "UPDATE " + sessionTableName + " SET creation_time=?, " + "incoming_seqnum=?, outgoing_seqnum=? " + "WHERE " + idWhereClause;
}

SQL_DELETE_MESSAGES = "DELETE FROM " + messageTableName + " WHERE " + idWhereClause;
public static String getDeleteMessagesSql(String messageTableName, String idWhereClause) {
return "DELETE FROM " + messageTableName + " WHERE " + idWhereClause;
}

private void setSqlStrings() {
String idWhereClause = JdbcUtil.getIDWhereClause(extendedSessionIdSupported);
String idColumns = JdbcUtil.getIDColumns(extendedSessionIdSupported);
String idPlaceholders = JdbcUtil.getIDPlaceholders(extendedSessionIdSupported);

SQL_UPDATE_SEQNUMS = getUpdateSequenceNumsSql(sessionTableName, idWhereClause);
SQL_INSERT_SESSION = getInsertSessionSql(sessionTableName, idColumns, idPlaceholders);
SQL_GET_SEQNUMS = getSequenceNumsSql(sessionTableName, idWhereClause);
SQL_UPDATE_MESSAGE = getUpdateMessageSql(messageTableName, idWhereClause);
SQL_INSERT_MESSAGE = getInsertMessageSql(messageTableName, idColumns, idPlaceholders);
SQL_GET_MESSAGES = getMessagesSql(messageTableName, idWhereClause);
SQL_UPDATE_SESSION = getUpdateSessionSql(sessionTableName, idWhereClause);
SQL_DELETE_MESSAGES = getDeleteMessagesSql(messageTableName, idWhereClause);
}

private void loadCache() throws SQLException, IOException {
Expand Down Expand Up @@ -238,7 +261,6 @@ public void get(int startSequence, int endSequence, Collection<String> messages)
public boolean set(int sequence, String message) throws IOException {
Connection connection = null;
PreparedStatement insert = null;
ResultSet rs = null;
try {
connection = dataSource.getConnection();
insert = connection.prepareStatement(SQL_INSERT_MESSAGE);
Expand All @@ -263,7 +285,6 @@ public boolean set(int sequence, String message) throws IOException {
}
}
} finally {
JdbcUtil.close(sessionID, rs);
JdbcUtil.close(sessionID, insert);
JdbcUtil.close(sessionID, connection);
}
Expand Down
15 changes: 10 additions & 5 deletions quickfixj-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,16 @@
<artifactId>quickfixj-messages-fix40</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.quickfixj</groupId>
<artifactId>quickfixj-perf-test</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.quickfixj</groupId>
<artifactId>quickfixj-perf-test</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.quickfixj</groupId>
<artifactId>quickfixj-stress-test</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.sleepycat</groupId>
<artifactId>je</artifactId>
Expand Down
80 changes: 80 additions & 0 deletions quickfixj-stress-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# QuickFIX/J Java Concurrency Stress Test

This is a [Java Concurrency Stress (jcstress)](https://github.com/openjdk/jcstress) concurrency stress testing module for QuickFIX/J FIX protocol implementation.

## How to Run

Concurrency stress testing classes can be individually run using your favorite IDE or command line.

### Building the Executable JAR

#### Build Full Project

To build the entire project including all modules:

```bash
$ mvn clean package
```

#### Build Only `quickfixj-stress-test` Module

Build the stress test module with required dependencies, skipping test execution:

```bash
$ mvn clean package -pl quickfixj-stress-test -am -PskipAT,skipBundlePlugin -DskipTests
```

**Command Options Explained:**
- `-pl quickfixj-stress-test` - Build only the stress test module
- `-am` - Also build required dependency modules
- `-PskipAT,skipBundlePlugin` - Skip acceptance tests and bundle plugin
- `-DskipTests` - Skip running unit test cases during build

### Running Tests

#### Run Complete Test Suite

Execute all concurrency regression test cases:

```bash
$ java -jar quickfixj-stress-test/target/quickfixj-stress-test.jar
```

#### Run a Single Test

Run a specific test class or test group (if test cases are nested):

```bash
$ java -jar quickfixj-stress-test/target/quickfixj-stress-test.jar -t JdbcStoreStressTest
```

#### View Available Options

Display all available command-line options:

```bash
$ java -jar quickfixj-stress-test/target/quickfixj-stress-test.jar -h
```

## Common Options

Some useful jcstress options include:

- `h` - Display help
- `-t <test>` - Run specific test or test group
- `-v` - Verbose mode
- `-jvmArgs <args>` - Additional JVM arguments

## Additional Notes

- For CI/CD integration, use the full path to the JAR file
- Stress tests may take significant time to complete depending on your system resources
- Review jcstress documentation for advanced configuration options

## Troubleshooting

If you encounter issues:

- Verify the JAR file was built successfully in `quickfixj-stress-test/target/`
- Check that you have sufficient system resources (CPU, memory)
- Review the jcstress output for specific error messages
74 changes: 74 additions & 0 deletions quickfixj-stress-test/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.quickfixj</groupId>
<artifactId>quickfixj-parent</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>

<artifactId>quickfixj-stress-test</artifactId>
<packaging>jar</packaging>

<name>QuickFIX/J Stress test</name>
<description>QuickFIX/J Stress test</description>
<url>http://www.quickfixj.org</url>

<dependencies>
<dependency>
<groupId>org.quickfixj</groupId>
<artifactId>quickfixj-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jcstress</groupId>
<artifactId>jcstress-core</artifactId>
<version>${jcstress.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<id>create-jcstress-jar</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>${project.artifactId}</finalName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jcstress.Main</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/TestList</resource>
</transformer>
</transformers>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>standalone</shadedClassifierName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Loading