Skip to content

Commit 1257b87

Browse files
committed
JDBC store testing with new jcstress suite
1 parent db4b9be commit 1257b87

File tree

9 files changed

+1308
-44
lines changed

9 files changed

+1308
-44
lines changed

pom.xml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@
5656
<module>quickfixj-all</module>
5757
<module>quickfixj-distribution</module>
5858
<module>quickfixj-perf-test</module>
59-
</modules>
59+
<module>quickfixj-stress-test</module>
60+
</modules>
6061

6162
<properties>
6263
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -69,7 +70,7 @@
6970
<maven.compiler.source>8</maven.compiler.source>
7071
<maven.compiler.target>8</maven.compiler.target>
7172
<mainClass/>
72-
<maven.version>3.9.11</maven.version> <!-- please also change version in .mvn/wrapper/maven-wrapper.properties -->
73+
<maven.version>3.9.11</maven.version> <!-- please also change version in .mvn/wrapper/maven-wrapper.properties -->
7374
<maven-libs-version>${maven.version}</maven-libs-version>
7475
<maven-plugin-api-version>${maven.version}</maven-plugin-api-version>
7576
<maven-resources-plugin-version>3.3.1</maven-resources-plugin-version>
@@ -102,7 +103,9 @@
102103
<orchestra.file>OrchestraFIXLatest.xml</orchestra.file>
103104
<org.quickfixj.orchestra.tools.version>1.0.2</org.quickfixj.orchestra.tools.version>
104105
<jaxen.version>2.0.0</jaxen.version>
105-
<jmh.version>1.37</jmh.version>
106+
<jmh.version>1.37</jmh.version>
107+
<jcstress.version>0.16</jcstress.version>
108+
<hsqldb.version>1.8.0.10</hsqldb.version>
106109
</properties>
107110

108111
<dependencyManagement>
@@ -273,7 +276,7 @@
273276
<version>${maven-compiler-plugin-version}</version>
274277
<inherited>true</inherited>
275278
<configuration>
276-
<forceJavacCompilerUse>true</forceJavacCompilerUse> <!-- https://bugs.openjdk.java.net/browse/JDK-8216202 -->
279+
<forceLegacyJavacApi>true</forceLegacyJavacApi> <!-- https://bugs.openjdk.java.net/browse/JDK-8216202 -->
277280
<meminitial>2g</meminitial>
278281
<maxmem>4g</maxmem>
279282
</configuration>

quickfixj-core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
<dependency>
103103
<groupId>hsqldb</groupId>
104104
<artifactId>hsqldb</artifactId>
105-
<version>1.8.0.10</version>
105+
<version>${hsqldb.version}</version>
106106
<scope>test</scope>
107107
</dependency>
108108
<dependency>

quickfixj-core/src/main/java/quickfix/JdbcStore.java

Lines changed: 81 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,9 @@ class JdbcStore implements MessageStore {
5454

5555
public JdbcStore(SessionSettings settings, SessionID sessionID, DataSource ds) throws Exception {
5656
this.sessionID = sessionID;
57-
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME)) {
58-
sessionTableName = settings
59-
.getString(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME);
60-
} else {
61-
sessionTableName = DEFAULT_SESSION_TABLE_NAME;
62-
}
6357

64-
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME)) {
65-
messageTableName = settings
66-
.getString(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME);
67-
} else {
68-
messageTableName = DEFAULT_MESSAGE_TABLE_NAME;
69-
}
58+
sessionTableName = getSessionTableName(settings, sessionID);
59+
messageTableName = getMessageTableName(settings, sessionID);
7060

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

93-
private void setSqlStrings() {
94-
String idWhereClause = JdbcUtil.getIDWhereClause(extendedSessionIdSupported);
95-
String idColumns = JdbcUtil.getIDColumns(extendedSessionIdSupported);
96-
String idPlaceholders = JdbcUtil.getIDPlaceholders(extendedSessionIdSupported);
83+
public static String getSessionTableName(SessionSettings settings, SessionID sessionID) throws ConfigError {
84+
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME)) {
85+
return settings.getString(sessionID, SETTING_JDBC_STORE_SESSIONS_TABLE_NAME);
86+
} else {
87+
return DEFAULT_SESSION_TABLE_NAME;
88+
}
89+
}
90+
91+
public static String getMessageTableName(SessionSettings settings, SessionID sessionID) throws ConfigError {
92+
if (settings.isSetting(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME)) {
93+
return settings.getString(sessionID, SETTING_JDBC_STORE_MESSAGES_TABLE_NAME);
94+
} else {
95+
return DEFAULT_MESSAGE_TABLE_NAME;
96+
}
97+
}
98+
99+
public static String getUpdateSequenceNumsSql(String sessionTableName, String idWhereClause) {
100+
return "UPDATE " + sessionTableName + " SET incoming_seqnum=?, " + "outgoing_seqnum=? WHERE " + idWhereClause;
101+
}
102+
103+
public static String getInsertSessionSql(String sessionTableName, String idColumns, String idPlaceholders) {
104+
return "INSERT INTO " + sessionTableName + " (" + idColumns + ", creation_time,incoming_seqnum, outgoing_seqnum) VALUES (" + idPlaceholders + ",?,?,?)";
105+
}
97106

