Skip to content

Commit 9cf3711

Browse files
Merge pull request #19 from ibm-messaging/1.0.2
Version 1.0.2
2 parents 0ca9325 + e796e00 commit 9cf3711

File tree

7 files changed

+69
-69
lines changed

7 files changed

+69
-69
lines changed

README.md

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,6 @@ You will need to put the public part of the client's certificate in the queue ma
155155
For troubleshooting, or to better understand the handshake performed by the IBM MQ Java client application in combination with your specific JSSE provider, you can enable debugging by setting `javax.net.debug=ssl` in the JVM environment.
156156

157157

158-
## Performance and syncpoint limit
159-
The connector uses a transacted JMS session to receive messages from MQ in syncpoint and periodically commits the in-flight transaction. This has the effect of batching messages together for improved efficiency. However, the frequency of committing transactions is controlled by the Kafka Connect framework rather than the connector. The connector is only able to receive up to the queue manager's maximum uncommitted message limit (typically 10000 messages) before committing.
160-
161-
By default, Kafka Connect only commits every 60 seconds (10 seconds for the standalone worker), meaning that each task is limited to a rate of about 166 messages per second. You can increase the frequency of committing by using the `offset.flush.interval.ms` configuration in the worker configuration file. For example, if you set `offset.flush.interval.ms=5000`, the connector commits every 5 seconds increasing the maximum rate per task to about 2000 messages per second.
162-
163-
If messages are being received faster than they can be committed, the connector prints a message `Uncommitted message limit reached` and sleeps for a short delay. You should use this as an indication to set the `offset.flush.interval.ms` to a lower value, or increase the number of tasks.
164-
165-
166158
## Configuration
167159
The configuration options for the Kafka Connect source connector for IBM MQ are as follows:
168160

@@ -181,10 +173,11 @@ The configuration options for the Kafka Connect source connector for IBM MQ are
181173
| mq.record.builder.key.header | The JMS message header to use as the Kafka record key | string | | JMSMessageID, JMSCorrelationID, JMSCorrelationIDAsBytes |
182174
| mq.ssl.cipher.suite | The name of the cipher suite for TLS (SSL) connection | string | | Blank or valid cipher suite |
183175
| mq.ssl.peer.name | The distinguished name pattern of the TLS (SSL) peer | string | | Blank or DN pattern |
176+
| mq.batch.size | The maximum number of messages in a batch (unit of work) | integer | 250 | 1 or greater |
184177
| topic | The name of the target Kafka topic | string | | Topic name |
185178

186179
### Using a CCDT file
187-
Some of the connection details for MQ can be provided in a [CCDT file](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.con.doc/q016730_.htm) by setting `mq.ccdt.url` in the Kafka Connect source connector configuration file. If using a CCDT file the `mq.connection.name.list` and `mq.channel.name` configuration options are not required.
180+
Some of the connection details for MQ can be provided in a [CCDT file](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.con.doc/q016730_.htm) by setting `mq.ccdt.url` in the MQ source connector configuration file. If using a CCDT file the `mq.connection.name.list` and `mq.channel.name` configuration options are not required.
188181

