@@ -30,7 +30,7 @@ import org.apache.kafka.common.message.CreateTopicsRequestData
30
30
import org .apache .kafka .common .message .CreateTopicsRequestData .{CreatableTopic , CreatableTopicConfig , CreatableTopicConfigCollection }
31
31
import org .apache .kafka .common .message .MetadataResponseData .MetadataResponseTopic
32
32
import org .apache .kafka .common .protocol .{ApiKeys , Errors }
33
- import org .apache .kafka .common .requests .{CreateTopicsRequest , CreateTopicsResponse , RequestContext , RequestHeader }
33
+ import org .apache .kafka .common .requests .{AbstractResponse , CreateTopicsRequest , CreateTopicsResponse , EnvelopeResponse , RequestContext , RequestHeader }
34
34
import org .apache .kafka .coordinator .group .GroupCoordinator
35
35
import org .apache .kafka .coordinator .share .ShareCoordinator
36
36
import org .apache .kafka .coordinator .transaction .TransactionLogConfig
@@ -198,6 +198,22 @@ class DefaultAutoTopicCreationManager(
198
198
.setTopics(topicsToCreate)
199
199
)
200
200
201
+ // Capture request header information for proper envelope response parsing
202
+ val requestHeaderForParsing = requestContext.map { context =>
203
+ val requestVersion =
204
+ channelManager.controllerApiVersions.toScala match {
205
+ case None =>
206
+ ApiKeys .CREATE_TOPICS .latestVersion()
207
+ case Some (nodeApiVersions) =>
208
+ nodeApiVersions.latestUsableVersion(ApiKeys .CREATE_TOPICS )
209
+ }
210
+
211
+ new RequestHeader (ApiKeys .CREATE_TOPICS ,
212
+ requestVersion,
213
+ context.clientId,
214
+ context.correlationId)
215
+ }
216
+
201
217
val requestCompletionHandler = new ControllerRequestCompletionHandler {
202
218
override def onTimeout (): Unit = {
203
219
clearInflightRequests(creatableTopics)
@@ -213,6 +229,33 @@ class DefaultAutoTopicCreationManager(
213
229
} else {
214
230
if (response.hasResponse) {
215
231
response.responseBody() match {
232
+ case envelopeResponse : EnvelopeResponse =>
233
+ // Unwrap the envelope response to get the actual CreateTopicsResponse
234
+ val envelopeError = envelopeResponse.error()
235
+ if (envelopeError != Errors .NONE ) {
236
+ warn(s " Auto topic creation failed for ${creatableTopics.keys} with envelope error: ${envelopeError}" )
237
+ } else {
238
+ requestHeaderForParsing match {
239
+ case Some (requestHeader) =>
240
+ try {
241
+ // Use the captured request header for proper envelope response parsing
242
+ val createTopicsResponse = AbstractResponse .parseResponse(
243
+ envelopeResponse.responseData(), requestHeader).asInstanceOf [CreateTopicsResponse ]
244
+
245
+ createTopicsResponse.data().topics().forEach(topicResult => {
246
+ val error = Errors .forCode(topicResult.errorCode)
247
+ if (error != Errors .NONE ) {
248
+ warn(s " Auto topic creation failed for ${topicResult.name} with error ' ${error.name}': ${topicResult.errorMessage}" )
249
+ }
250
+ })
251
+ } catch {
252
+ case e : Exception =>
253
+ warn(s " Failed to parse envelope response for auto topic creation of ${creatableTopics.keys}" , e)
254
+ }
255
+ case None =>
256
+ warn(s " Cannot parse envelope response without original request header information " )
257
+ }
258
+ }
216
259
case createTopicsResponse : CreateTopicsResponse =>
217
260
createTopicsResponse.data().topics().forEach(topicResult => {
218
261
val error = Errors .forCode(topicResult.errorCode)
@@ -229,26 +272,13 @@ class DefaultAutoTopicCreationManager(
229
272
}
230
273
}
231
274
232
- val request = requestContext.map { context =>
233
- val requestVersion =
234
- channelManager.controllerApiVersions.toScala match {
235
- case None =>
236
- // We will rely on the Metadata request to be retried in the case
237
- // that the latest version is not usable by the controller.
238
- ApiKeys .CREATE_TOPICS .latestVersion()
239
- case Some (nodeApiVersions) =>
240
- nodeApiVersions.latestUsableVersion(ApiKeys .CREATE_TOPICS )
241
- }
242
-
243
- // Borrow client information such as client id and correlation id from the original request,
244
- // in order to correlate the create request with the original metadata request.
245
- val requestHeader = new RequestHeader (ApiKeys .CREATE_TOPICS ,
246
- requestVersion,
247
- context.clientId,
248
- context.correlationId)
249
- ForwardingManager .buildEnvelopeRequest(context,
250
- createTopicsRequest.build(requestVersion).serializeWithHeader(requestHeader))
251
- }.getOrElse(createTopicsRequest)
275
+ val request = (requestContext, requestHeaderForParsing) match {
276
+ case (Some (context), Some (requestHeader)) =>
277
+ ForwardingManager .buildEnvelopeRequest(context,
278
+ createTopicsRequest.build(requestHeader.apiVersion()).serializeWithHeader(requestHeader))
279
+ case _ =>
280
+ createTopicsRequest
281
+ }
252
282
253
283
channelManager.sendRequest(request, requestCompletionHandler)
254
284
@@ -364,6 +394,22 @@ class DefaultAutoTopicCreationManager(
364
394
.setTopics(topicsToCreate)
365
395
)
366
396
397
+ // Capture request header information for proper envelope response parsing
398
+ val requestHeaderForParsing = requestContext.map { context =>
399
+ val requestVersion =
400
+ channelManager.controllerApiVersions.toScala match {
401
+ case None =>
402
+ ApiKeys .CREATE_TOPICS .latestVersion()
403
+ case Some (nodeApiVersions) =>
404
+ nodeApiVersions.latestUsableVersion(ApiKeys .CREATE_TOPICS )
405
+ }
406
+
407
+ new RequestHeader (ApiKeys .CREATE_TOPICS ,
408
+ requestVersion,
409
+ context.clientId,
410
+ context.correlationId)
411
+ }
412
+
367
413
val requestCompletionHandler = new ControllerRequestCompletionHandler {
368
414
override def onTimeout (): Unit = {
369
415
clearInflightRequests(creatableTopics)
@@ -382,36 +428,52 @@ class DefaultAutoTopicCreationManager(
382
428
warn(s " Auto topic creation failed for ${creatableTopics.keys} with version mismatch exception: ${versionException.getMessage}" )
383
429
cacheTopicCreationErrors(creatableTopics.keys.toSet, versionException.getMessage, timeoutMs)
384
430
} else {
385
- response.responseBody() match {
386
- case createTopicsResponse : CreateTopicsResponse =>
387
- cacheTopicCreationErrorsFromResponse(createTopicsResponse, timeoutMs)
388
- case _ =>
389
- debug(s " Auto topic creation completed for ${creatableTopics.keys} with response ${response.responseBody}. " )
431
+ if (response.hasResponse) {
432
+ response.responseBody() match {
433
+ case envelopeResponse : EnvelopeResponse =>
434
+ // Unwrap the envelope response to get the actual CreateTopicsResponse
435
+ val envelopeError = envelopeResponse.error()
436
+ if (envelopeError != Errors .NONE ) {
437
+ warn(s " Auto topic creation failed for ${creatableTopics.keys} with envelope error: ${envelopeError}" )
438
+ cacheTopicCreationErrors(creatableTopics.keys.toSet, s " Envelope error: ${envelopeError}" , timeoutMs)
439
+ } else {
440
+ requestHeaderForParsing match {
441
+ case Some (requestHeader) =>
442
+ try {
443
+ // Use the captured request header for proper envelope response parsing
444
+ val createTopicsResponse = AbstractResponse .parseResponse(
445
+ envelopeResponse.responseData(), requestHeader).asInstanceOf [CreateTopicsResponse ]
446
+
447
+ cacheTopicCreationErrorsFromResponse(createTopicsResponse, timeoutMs)
448
+ } catch {
449
+ case e : Exception =>
450
+ warn(s " Failed to parse envelope response for auto topic creation of ${creatableTopics.keys}" , e)
451
+ cacheTopicCreationErrors(creatableTopics.keys.toSet, s " Response parsing error: ${e.getMessage}" , timeoutMs)
452
+ }
453
+ case None =>
454
+ warn(s " Cannot parse envelope response without original request header information " )
455
+ cacheTopicCreationErrors(creatableTopics.keys.toSet, " Missing request header for envelope parsing" , timeoutMs)
456
+ }
457
+ }
458
+ case createTopicsResponse : CreateTopicsResponse =>
459
+ cacheTopicCreationErrorsFromResponse(createTopicsResponse, timeoutMs)
460
+ case unexpectedResponse =>
461
+ warn(s " Auto topic creation request received unexpected response type: ${unexpectedResponse.getClass.getSimpleName}" )
462
+ cacheTopicCreationErrors(creatableTopics.keys.toSet, s " Unexpected response type: ${unexpectedResponse.getClass.getSimpleName}" , timeoutMs)
463
+ }
464
+ debug(s " Auto topic creation completed for ${creatableTopics.keys} with response ${response.responseBody}. " )
390
465
}
391
466
}
392
467
}
393
468
}
394
469
395
- val request = requestContext.map { context =>
396
- val requestVersion =
397
- channelManager.controllerApiVersions.toScala match {
398
- case None =>
399
- // We will rely on the Metadata request to be retried in the case
400
- // that the latest version is not usable by the controller.
401
- ApiKeys .CREATE_TOPICS .latestVersion()
402
- case Some (nodeApiVersions) =>
403
- nodeApiVersions.latestUsableVersion(ApiKeys .CREATE_TOPICS )
404
- }
405
-
406
- // Borrow client information such as client id and correlation id from the original request,
407
- // in order to correlate the create request with the original metadata request.
408
- val requestHeader = new RequestHeader (ApiKeys .CREATE_TOPICS ,
409
- requestVersion,
410
- context.clientId,
411
- context.correlationId)
412
- ForwardingManager .buildEnvelopeRequest(context,
413
- createTopicsRequest.build(requestVersion).serializeWithHeader(requestHeader))
414
- }.getOrElse(createTopicsRequest)
470
+ val request = (requestContext, requestHeaderForParsing) match {
471
+ case (Some (context), Some (requestHeader)) =>
472
+ ForwardingManager .buildEnvelopeRequest(context,
473
+ createTopicsRequest.build(requestHeader.apiVersion()).serializeWithHeader(requestHeader))
474
+ case _ =>
475
+ createTopicsRequest
476
+ }
415
477
416
478
channelManager.sendRequest(request, requestCompletionHandler)
417
479
0 commit comments