98-
SQL_UPDATE_SEQNUMS = "UPDATE " + sessionTableName + " SET incoming_seqnum=?, "
99-
+ "outgoing_seqnum=? WHERE " + idWhereClause;
107+
public static String getSequenceNumsSql(String sessionTableName, String idWhereClause) {
108+
return "SELECT creation_time, incoming_seqnum, outgoing_seqnum FROM " + sessionTableName + " WHERE " + idWhereClause;
109+
}
100110

101-
SQL_INSERT_SESSION = "INSERT INTO " + sessionTableName + " (" + idColumns
102-
+ ", creation_time,incoming_seqnum, outgoing_seqnum) VALUES (" + idPlaceholders
103-
+ ",?,?,?)";
111+
public static String getUpdateMessageSql(String messageTableName, String idWhereClause) {
112+
return "UPDATE " + messageTableName + " SET message=? " + "WHERE " + idWhereClause + " and msgseqnum=?";
113+
}
104114

105-
SQL_GET_SEQNUMS = "SELECT creation_time, incoming_seqnum, outgoing_seqnum FROM "
106-
+ sessionTableName + " WHERE " + idWhereClause;
115+
public static String getInsertMessageSql(String messageTableName, String idColumns, String idPlaceholders) {
116+
return "INSERT INTO " + messageTableName + " (" + idColumns + ", msgseqnum,message) VALUES (" + idPlaceholders + ",?,?)";
117+
}
107118

108-
SQL_UPDATE_MESSAGE = "UPDATE " + messageTableName + " SET message=? " + "WHERE "
109-
+ idWhereClause + " and msgseqnum=?";
119+
public static String getMessagesSql(String messageTableName, String idWhereClause) {
120+
return "SELECT message FROM " + messageTableName + " WHERE " + idWhereClause + " and msgseqnum>=? and msgseqnum<=? " + "ORDER BY msgseqnum";
121+
}
110122

111-
SQL_INSERT_MESSAGE = "INSERT INTO " + messageTableName + " (" + idColumns
112-
+ ", msgseqnum,message) VALUES (" + idPlaceholders + ",?,?)";
123+
public static String getUpdateSessionSql(String sessionTableName, String idWhereClause) {
124+
return "UPDATE " + sessionTableName + " SET creation_time=?, " + "incoming_seqnum=?, outgoing_seqnum=? " + "WHERE " + idWhereClause;
125+
}
113126

114-
SQL_GET_MESSAGES = "SELECT message FROM " + messageTableName + " WHERE " + idWhereClause
115-
+ " and msgseqnum>=? and msgseqnum<=? " + "ORDER BY msgseqnum";
127+
public static String getDeleteMessagesSql(String messageTableName, String idWhereClause) {
128+
return "DELETE FROM " + messageTableName + " WHERE " + idWhereClause;
129+
}
116130

117-
SQL_UPDATE_SESSION = "UPDATE " + sessionTableName + " SET creation_time=?, "
118-
+ "incoming_seqnum=?, outgoing_seqnum=? " + "WHERE " + idWhereClause;
131+
private void setSqlStrings() {
132+
String idWhereClause = JdbcUtil.getIDWhereClause(extendedSessionIdSupported);
133+
String idColumns = JdbcUtil.getIDColumns(extendedSessionIdSupported);
134+
String idPlaceholders = JdbcUtil.getIDPlaceholders(extendedSessionIdSupported);
119135

120-
SQL_DELETE_MESSAGES = "DELETE FROM " + messageTableName + " WHERE " + idWhereClause;
136+
SQL_UPDATE_SEQNUMS = getUpdateSequenceNumsSql(sessionTableName, idWhereClause);
137+
SQL_INSERT_SESSION = getInsertSessionSql(sessionTableName, idColumns, idPlaceholders);
138+
SQL_GET_SEQNUMS = getSequenceNumsSql(sessionTableName, idWhereClause);
139+
SQL_UPDATE_MESSAGE = getUpdateMessageSql(messageTableName, idWhereClause);
140+
SQL_INSERT_MESSAGE = getInsertMessageSql(messageTableName, idColumns, idPlaceholders);
141+
SQL_GET_MESSAGES = getMessagesSql(messageTableName, idWhereClause);
142+
SQL_UPDATE_SESSION = getUpdateSessionSql(sessionTableName, idWhereClause);
143+
SQL_DELETE_MESSAGES = getDeleteMessagesSql(messageTableName, idWhereClause);
121144
}
122145

