Skip to content

Commit 28f8e7e

Browse files
[TS-2459] Long recovery in case of mixing recovery message with non-recovery messages (#6)
* Updated libraries
1 parent 5334d08 commit 28f8e7e

File tree

15 files changed

+1610
-1013
lines changed

15 files changed

+1610
-1013
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM gradle:7.6-jdk11 AS build
1+
FROM gradle:8.7-jdk11 AS build
22
ARG release_version
33
COPY ./ .
44
RUN gradle --no-daemon clean build dockerPrepare -Prelease_version=${release_version}

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# th2-conn-dirty-fix (1.2.1)
1+
# th2-conn-dirty-fix (1.3.0)
22

33
This microservice allows sending and receiving messages via FIX protocol
44

@@ -333,6 +333,18 @@ spec:
333333
memory: 100Mi
334334
cpu: 20m
335335
```
336+
## 1.3.0
337+
338+
* Migrated to th2 gradle plugin `0.1.1`
339+
* Updated:
340+
* bom: `4.6.1`
341+
* common: `5.13.1-dev`
342+
* common-utils: `2.2.3-dev`
343+
* conn-dirty-tcp-core: `3.6.0-dev`
344+
* grpc-lw-data-provider: `2.3.1-dev`
345+
* httpclient5: `5.3.1`
346+
* auto-service: `1.1.1`
347+
* kotlin-logging: `3.0.5`
336348

337349
## 1.2.1
338350

build.gradle

Lines changed: 19 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
1-
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2-
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
3-
import com.github.jk1.license.filter.LicenseBundleNormalizer
4-
import com.github.jk1.license.render.JsonReportRenderer
5-
61
plugins {
7-
id 'java'
2+
id "application"
3+
id "com.exactpro.th2.gradle.component" version "0.1.1"
84
id 'org.jetbrains.kotlin.jvm' version '1.8.22'
9-
id 'com.palantir.docker' version '0.25.0'
10-
id "org.owasp.dependencycheck" version "8.3.1"
11-
id "com.gorylenko.gradle-git-properties" version "2.4.1"
12-
id 'com.github.jk1.dependency-license-report' version '2.5'
13-
id "de.undercouch.download" version "5.4.0"
5+
id "org.jetbrains.kotlin.kapt" version "1.8.22"
146
}
157

16-
apply plugin: 'application'
17-
apply plugin: 'com.palantir.docker'
18-
apply plugin: 'kotlin-kapt'
19-
208
group 'com.exactpro.th2'
219
version release_version
2210

23-
sourceCompatibility = 11
24-
targetCompatibility = 11
11+
kotlin {
12+
jvmToolchain(11)
13+
}
2514

2615
repositories {
2716
mavenCentral()
@@ -45,41 +34,34 @@ repositories {
4534
}
4635

4736
dependencies {
48-
api platform('com.exactpro.th2:bom:4.5.0')
49-
50-
implementation("com.exactpro.th2:common:5.4.0-dev") {
37+
implementation("com.exactpro.th2:common:5.13.1-dev") {
5138
exclude group: 'com.exactpro.th2', module: 'task-utils'
5239
}
53-
implementation group: 'com.exactpro.th2', name: 'common-utils', version: '2.2.1-dev'
54-
40+
implementation "com.exactpro.th2:common-utils:2.2.3-dev"
5541
implementation 'com.exactpro.th2:netty-bytebuf-utils:0.0.1'
5642
implementation 'net.lingala.zip4j:zip4j:2.11.5'
57-
implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1'
58-
59-
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2'
60-
61-
implementation 'com.exactpro.th2:conn-dirty-tcp-core:3.4.0-dev'
62-
implementation 'com.exactpro.th2:grpc-lw-data-provider:2.2.0-dev'
43+
implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1'
44+
implementation'com.exactpro.th2:conn-dirty-tcp-core:3.6.0-dev'
45+
implementation 'com.exactpro.th2:grpc-lw-data-provider:2.3.1-dev'
6346

6447
implementation 'org.slf4j:slf4j-api'
65-
implementation 'io.github.microutils:kotlin-logging:3.0.0' // The last version bases on kotlin 1.6.0
48+
implementation 'io.github.microutils:kotlin-logging:3.0.5'
6649
implementation 'org.apache.commons:commons-lang3'
6750

6851
implementation 'io.netty:netty-all'
69-
implementation 'com.google.auto.service:auto-service:1.0.1'
52+
implementation 'com.google.auto.service:auto-service:1.1.1'
7053

7154
implementation 'com.fasterxml.jackson.core:jackson-core'
7255
implementation 'com.fasterxml.jackson.core:jackson-databind'
7356
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
7457

75-
testImplementation 'org.mockito:mockito-core:5.4.0'
76-
testImplementation 'org.mockito:mockito-all:1.10.19'
77-
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5:1.8.10'
78-
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.3'
79-
testImplementation 'org.mockito.kotlin:mockito-kotlin:4.1.0'
58+
testImplementation 'org.mockito:mockito-core:5.12.0'
59+
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5:1.8.22'
60+
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.3'
61+
testImplementation 'org.mockito.kotlin:mockito-kotlin:5.3.1'
8062

81-
annotationProcessor 'com.google.auto.service:auto-service:1.0.1'
82-
kapt 'com.google.auto.service:auto-service:1.0.1'
63+
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
64+
kapt 'com.google.auto.service:auto-service:1.1.1'
8365
}
8466

8567
test {
@@ -90,70 +72,8 @@ application {
9072
mainClass.set('com.exactpro.th2.conn.dirty.tcp.core.Main')
9173
}
9274

93-
applicationName = 'service'
94-
95-
distTar {
96-
archiveFileName.set("${applicationName}.tar")
97-
}
98-
99-
dockerPrepare {
100-
dependsOn distTar
101-
}
102-
103-
docker {
104-
copySpec.from(tarTree("$buildDir/distributions/${applicationName}.tar"))
105-
}
106-
107-
tasks.withType(KotlinCompile).configureEach {
108-
compilerOptions {
109-
jvmTarget.set(JvmTarget.JVM_11)
110-
}
111-
}
112-
113-
test {
114-
testLogging {
115-
events "passed", "skipped", "failed"
116-
// You can adjust the logging level as needed
117-
exceptionFormat "full"
118-
showStandardStreams = true
119-
}
120-
}
121-
12275
dependencyCheck {
123-
formats=['SARIF', 'JSON', 'HTML']
124-
failBuildOnCVSS=5
12576
suppressionFile="suppressions.xml"
126-
12777
//FIXME: we should check all used dependencies
12878
skipConfigurations = ['kapt', 'kaptClasspath_kaptKotlin', 'kaptTest', 'kaptTestFixtures', 'annotationProcessor']
129-
analyzers {
130-
assemblyEnabled = false
131-
nugetconfEnabled = false
132-
nodeEnabled = false
133-
}
134-
}
135-
136-
dependencyLocking {
137-
lockAllConfigurations()
138-
}
139-
140-
licenseReport {
141-
def licenseNormalizerBundlePath = "$buildDir/license-normalizer-bundle.json"
142-
143-
if (!file(licenseNormalizerBundlePath).exists()) {
144-
download.run {
145-
src 'https://raw.githubusercontent.com/th2-net/.github/main/license-compliance/gradle-license-report/license-normalizer-bundle.json'
146-
dest "$buildDir/license-normalizer-bundle.json"
147-
overwrite false
148-
}
149-
}
150-
151-
filters = [
152-
new LicenseBundleNormalizer(licenseNormalizerBundlePath, false)
153-
]
154-
renderers = [
155-
new JsonReportRenderer('licenses.json', false),
156-
]
157-
excludeOwnGroup = false
158-
allowedLicensesFile = new URL("https://raw.githubusercontent.com/th2-net/.github/main/license-compliance/gradle-license-report/allowed-licenses.json")
15979
}

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
release_version=1.2.1
1+
release_version=1.3.0
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
5-
zipStorePath=wrapper/dists
5+
zipStorePath=wrapper/dists

src/main/java/com/exactpro/th2/FixHandler.java

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@
5959
import com.fasterxml.jackson.databind.ObjectMapper;
6060
import io.netty.buffer.ByteBuf;
6161
import io.netty.buffer.Unpooled;
62+
import kotlin.Unit;
63+
import kotlin.jvm.functions.Function1;
64+
import org.apache.commons.lang3.StringUtils;
65+
import org.apache.commons.lang3.exception.ExceptionUtils;
66+
import org.jetbrains.annotations.NotNull;
67+
import org.jetbrains.annotations.Nullable;
68+
import org.slf4j.Logger;
69+
import org.slf4j.LoggerFactory;
70+
6271
import java.io.DataInputStream;
6372
import java.io.DataOutputStream;
6473
import java.io.IOException;
@@ -96,15 +105,6 @@
96105
import java.util.function.Consumer;
97106
import java.util.stream.Collectors;
98107

99-
import kotlin.Unit;
100-
import kotlin.jvm.functions.Function1;
101-
import org.apache.commons.lang3.StringUtils;
102-
import org.apache.commons.lang3.exception.ExceptionUtils;
103-
import org.jetbrains.annotations.NotNull;
104-
import org.jetbrains.annotations.Nullable;
105-
import org.slf4j.Logger;
106-
import org.slf4j.LoggerFactory;
107-
108108
import static com.exactpro.th2.common.event.EventUtils.createMessageBean;
109109
import static com.exactpro.th2.conn.dirty.fix.FixByteBufUtilKt.findField;
110110
import static com.exactpro.th2.conn.dirty.fix.FixByteBufUtilKt.findLastField;
@@ -181,7 +181,6 @@
181181
import static com.exactpro.th2.netty.bytebuf.util.ByteBufUtil.asExpandable;
182182
import static com.exactpro.th2.netty.bytebuf.util.ByteBufUtil.indexOf;
183183
import static com.exactpro.th2.netty.bytebuf.util.ByteBufUtil.isEmpty;
184-
import static com.exactpro.th2.netty.bytebuf.util.ByteBufUtil.startsWith;
185184
import static com.exactpro.th2.util.MessageUtil.findByte;
186185
import static com.exactpro.th2.util.MessageUtil.getBodyLength;
187186
import static com.exactpro.th2.util.MessageUtil.getChecksum;
@@ -392,11 +391,11 @@ public CompletableFuture<MessageID> send(@NotNull ByteBuf body, @NotNull Map<Str
392391
try {
393392
sendingTimeoutHandler.getWithTimeout(channel.open());
394393
} catch (TimeoutException e) {
395-
ExceptionUtils.rethrow(new TimeoutException(
394+
ExceptionUtils.asRuntimeException(new TimeoutException(
396395
String.format("could not open connection before timeout %d mls elapsed",
397396
currentTimeout)));
398397
} catch (Exception e) {
399-
ExceptionUtils.rethrow(e);
398+
ExceptionUtils.asRuntimeException(e);
400399
}
401400
}
402401

@@ -411,7 +410,7 @@ public CompletableFuture<MessageID> send(@NotNull ByteBuf body, @NotNull Map<Str
411410
}
412411
if (System.currentTimeMillis() > deadline) {
413412
// The method should have checked exception in signature...
414-
ExceptionUtils.rethrow(new TimeoutException(String.format("session was not established within %d mls",
413+
ExceptionUtils.asRuntimeException(new TimeoutException(String.format("session was not established within %d mls",
415414
settings.getConnectionTimeoutOnSend())));
416415
}
417416
}
@@ -426,7 +425,7 @@ public CompletableFuture<MessageID> send(@NotNull ByteBuf body, @NotNull Map<Str
426425
}
427426
if (System.currentTimeMillis() > deadline) {
428427
// The method should have checked exception in signature...
429-
ExceptionUtils.rethrow(new TimeoutException(String.format("session was not established within %d mls",
428+
ExceptionUtils.asRuntimeException(new TimeoutException(String.format("session was not established within %d mls",
430429
settings.getConnectionTimeoutOnSend())));
431430
}
432431
}
@@ -496,7 +495,7 @@ public ByteBuf onReceive(@NotNull IChannel channel, @NotNull ByteBuf buffer) {
496495

497496
@NotNull
498497
@Override
499-
public Map<String, String> onIncoming(@NotNull IChannel channel, @NotNull ByteBuf message, MessageID messageId) {
498+
public Map<String, String> onIncoming(@NotNull IChannel channel, @NotNull ByteBuf message, @NotNull MessageID messageId) {
500499
Map<String, String> metadata = new HashMap<>();
501500

502501
StrategyState state = strategy.getState();
@@ -537,7 +536,7 @@ public Map<String, String> onIncoming(@NotNull IChannel channel, @NotNull ByteBu
537536
if(msgTypeValue.equals(MSG_TYPE_LOGOUT)) {
538537
serverMsgSeqNum.incrementAndGet();
539538
state.addMessageID(messageId);
540-
strategy.getIncomingMessageStrategy(x -> x.getLogoutStrategy()).process(message, metadata);
539+
strategy.getIncomingMessageStrategy(IncomingMessagesStrategy::getLogoutStrategy).process(message, metadata);
541540
return metadata;
542541
}
543542

@@ -759,11 +758,7 @@ private void resetSequence(ByteBuf message) {
759758
} else {
760759
int newSeqNo = Integer.parseInt(requireNonNull(seqNumValue.getValue()));
761760
serverMsgSeqNum.updateAndGet(sequence -> {
762-
if(sequence < newSeqNo - 1) {
763-
return newSeqNo - 1;
764-
} else {
765-
return sequence;
766-
}
761+
return Math.max(sequence, newSeqNo - 1);
767762
});
768763
}
769764
}
@@ -850,7 +845,7 @@ private void recovery(int beginSeqNo, int endSeqNo, RecoveryConfig recoveryConfi
850845
}
851846

852847
AtomicBoolean skip = new AtomicBoolean(recoveryConfig.getOutOfOrder());
853-
AtomicReference<ByteBuf> skipped = new AtomicReference(null);
848+
AtomicReference<ByteBuf> skipped = new AtomicReference<>(null);
854849

855850
int endSeq = endSeqNo;
856851
LOGGER.info("Loading messages from {} to {}", beginSeqNo, endSeqNo);
@@ -1596,7 +1591,7 @@ private Map<String, String> gapFillSequenceReset(ByteBuf message, Map<String, St
15961591
onOutgoingUpdateTag(message, metadata);
15971592
FixField msgType = findField(message, MSG_TYPE_TAG, US_ASCII);
15981593

1599-
if(msgType == null || !msgType.getValue().equals(MSG_TYPE_SEQUENCE_RESET)) return null;
1594+
if(msgType == null || !Objects.equals(msgType.getValue(), MSG_TYPE_SEQUENCE_RESET)) return null;
16001595

16011596
if(resendRequestConfig.getGapFill()) return null;
16021597

@@ -1613,11 +1608,13 @@ private Map<String, String> missOutgoingMessages(ByteBuf message, Map<String, St
16131608
int countToMiss = strategy.getMissOutgoingMessagesConfiguration().getCount();
16141609
var strategyState = strategy.getState();
16151610
onOutgoingUpdateTag(message, metadata);
1616-
if(!strategyState.addMissedMessageToCacheIfCondition(msgSeqNum.get(), message.copy(), x -> x <= countToMiss)) {
1617-
return null;
1611+
if(strategyState.addMissedMessageToCacheIfCondition(msgSeqNum.get(), message.copy(), x -> x <= countToMiss)) {
1612+
message.clear();
1613+
}
1614+
if(strategy.getAllowMessagesBeforeRetransmissionFinishes()
1615+
&& Duration.between(strategy.getStartTime(), Instant.now()).compareTo(strategy.getConfig().getDuration()) > 0 ) {
1616+
strategy.disableAllowMessagesBeforeRetransmissionFinishes("after " + strategy.getConfig().getDuration() + " strategy duration");
16181617
}
1619-
1620-
message.clear();
16211618

16221619
return null;
16231620
}
@@ -1772,9 +1769,7 @@ private void runLogonFromAnotherConnection(RuleConfiguration configuration) {
17721769
ByteBuf logonBuf = Unpooled.wrappedBuffer(logon.toString().getBytes(StandardCharsets.UTF_8));
17731770

17741771
channel.send(logonBuf, strategy.getState().enrichProperties(props), null, SendMode.DIRECT_MQ)
1775-
.thenAcceptAsync(x -> {
1776-
strategy.getState().addMessageID(x);
1777-
}, executorService);
1772+
.thenAcceptAsync(x -> strategy.getState().addMessageID(x), executorService);
17781773

17791774
boolean logonSent = false;
17801775
boolean responseReceived = true;
@@ -1783,7 +1778,7 @@ private void runLogonFromAnotherConnection(RuleConfiguration configuration) {
17831778
try(
17841779
Socket socket = new Socket(address.getAddress(), address.getPort());
17851780
DataOutputStream dOut = new DataOutputStream(socket.getOutputStream());
1786-
DataInputStream dIn = new DataInputStream(socket.getInputStream());
1781+
DataInputStream dIn = new DataInputStream(socket.getInputStream())
17871782
){
17881783
socket.setSoTimeout(5000);
17891784

@@ -1817,7 +1812,8 @@ private void runLogonFromAnotherConnection(RuleConfiguration configuration) {
18171812
LOGGER.info("Waiting for 5 seconds to check if main session will be disconnected.");
18181813
Thread.sleep(5000);
18191814
} catch (InterruptedException e) {
1820-
e.printStackTrace();
1815+
LOGGER.error("Interrupted", e);
1816+
Thread.currentThread().interrupt();
18211817
}
18221818

18231819
HashMap<String, Object> additionalDetails = new HashMap<>();
@@ -2228,7 +2224,7 @@ private void applyNextStrategy() {
22282224
ruleErrorEvent(nextStrategyConfig.getRuleType(), null, e);
22292225
}
22302226

2231-
LOGGER.info("Next strategy applied: {}", nextStrategyConfig.getRuleType());
2227+
LOGGER.info("Next strategy applied: {}, duration: {}", nextStrategyConfig.getRuleType(), nextStrategyConfig.getDuration());
22322228
executorService.schedule(this::applyNextStrategy, nextStrategyConfig.getDuration().toMillis(), TimeUnit.MILLISECONDS);
22332229
}
22342230

0 commit comments

Comments
 (0)