Skip to content

feat: error handling in sink connector#142

Merged
Emrehzl94 merged 15 commits intoneo4j:mainfrom
ali-ince:error-handling
Jun 28, 2024
Merged

feat: error handling in sink connector#142
Emrehzl94 merged 15 commits intoneo4j:mainfrom
ali-ince:error-handling

Conversation

@Emrehzl94
Copy link
Contributor

This pr includes error handling mechanism for sink connector and its integration tests. With this pr, now we are able to send error information to a configured DLQ for each sink message.

@Emrehzl94 Emrehzl94 self-assigned this Jun 20, 2024
@Emrehzl94 Emrehzl94 marked this pull request as ready for review June 20, 2024 12:37
@Emrehzl94 Emrehzl94 requested a review from a team as a code owner June 20, 2024 12:37
Copy link
Contributor

@ali-ince ali-ince left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Left a couple of minor comments.

I think test names can be slightly rephrased in general.

}

override fun transformUpdate(event: NodeEvent): Query {
if (event.before == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we also have a similar validation for create and delete events, maybe in a follow-up PR. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I think we should, since they can also fail if before or after is null

errorDlqTopic = DLQ_TOPIC,
errorDlqContextHeadersEnable = true)
@Test
fun `should report errors when cypher strategy with multiple events`(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the test name requires a bit of rephrasing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any suggestions? maybe should report failed events with cypher strategy ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, that sounds better :)
bad news is that, most of the test cases in this class requires similar renaming :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no worries :) not a big effort to change them :)

errorHeaders.getValue(ErrorHeaders.EXCEPTION_CLASS_NAME) shouldBe
"org.neo4j.driver.exceptions.ClientException"
errorHeaders.getValue(ErrorHeaders.EXCEPTION_MESSAGE) shouldBe
"Unable to convert kotlin.Unit to Neo4j Value."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we have a better error message displayed here, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so you mean we should prevent this error with some validation even before sending the query to the driver, if I'm not mistaken?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, what I mean is that we should handle this error way before we send the query to Neo4j - it will still fail but with a clear error message about what's wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it 👍

errorHeaders.getValue(ErrorHeaders.EXCEPTION_CLASS_NAME) shouldBe
"org.neo4j.driver.exceptions.ClientException"
errorHeaders.getValue(ErrorHeaders.EXCEPTION_MESSAGE) shouldBe
"Unable to convert kotlin.Unit to Neo4j Value."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

TopicVerifier.create<String, String>(consumer)
.assertMessageValue { it shouldBe message2ToFail.value }
.verifyWithin(Duration.ofSeconds(30))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe for a future improvement but we could also verify that the connector instance is in FAILED state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should I create a card for this?

@neo4j-connectors
Copy link
Collaborator

neo4j-connectors commented Jun 26, 2024

Warnings
⚠️ Commit Message 'refactor: implement requested changes.': subject may not end with full stop

Generated by 🚫 dangerJS against 65300e8

# Conflicts:
#	sink/src/main/kotlin/org/neo4j/connectors/kafka/sink/strategy/RedirectingHandler.kt
@Test
fun `should split changes into transactional boundaries`() {
val handler = CdcSchemaHandler("my-topic", Renderer.getRenderer(Configuration.defaultConfig()))
val handler =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice spot 😉

Copy link
Contributor

@ali-ince ali-ince left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Let's wait for @fbiville's approval before merge though.

@Test
fun `should report an error with all error headers when headers are enabled`(
@TopicProducer(TOPIC) producer: ConvertingKafkaProducer,
@TopicConsumer(topic = DLQ_TOPIC, offset = "earliest") consumer: ConvertingKafkaConsumer,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@TopicConsumer(topic = DLQ_TOPIC, offset = "earliest") consumer: ConvertingKafkaConsumer,
@TopicConsumer(topic = DLQ_TOPIC, offset = "earliest") errorConsumer: ConvertingKafkaConsumer,

) {
session.run("CREATE CONSTRAINT FOR (n:Person) REQUIRE n.id IS KEY").consume()

val schema =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
val schema =
val schemaWithMissingSurname =

"org.neo4j.driver.exceptions.ClientException"
errorHeaders.getValue(ErrorHeaders.EXCEPTION_MESSAGE) shouldBe
"""Cannot merge the following node because of null property value for 'surname': (:Person {surname: null})"""
errorHeaders.getValue(ErrorHeaders.EXCEPTION_STACKTRACE) shouldNotBe null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean why we only check it's not null?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering why we don't get a stack trace

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're getting it, just it's gonna be too many lines in the test to put the all stack trace here and check they are equal, this is why I didn't put it and only check it's not null, maybe instead of all stack trace I can put shouldContain check

}

@Neo4jSink(
cud = [CudStrategy(TOPIC)], errorDlqTopic = DLQ_TOPIC, errorDlqContextHeadersEnable = true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it's just me but I have a hard time parsing errorDlqContextHeadersEnable 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any suggestions for renaming? Yes, I know it's hard to grab at first glance but I just followed same pattern with actual configuration name errors.deadletterqueue.context.headers.enable 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about enableErrorHeaders ?

handler.handle(listOf(nodeChangeEventMessage))
} shouldHaveMessage "create operation requires 'after' field in the event object"

val relationshipChangeEventMessage =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd personally move this to a separate test

handler.handle(listOf(nodeChangeEventMessage))
} shouldHaveMessage "update operation requires 'after' field in the event object"

val relationshipChangeEventMessage =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above

handler.handle(listOf(nodeChangeEventMessage))
} shouldHaveMessage "create operation requires 'after' field in the event object"

val relationshipChangeEventMessage =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above

handler.handle(listOf(nodeChangeEventMessage))
} shouldHaveMessage "update operation requires 'before' field in the event object"

val relationshipChangeEventMessage =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

handler.handle(listOf(nodeChangeEventMessage))
} shouldHaveMessage "update operation requires 'after' field in the event object"

val relationshipChangeEventMessage =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Contributor

@ali-ince ali-ince left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I think this is now ready to merged 👍

@Emrehzl94 Emrehzl94 merged commit d6df239 into neo4j:main Jun 28, 2024
@ali-ince ali-ince deleted the error-handling branch July 2, 2024 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants