Skip to content

Commit e34f70d

Browse files
committed
feat: migrate from HLRC to elasticsearch-java 8.x client
1 parent 4c47b00 commit e34f70d

26 files changed

Lines changed: 886 additions & 1042 deletions

checkstyle/suppressions.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<!-- switch statements on types exceed maximum complexity -->
1010
<suppress
11-
checks="(CyclomaticComplexity)"
11+
checks="(CyclomaticComplexity|NPathComplexity)"
1212
files="(Mapping|DataConverter).java"
1313
/>
1414

pom.xml

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
</scm>
3838

3939
<properties>
40-
<es.version>7.17.24</es.version>
40+
<elasticsearch.java.version>8.19.15</elasticsearch.java.version>
4141
<lz4.version>1.10.1</lz4.version>
4242
<hamcrest.version>1.3</hamcrest.version>
4343
<gson.version>2.9.0</gson.version>
@@ -84,23 +84,8 @@
8484
</dependency>
8585
<dependency>
8686
<groupId>org.elasticsearch.client</groupId>
87-
<artifactId>elasticsearch-rest-high-level-client</artifactId>
88-
<version>${es.version}</version>
89-
</dependency>
90-
<dependency>
91-
<groupId>org.elasticsearch</groupId>
92-
<artifactId>elasticsearch</artifactId>
93-
<version>${es.version}</version>
94-
<exclusions>
95-
<exclusion>
96-
<groupId>org.yaml</groupId>
97-
<artifactId>snakeyaml</artifactId>
98-
</exclusion>
99-
<exclusion>
100-
<groupId>org.lz4</groupId>
101-
<artifactId>lz4-java</artifactId>
102-
</exclusion>
103-
</exclusions>
87+
<artifactId>elasticsearch-rest-client</artifactId>
88+
<version>${elasticsearch.java.version}</version>
10489
</dependency>
10590
<dependency>
10691
<groupId>org.apache.logging.log4j</groupId>
@@ -115,7 +100,7 @@
115100
<dependency>
116101
<groupId>co.elastic.clients</groupId>
117102
<artifactId>elasticsearch-java</artifactId>
118-
<version>8.2.2</version>
103+
<version>${elasticsearch.java.version}</version>
119104
</dependency>
120105
<dependency>
121106
<groupId>org.eclipse.parsson</groupId>
@@ -508,7 +493,7 @@
508493
</tags>
509494

510495
<requirements>
511-
<requirement>Elasticsearch 7.x</requirement>
496+
<requirement>Elasticsearch 8.x / 9.x</requirement>
512497
</requirements>
513498

514499
<deliveryGuarantee>

src/main/java/io/confluent/connect/elasticsearch/DataConverter.java

Lines changed: 74 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,10 @@
3737
import org.apache.kafka.connect.storage.Converter;
3838
import org.apache.kafka.connect.storage.HeaderConverter;
3939
import org.apache.kafka.connect.storage.SimpleHeaderConverter;
40-
import org.elasticsearch.action.DocWriteRequest;
41-
import org.elasticsearch.action.DocWriteRequest.OpType;
42-
import org.elasticsearch.action.delete.DeleteRequest;
43-
import org.elasticsearch.action.index.IndexRequest;
44-
import org.elasticsearch.action.update.UpdateRequest;
45-
import org.elasticsearch.index.VersionType;
46-
import org.elasticsearch.xcontent.XContentType;
40+
import co.elastic.clients.elasticsearch._types.VersionType;
41+
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
42+
import co.elastic.clients.json.JsonData;
43+
4744
import org.slf4j.Logger;
4845
import org.slf4j.LoggerFactory;
4946

@@ -121,7 +118,7 @@ private String convertKey(Schema keySchema, Object key) {
121118
}
122119
}
123120

124-
public DocWriteRequest<?> convertRecord(SinkRecord record, String resourceName) {
121+
public BulkOperation convertRecord(SinkRecord record, String resourceName) {
125122
if (record.value() == null) {
126123
switch (config.behaviorOnNullValues()) {
127124
case IGNORE:
@@ -166,32 +163,91 @@ public DocWriteRequest<?> convertRecord(SinkRecord record, String resourceName)
166163

167164
// delete
168165
if (record.value() == null) {
169-
return maybeAddExternalVersioning(new DeleteRequest(resourceName).id(id), record);
166+
if (!config.isDataStream() && !config.shouldIgnoreKey(record.topic())) {
167+
final long externalVersion = resolveExternalVersion(record);
168+
return BulkOperation.of(b -> b.delete(d -> d
169+
.index(resourceName).id(id)
170+
.versionType(VersionType.External).version(externalVersion)));
171+
}
172+
return BulkOperation.of(b -> b.delete(d -> d.index(resourceName).id(id)));
170173
}
171174

172175
String payload = getPayload(record);
173176
payload = maybeAddTimestamp(payload, record.timestamp());
177+
final String finalPayload = payload;
174178

175179
// index
176180
switch (config.writeMethod()) {
177181
case UPSERT:
178-
return new UpdateRequest(resourceName, id)
179-
.doc(payload, XContentType.JSON)
180-
.upsert(payload, XContentType.JSON)
181-
.retryOnConflict(Math.min(config.maxInFlightRequests(), 5));
182+
return BulkOperation.of(b -> b.update(u -> u
183+
.index(resourceName)
184+
.id(id)
185+
.retryOnConflict(Math.min(config.maxInFlightRequests(), 5))
186+
.action(a -> a
187+
.doc(JsonData.fromJson(finalPayload))
188+
.upsert(JsonData.fromJson(finalPayload))
189+
)
190+
));
182191
case INSERT:
183-
OpType opType = config.isDataStream() ? OpType.CREATE : OpType.INDEX;
184-
IndexRequest req =
185-
new IndexRequest(resourceName).source(payload, XContentType.JSON).opType(opType);
192+
if (config.isDataStream()) {
193+
if (config.useAutogeneratedIds()) {
194+
return BulkOperation.of(b -> b.create(c -> c
195+
.index(resourceName)
196+
.document(JsonData.fromJson(finalPayload))
197+
));
198+
}
199+
return BulkOperation.of(b -> b.create(c -> c
200+
.index(resourceName).id(id)
201+
.document(JsonData.fromJson(finalPayload))
202+
));
203+
}
186204
if (config.useAutogeneratedIds()) {
187-
return req;
205+
return BulkOperation.of(b -> b.index(i -> i
206+
.index(resourceName)
207+
.document(JsonData.fromJson(finalPayload))
208+
));
188209
}
189-
return maybeAddExternalVersioning(req.id(id), record);
210+
if (!config.shouldIgnoreKey(record.topic())) {
211+
final long externalVersion = resolveExternalVersion(record);
212+
return BulkOperation.of(b -> b.index(i -> i
213+
.index(resourceName).id(id)
214+
.document(JsonData.fromJson(finalPayload))
215+
.versionType(VersionType.External).version(externalVersion)));
216+
}
217+
return BulkOperation.of(b -> b.index(i -> i
218+
.index(resourceName).id(id)
219+
.document(JsonData.fromJson(finalPayload))
220+
));
190221
default:
191222
return null; // shouldn't happen
192223
}
193224
}
194225

226+
/**
227+
* In many cases, we explicitly set the record version using the topic's offset.
228+
* This version will, in turn, be checked by Elasticsearch and will throw a versioning
229+
* error if the request represents an equivalent or older version of the record.
230+
*
231+
* @param record the record to be processed
232+
* @return the version to use for the Elasticsearch request
233+
*/
234+
private long resolveExternalVersion(SinkRecord record) {
235+
if (config.hasExternalVersionHeader()) {
236+
final Header versionHeader = record.headers().lastWithName(config.externalVersionHeader());
237+
final byte[] versionValue = HEADER_CONVERTER.fromConnectHeader(
238+
record.topic(), versionHeader.key(), versionHeader.schema(), versionHeader.value()
239+
);
240+
try {
241+
//fromConnectHeader byte output is UTF_8
242+
return Long.parseLong(new String(versionValue, StandardCharsets.UTF_8));
243+
} catch (NumberFormatException e) {
244+
throw new ConnectException("Error converting to long: "
245+
+ new String(versionValue, StandardCharsets.UTF_8), e);
246+
}
247+
}
248+
return record.kafkaOffset();
249+
}
250+
195251
private String getPayload(SinkRecord record) {
196252
if (record.value() == null) {
197253
return null;
@@ -241,44 +297,6 @@ private String maybeAddTimestamp(String payload, Long timestamp) {
241297
return payload;
242298
}
243299

244-
/**
245-
* In many cases, we explicitly set the record version using the topic's offset.
246-
* This version will, in turn, be checked by Elasticsearch and will throw a versioning
247-
* error if the request represents an equivalent or older version of the record.
248-
*
249-
* @param request the request currently being constructed for `record`
250-
* @param record the record to be processed
251-
* @return the (possibly modified) request which was passed in
252-
*/
253-
private DocWriteRequest<?> maybeAddExternalVersioning(
254-
DocWriteRequest<?> request,
255-
SinkRecord record
256-
) {
257-
if (!config.isDataStream() && !config.shouldIgnoreKey(record.topic())) {
258-
request.versionType(VersionType.EXTERNAL);
259-
if (config.hasExternalVersionHeader()) {
260-
final Header versionHeader = record.headers().lastWithName(config.externalVersionHeader());
261-
final byte[] versionValue = HEADER_CONVERTER.fromConnectHeader(
262-
record.topic(),
263-
versionHeader.key(),
264-
versionHeader.schema(),
265-
versionHeader.value()
266-
);
267-
try {
268-
//fromConnectHeader byte output is UTF_8
269-
request.version(Long.parseLong(new String(versionValue, StandardCharsets.UTF_8)));
270-
} catch (NumberFormatException e) {
271-
throw new ConnectException("Error converting to long: "
272-
+ new String(versionValue, StandardCharsets.UTF_8), e);
273-
}
274-
} else {
275-
request.version(record.kafkaOffset());
276-
}
277-
}
278-
279-
return request;
280-
}
281-
282300
// We need to pre process the Kafka Connect schema before converting to JSON as Elasticsearch
283301
// expects a different JSON format from the current JSON converter provides. Rather than
284302
// completely rewrite a converter for Elasticsearch, we will refactor the JSON converter to

0 commit comments

Comments
 (0)