Skip to content

debezium/dbz#285 add csv connector example to debezium-examples#407

Open
yuanglili wants to merge 2 commits into
debezium:mainfrom
yuanglili:dbz-1771-csv-connector
Open

debezium/dbz#285 add csv connector example to debezium-examples#407
yuanglili wants to merge 2 commits into
debezium:mainfrom
yuanglili:dbz-1771-csv-connector

Conversation

@yuanglili

@yuanglili yuanglili commented Apr 23, 2026

Copy link
Copy Markdown

Description

This PR adds a CSV file-based connector example to debezium-examples

It demonstrates the key concepts of building a Debezium connector including:

  • snapshot/streaming handover,
  • offset management
  • schema evolution
  • value type conversion

Checklist

  • If the changes include a new example, I added it to the list of examples in the README.md file

Signed-off-by: nonononoonononon <yuangli971@gmail.com>
Signed-off-by: nonononoonononon <yuangli971@gmail.com>
@yuanglili

Copy link
Copy Markdown
Author

@kmos, I created an example based on your debezium-connector-csv and it works as expected.

I'm also thinking about whether it would be helpful to add diagrams showing the relationships between the key components, or a simple UI for performing insert/update/delete operations to make it more intuitive.

Let me know if there's anything that needs to be changed!

@kmos

kmos commented Apr 23, 2026

Copy link
Copy Markdown
Member

@yuanglili I’m a big supporter of diagrams and visualization. I totally agree.

@Naros Naros left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi @yuanglili, great work. I've left some inline comments to bring the PR more in line with how most Debezium connectors are structured, for those who may use this as a skeleton for future connectors.

Btw, I noticed the PR uses Debezium 3.0.8, which is very old. Could we move this up to Debezium 3.5 by any chance?


@Override
public String getContextName() {
return "CSV";

@Naros Naros May 6, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's add a Module class like other connectors and replace the hard-coded values here for context and connector to refer to the Module class.

}

@Override
protected SourceInfoStructMaker<?> getSourceInfoStructMaker(Version version) {

@Naros Naros May 6, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a reason for not delegating CommonConnectorConfig#getSourceInfoStructMaker like other connectors? If there is, we should document the reason.


@Override
public String version() {
return "1.0";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This can also delegate to Module

* {@link #doStop} is a no-op;
* the coordinator manages its own shutdown.
*/
public class CsvConnectorTask extends BaseSourceTask<CsvPartition, CsvOffsetContext> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I believe we may be missing overrides for getConnectorName and getErrorHandler?

import io.debezium.connector.base.ChangeEventQueue;
import io.debezium.pipeline.ErrorHandler;

public class CsvErrorHandler extends ErrorHandler {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We probably should include some class-level Java docs.

Comment on lines +191 to +204
Schema keySchema = collectionSchema.keySchema();
Schema valueSchema = collectionSchema.getEnvelopeSchema().schema()
.field("after").schema();

ColumnDef keyCol = cols.get(0);
Struct keyStruct = new Struct(keySchema)
.put(keyCol.name(), convertValue(parts[1], keyCol.type()));

Struct valueStruct = new Struct(valueSchema);
for (int i = 0; i < cols.size(); i++) {
ColumnDef col = cols.get(i);
String rawValue = (i + 1 < parts.length) ? parts[i + 1] : null;
valueStruct.put(col.name(), rawValue == null ? null : convertValue(rawValue, col.type()));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we move this logic into the emitter class? In practice, we often centralize this in the emitter because it's shared logic that you'll need for snapshot and streaming, and you reuse the emitter in both contexts.

* handover line. The fine-grained snapshot/skip decision is made in
* {@link CsvSnapshotChangeEventSource#getSnapshottingTask} via the line-number offset.
*/
class CsvSnapshotter implements Snapshotter {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Any reason we could not just use the existing InitialSnapshotter from debezium-connector-common?


@Override
public String version() {
return "1.0";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Another Module.version() reference opportunity.

Comment on lines +81 to +84
@Override
public <T extends DataCollectionId> List<T> getMatchingCollections(Configuration configuration) {
return List.of();
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I believe this has since been removed, depending on the version of Debezium this is based.

Comment on lines +190 to +206
org.apache.kafka.connect.data.Schema keySchema = collectionSchema.keySchema();
org.apache.kafka.connect.data.Schema valueSchema = collectionSchema.getEnvelopeSchema().schema()
.field("after").schema();

ColumnDef keyCol = cols.get(0);
org.apache.kafka.connect.data.Struct keyStruct =
new org.apache.kafka.connect.data.Struct(keySchema)
.put(keyCol.name(), CsvSnapshotChangeEventSource.convertValue(parts[1], keyCol.type()));

org.apache.kafka.connect.data.Struct valueStruct =
new org.apache.kafka.connect.data.Struct(valueSchema);
for (int i = 0; i < cols.size(); i++) {
ColumnDef col = cols.get(i);
String rawValue = (i + 1 < parts.length) ? parts[i + 1] : null;
valueStruct.put(col.name(),
rawValue == null ? null : CsvSnapshotChangeEventSource.convertValue(rawValue, col.type()));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is the shared logic I mentioned that we can move into the emitter.

@jpechane

jpechane commented May 7, 2026

Copy link
Copy Markdown
Contributor

I wonder if it would be possible to extend Relation to the Debezium Framework section signficantly to make sure that every single class necessary to implement the connector (I mean generic connector) is included and short description is added? That would be great start for a follow-up blogpost explaining the details and future conversion into formal connector developer giude.

@yuanglili

Copy link
Copy Markdown
Author

Hi @yuanglili, great work. I've left some inline comments to bring the PR more in line with how most Debezium connectors are structured, for those who may use this as a skeleton for future connectors.

Btw, I noticed the PR uses Debezium 3.0.8, which is very old. Could we move this up to Debezium 3.5 by any chance?

Sure, I'll switch it to Debezium 3.5

@yuanglili

Copy link
Copy Markdown
Author

I wonder if it would be possible to extend Relation to the Debezium Framework section signficantly to make sure that every single class necessary to implement the connector (I mean generic connector) is included and short description is added? That would be great start for a follow-up blogpost explaining the details and future conversion into formal connector developer giude.

I totally agree. I’m working on a more detailed version of the section right now. I’ll first address the current review comments and then submit an updated version.

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