123146
private void loadCache() throws SQLException, IOException {
@@ -171,6 +194,32 @@ public int getNextTargetMsgSeqNum() throws IOException {
171194
return cache.getNextTargetMsgSeqNum();
172195
}
173196

197+
int getNextSenderMsgSeqNumFromDb() throws SQLException {
198+
return getNextMsgSeqNumsFromDb()[0];
199+
}
200+
201+
int getNextTargetMsgSeqNumFromDb() throws SQLException {
202+
return getNextMsgSeqNumsFromDb()[1];
203+
}
204+
205+
int[] getNextMsgSeqNumsFromDb() throws SQLException {
206+
try (Connection connection = dataSource.getConnection()) {
207+
try (PreparedStatement query = connection.prepareStatement(SQL_GET_SEQNUMS)) {
208+
setSessionIdParameters(query, 1);
209+
210+
try (ResultSet result = query.executeQuery()) {
211+
if (result.next()) {
212+
int targetSeqNum = result.getInt(2);
213+
int senderSeqNum = result.getInt(3);
214+
return new int[] {senderSeqNum, targetSeqNum};
215+
} else {
216+
return new int[] {-1, -1};
217+
}
218+
}
219+
}
220+
}
221+
}
222+
174223
public void incrNextSenderMsgSeqNum() throws IOException {
175224
cache.incrNextSenderMsgSeqNum();
176225
setNextSenderMsgSeqNum(cache.getNextSenderMsgSeqNum());
@@ -238,7 +287,6 @@ public void get(int startSequence, int endSequence, Collection<String> messages)
238287
public boolean set(int sequence, String message) throws IOException {
239288
Connection connection = null;
240289
PreparedStatement insert = null;
241-
ResultSet rs = null;
242290
try {
243291
connection = dataSource.getConnection();
244292
insert = connection.prepareStatement(SQL_INSERT_MESSAGE);
@@ -263,7 +311,6 @@ public boolean set(int sequence, String message) throws IOException {
263311
}
264312
}
265313
} finally {
266-
JdbcUtil.close(sessionID, rs);
267314
JdbcUtil.close(sessionID, insert);
268315
JdbcUtil.close(sessionID, connection);
269316
}

quickfixj-distribution/pom.xml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,16 @@
122122
<artifactId>quickfixj-messages-fix40</artifactId>
123123
<version>${project.version}</version>
124124
</dependency>
125-
<dependency>
126-
<groupId>org.quickfixj</groupId>
127-
<artifactId>quickfixj-perf-test</artifactId>
128-
<version>${project.version}</version>
129-
</dependency>
125+
<dependency>
126+
<groupId>org.quickfixj</groupId>
127+
<artifactId>quickfixj-perf-test</artifactId>
128+
<version>${project.version}</version>
129+
</dependency>
130+
<dependency>
131+
<groupId>org.quickfixj</groupId>
132+
<artifactId>quickfixj-stress-test</artifactId>
133+
<version>${project.version}</version>
134+
</dependency>
130135
<dependency>
131136
<groupId>com.sleepycat</groupId>
132137
<artifactId>je</artifactId>

quickfixj-stress-test/README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# QuickFIX/J Java Concurrency Stress Test
2+
3+
This is a [Java Concurrency Stress (jcstress)](https://github.com/openjdk/jcstress) concurrency stress testing module for QuickFIX/J FIX protocol implementation.
4+
5+
## How to Run
6+
7+
Concurrency stress testing classes can be individually run using your favorite IDE or command line.
8+
9+
### Building the Executable JAR
10+
11+
#### Build Full Project
12+
13+
To build the entire project including all modules:
14+
15+
```bash
16+
$ mvn clean package
17+
```
18+
19+
#### Build Only `quickfixj-stress-test` Module
20+
21+
Build the stress test module with required dependencies, skipping test execution:
22+
23+
```bash
24+
$ mvn clean package -pl quickfixj-stress-test -am -PskipAT,skipBundlePlugin -DskipTests
25+
```
26+
27+
**Command Options Explained:**
28+
- `-pl quickfixj-stress-test` - Build only the stress test module
29+
- `-am` - Also build required dependency modules
30+
- `-PskipAT,skipBundlePlugin` - Skip acceptance tests and bundle plugin
31+
- `-DskipTests` - Skip running unit test cases during build
32+
33+
### Running Tests
34+
35+
#### Run Complete Test Suite
36+
37+
Execute all concurrency regression test cases:
38+
39+
```bash
40+
$ java -jar quickfixj-stress-test/target/quickfixj-stress-test.jar
41+
```
42+
43+
#### Run a Single Test
44+
45+
Run a specific test class or test group (if test cases are nested):
46+
47+
```bash
48+
$ java -jar quickfixj-stress-test/target/quickfixj-stress-test.jar -t JdbcStoreStressTest
49+
```
50+
51+
#### View Available Options
52+
53+
Display all available command-line options:
54+
55+
```bash
56+
$ java -jar quickfixj-stress-test/target/quickfixj-stress-test.jar -h
57+
```
58+
59+
## Common Options
60+
61+
Some useful jcstress options include:
62+
63+
- `-t <test>` - Run specific test or test group
64+
- `-v` - Verbose mode
65+
- `-time <ms>` - Duration for each test iteration
66+
- `-iters <n>` - Number of iterations
67+
- `-jvmArgs <args>` - Additional JVM arguments
68+
69+
## Additional Notes
70+
71+
- Test results are generated in the `results/` directory by default
72+
- For CI/CD integration, use the full path to the JAR file
73+
- Stress tests may take significant time to complete depending on your system resources
74+
- Review jcstress documentation for advanced configuration options
75+
76+
## Troubleshooting
77+
78+
If you encounter issues:
79+
80+
1. Ensure all prerequisites are met
81+
2. Verify the JAR file was built successfully in `quickfixj-stress-test/target/`
82+
3. Check that you have sufficient system resources (CPU, memory)
83+
4. Review the jcstress output for specific error messages

quickfixj-stress-test/pom.xml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.quickfixj</groupId>
9+
<artifactId>quickfixj-parent</artifactId>
10+
<version>3.0.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>quickfixj-stress-test</artifactId>
14+
<packaging>jar</packaging>
15+
16+
<name>QuickFIX/J Stress test</name>
17+
<description>QuickFIX/J Stress test</description>
18+
<url>http://www.quickfixj.org</url>
19+
20+
<dependencies>
21+
<dependency>
22+
<groupId>org.quickfixj</groupId>
23+
<artifactId>quickfixj-core</artifactId>
24+
<version>${project.version}</version>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.openjdk.jcstress</groupId>
28+
<artifactId>jcstress-core</artifactId>
29+
<version>${jcstress.version}</version>
30+
</dependency>
31+
<dependency>
32+
<groupId>hsqldb</groupId>
33+
<artifactId>hsqldb</artifactId>
34+
<version>${hsqldb.version}</version>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.slf4j</groupId>
38+
<artifactId>slf4j-jdk14</artifactId>
39+
<version>${slf4j.version}</version>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.mockito</groupId>
43+
<artifactId>mockito-core</artifactId>
44+
<scope>compile</scope>
45+
</dependency>
46+
</dependencies>
47+
48+
<build>
49+
<plugins>
50+
<plugin>
51+
<groupId>org.apache.maven.plugins</groupId>
52+
<artifactId>maven-shade-plugin</artifactId>
53+
<executions>
54+
<execution>
55+
<id>create-jcstress-jar</id>
56+
<phase>package</phase>
57+
<goals>
58+
<goal>shade</goal>
59+
</goals>
60+
<configuration>
61+
<finalName>${project.artifactId}</finalName>
62+
<transformers>
63+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
64+
<mainClass>org.openjdk.jcstress.Main</mainClass>
65+
</transformer>
66+
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
67+
<resource>META-INF/TestList</resource>
68+
</transformer>
69+
</transformers>
70+
<createDependencyReducedPom>false</createDependencyReducedPom>
71+
<shadedArtifactAttached>true</shadedArtifactAttached>
72+
<shadedClassifierName>standalone</shadedClassifierName>
73+
</configuration>
74+
</execution>
75+
</executions>
76+
</plugin>
77+
</plugins>
78+
</build>
79+
</project>

0 commit comments

Comments
 (0)