189182
### Externalizing secrets
190183
[KIP 297](https://cwiki.apache.org/confluence/display/KAFKA/KIP-297%3A+Externalizing+Secrets+for+Connect+Configurations) introduced a mechanism to externalize secrets to be used as configuration for Kafka connectors.
@@ -215,7 +208,7 @@ mq.password=${file:mq-secret.properties:secret-key}
215208

216209
### Unable to connect to Kafka
217210

218-
You may receive an `org.apache.kafka.common.errors.SslAuthenticationException: SSL handshake failed` error when trying to run the MQ Source Connector using SSL to connect to your Kafka cluster. In the case that the error is caused by the following exception: `Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching XXXXX found.`, Java may be replacing the IP address of your cluster with the corresponding hostname in your `/etc/hosts` file. For example, to push Docker images to a custom Docker repository, you may add an entry in this file which corresponds to the IP of your repository e.g. `123.456.78.90 mycluster.icp`. To fix this, you can comment out this line in your `/etc/hosts` file.
211+
You may receive an `org.apache.kafka.common.errors.SslAuthenticationException: SSL handshake failed` error when trying to run the MQ source connector using SSL to connect to your Kafka cluster. In the case that the error is caused by the following exception: `Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching XXXXX found.`, Java may be replacing the IP address of your cluster with the corresponding hostname in your `/etc/hosts` file. For example, to push Docker images to a custom Docker repository, you may add an entry in this file which corresponds to the IP of your repository e.g. `123.456.78.90 mycluster.icp`. To fix this, you can comment out this line in your `/etc/hosts` file.
219212

220213

221214
## Support
@@ -227,7 +220,7 @@ For issues relating specifically to this connector, please use the [GitHub issue
227220

228221

229222
## License
230-
Copyright 2017, 2018 IBM Corporation
223+
Copyright 2017, 2018, 2019 IBM Corporation
231224

232225
Licensed under the Apache License, Version 2.0 (the "License");
233226
you may not use this file except in compliance with the License.

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<groupId>com.ibm.eventstreams.connect</groupId>
2121
<artifactId>kafka-connect-mq-source</artifactId>
2222
<packaging>jar</packaging>
23-
<version>1.0.2-beta</version>
23+
<version>1.0.2</version>
2424
<name>kafka-connect-mq-source</name>
2525
<organization>
2626
<name>IBM Corporation</name>

src/main/java/com/ibm/eventstreams/connect/mqsource/MQSourceConnector.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,17 @@ public class MQSourceConnector extends SourceConnector {
9292
public static final String CONFIG_DOCUMENTATION_MQ_SSL_PEER_NAME = "The distinguished name pattern of the TLS (SSL) peer.";
9393
public static final String CONFIG_DISPLAY_MQ_SSL_PEER_NAME = "SSL peer name";
9494

95+
public static final String CONFIG_NAME_MQ_BATCH_SIZE = "mq.batch.size";
96+
public static final String CONFIG_DOCUMENTATION_MQ_BATCH_SIZE = "The maximum number of messages in a batch. A batch uses a single unit of work.";
97+
public static final String CONFIG_DISPLAY_MQ_BATCH_SIZE = "Batch size";
98+
public static final int CONFIG_VALUE_MQ_BATCH_SIZE_DEFAULT = 250;
99+
public static final int CONFIG_VALUE_MQ_BATCH_SIZE_MINIMUM = 1;
100+
95101
public static final String CONFIG_NAME_TOPIC = "topic";
96102
public static final String CONFIG_DOCUMENTATION_TOPIC = "The name of the target Kafka topic.";
97103
public static final String CONFIG_DISPLAY_TOPIC = "Target Kafka topic";
98104

99-
public static String VERSION = "1.0.2-beta";
105+
public static String VERSION = "1.0.2";
100106

101107
private Map<String, String> configProps;
102108

@@ -229,6 +235,11 @@ public class MQSourceConnector extends SourceConnector {
229235
CONFIG_DOCUMENTATION_MQ_SSL_PEER_NAME, CONFIG_GROUP_MQ, 13, Width.MEDIUM,
230236
CONFIG_DISPLAY_MQ_SSL_PEER_NAME);
231237

238+
config.define(CONFIG_NAME_MQ_BATCH_SIZE, Type.INT, CONFIG_VALUE_MQ_BATCH_SIZE_DEFAULT,
239+
ConfigDef.Range.atLeast(CONFIG_VALUE_MQ_BATCH_SIZE_MINIMUM), Importance.LOW,
240+
CONFIG_DOCUMENTATION_MQ_BATCH_SIZE, CONFIG_GROUP_MQ, 14, Width.MEDIUM,
241+
CONFIG_DISPLAY_MQ_BATCH_SIZE);
242+
232243
config.define(CONFIG_NAME_TOPIC, Type.STRING, null, Importance.HIGH,
233244
CONFIG_DOCUMENTATION_TOPIC, null, 0, Width.MEDIUM,
234245
CONFIG_DISPLAY_TOPIC);

src/main/java/com/ibm/eventstreams/connect/mqsource/MQSourceTask.java

Lines changed: 47 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232
public class MQSourceTask extends SourceTask {
3333
private static final Logger log = LoggerFactory.getLogger(MQSourceTask.class);
3434

35-
private static int BATCH_SIZE = 250; // The maximum number of records returned per call to poll()
35+
// The maximum number of records returned per call to poll()
36+
private int batchSize = MQSourceConnector.CONFIG_VALUE_MQ_BATCH_SIZE_DEFAULT;
3637
private CountDownLatch batchCompleteSignal = null; // Used to signal completion of a batch
3738
private AtomicInteger pollCycle = new AtomicInteger(1); // Incremented each time poll() is called
3839
private int lastCommitPollCycle = 0; // The value of pollCycle the last time commit() was called
40+
private AtomicBoolean receivingMessages = new AtomicBoolean(); // Whether currently receiving messages
3941
private AtomicBoolean stopNow = new AtomicBoolean(); // Whether stop has been requested
4042

4143
private JMSReader reader;
@@ -69,6 +71,11 @@ public MQSourceTask() {
6971
log.debug("Task props entry {} : {}", entry.getKey(), value);
7072
}
7173

74+
String strBatchSize = props.get(MQSourceConnector.CONFIG_NAME_MQ_BATCH_SIZE);
75+
if (strBatchSize != null) {
76+
batchSize = Integer.parseInt(strBatchSize);
77+
}
78+
7279
// Construct a reader to interface with MQ
7380
reader = new JMSReader();
7481
reader.configure(props);
@@ -106,22 +113,40 @@ public MQSourceTask() {
106113
int currentPollCycle = pollCycle.incrementAndGet();
107114
log.debug("Starting poll cycle {}", currentPollCycle);
108115

109-
if (!stopNow.get()) {
110-
log.info("Polling for records");
111-
SourceRecord src;
112-
do {
113-
// For the first message in the batch, wait a while if no message
114-
src = reader.receive(messageCount == 0);
115-
if (src != null) {
116-
msgs.add(src);
117-
messageCount++;
118-
}
119-
} while ((src != null) && (messageCount < BATCH_SIZE) && !stopNow.get());
116+
try {
117+
receivingMessages.set(true);
118+
119+
if (!stopNow.get()) {
120+
log.info("Polling for records");
121+
SourceRecord src;
122+
do {
123+
// For the first message in the batch, wait a while if no message
124+
src = reader.receive(messageCount == 0);
125+
if (src != null) {
126+
msgs.add(src);
127+
messageCount++;
128+
}
129+
} while ((src != null) && (messageCount < batchSize) && !stopNow.get());
130+
}
131+
else {
132+
log.info("Stopping polling for records");
133+
}
134+
}
135+
finally {
136+
receivingMessages.set(false);
120137
}
121138

122139
synchronized(this) {
123140
if (messageCount > 0) {
124-
batchCompleteSignal = new CountDownLatch(messageCount);
141+
if (!stopNow.get()) {
142+
batchCompleteSignal = new CountDownLatch(messageCount);
143+
}
144+
else {
145+
// Discard this batch - we've rolled back when the connection to MQ was closed in stop()
146+
log.debug("Discarding a batch of {} records as task is stopping", messageCount);
147+
msgs.clear();
148+
batchCompleteSignal = null;
149+
}
125150
}
126151
else {
127152
batchCompleteSignal = null;
@@ -157,7 +182,6 @@ public void commit() throws InterruptedException {
157182
// batch complete signal directly.
158183
int currentPollCycle = pollCycle.get();
159184
log.debug("Commit starting in poll cycle {}", currentPollCycle);
160-
boolean willShutdown = false;
161185

162186
if (lastCommitPollCycle == currentPollCycle)
163187
{
@@ -171,25 +195,10 @@ public void commit() throws InterruptedException {
171195
batchCompleteSignal.countDown();
172196
}
173197
}
174-
else if (stopNow.get()) {
175-
log.debug("Shutting down with empty batch after delay");
176-
willShutdown = true;
177-
}
178198
}
179199
}
180200
else {
181201
lastCommitPollCycle = currentPollCycle;
182-
183-
synchronized (this) {
184-
if ((batchCompleteSignal == null) && stopNow.get()) {
185-
log.debug("Shutting down with empty batch");
186-
willShutdown = true;
187-
}
188-
}
189-
}
190-
191-
if (willShutdown) {
192-
shutdown();
193202
}
194203

195204
log.trace("[{}] Exit {}.commit", Thread.currentThread().getId(), this.getClass().getName());
@@ -210,16 +219,20 @@ else if (stopNow.get()) {
210219

211220
stopNow.set(true);
212221

213-
boolean willShutdown = false;
222+
boolean willClose = false;
214223

215224
synchronized(this) {
216-
if (batchCompleteSignal == null) {
217-
willShutdown = true;
225+
if (receivingMessages.get()) {
226+
log.debug("Will close connection");
227+
willClose = true;
218228
}
219229
}
220230

221-
if (willShutdown) {
222-
shutdown();
231+
if (willClose) {
232+
// Close the connection to MQ to clean up
233+
if (reader != null) {
234+
reader.close();
235+
}
223236
}
224237

225238
log.trace("[{}] Exit {}.stop", Thread.currentThread().getId(), this.getClass().getName());
@@ -247,20 +260,4 @@ else if (stopNow.get()) {
247260

248261
log.trace("[{}] Exit {}.commitRecord", Thread.currentThread().getId(), this.getClass().getName());
249262
}
250-
251-
/**
252-
* <p>
253-
* Shuts down the task, releasing any resource held by the task.
254-
* </p>
255-
*/
256-
private void shutdown() {
257-
log.trace("[{}] Entry {}.shutdown", Thread.currentThread().getId(), this.getClass().getName());
258-
259-
// Close the connection to MQ to clean up
260-
if (reader != null) {
261-
reader.close();
262-
}
263-
264-
log.trace("[{}] Exit {}.shutdown", Thread.currentThread().getId(), this.getClass().getName());
265-
}
266263
}

src/main/java/com/ibm/eventstreams/connect/mqsource/builders/BaseRecordBuilder.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2018 IBM Corporation
2+
* Copyright 2018, 2019 IBM Corporation
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -115,6 +115,8 @@ SchemaAndValue getKey(JMSContext context, String topic, Message message) throws
115115
keySchema = Schema.OPTIONAL_BYTES_SCHEMA;
116116
key = message.getJMSCorrelationIDAsBytes();
117117
break;
118+
default:
119+
break;
118120
}
119121

120122
return new SchemaAndValue(keySchema, key);

src/main/java/com/ibm/eventstreams/connect/mqsource/builders/DefaultRecordBuilder.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2017, 2018 IBM Corporation
2+
* Copyright 2017, 2018, 2019 IBM Corporation
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,7 +24,6 @@
2424
import org.apache.kafka.connect.data.Schema;
2525
import org.apache.kafka.connect.data.SchemaAndValue;
2626
import org.apache.kafka.connect.errors.ConnectException;
27-
import org.apache.kafka.connect.source.SourceRecord;
2827

2928
import org.slf4j.Logger;
3029
import org.slf4j.LoggerFactory;

src/main/java/com/ibm/eventstreams/connect/mqsource/builders/JsonRecordBuilder.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2017, 2018 IBM Corporation
2+
* Copyright 2017, 2018, 2019 IBM Corporation
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,11 +24,9 @@
2424
import javax.jms.Message;
2525
import javax.jms.TextMessage;
2626

27-
import org.apache.kafka.connect.data.Schema;
2827
import org.apache.kafka.connect.data.SchemaAndValue;
2928
import org.apache.kafka.connect.errors.ConnectException;
3029
import org.apache.kafka.connect.json.JsonConverter;
31-
import org.apache.kafka.connect.source.SourceRecord;
3230

3331
import org.slf4j.Logger;
3432
import org.slf4j.LoggerFactory;

0 commit comments

Comments
 (0)