5
5
package io .airbyte .integrations .debezium .internals ;
6
6
7
7
import com .fasterxml .jackson .databind .JsonNode ;
8
- import com .google .common .annotations .VisibleForTesting ;
9
- import io .airbyte .db .jdbc .JdbcUtils ;
10
8
import io .airbyte .protocol .models .ConfiguredAirbyteCatalog ;
11
- import io .airbyte .protocol .models .ConfiguredAirbyteStream ;
12
- import io .airbyte .protocol .models .SyncMode ;
13
9
import io .debezium .engine .ChangeEvent ;
14
10
import io .debezium .engine .DebeziumEngine ;
15
11
import io .debezium .engine .format .Json ;
23
19
import java .util .concurrent .TimeUnit ;
24
20
import java .util .concurrent .atomic .AtomicBoolean ;
25
21
import java .util .concurrent .atomic .AtomicReference ;
26
- import java .util .stream .Collectors ;
27
- import org .codehaus .plexus .util .StringUtils ;
28
22
import org .slf4j .Logger ;
29
23
import org .slf4j .LoggerFactory ;
30
24
31
25
/**
32
- * The purpose of this class is to intiliaze and spawn the debezium engine with the right properties
33
- * to fetch records
26
+ * The purpose of this class is to initialize and spawn the debezium engine with the right
27
+ * properties to fetch records
34
28
*/
35
29
public class DebeziumRecordPublisher implements AutoCloseable {
36
30
37
31
private static final Logger LOGGER = LoggerFactory .getLogger (DebeziumRecordPublisher .class );
38
32
private final ExecutorService executor ;
39
33
private DebeziumEngine <ChangeEvent <String , String >> engine ;
40
-
41
- private final JsonNode config ;
42
- private final AirbyteFileOffsetBackingStore offsetManager ;
43
- private final Optional <AirbyteSchemaHistoryStorage > schemaHistoryManager ;
44
-
45
34
private final AtomicBoolean hasClosed ;
46
35
private final AtomicBoolean isClosing ;
47
36
private final AtomicReference <Throwable > thrownError ;
48
37
private final CountDownLatch engineLatch ;
49
- private final Properties properties ;
50
- private final ConfiguredAirbyteCatalog catalog ;
38
+ private final DebeziumPropertiesManager debeziumPropertiesManager ;
51
39
52
40
public DebeziumRecordPublisher (final Properties properties ,
53
41
final JsonNode config ,
54
42
final ConfiguredAirbyteCatalog catalog ,
55
43
final AirbyteFileOffsetBackingStore offsetManager ,
56
44
final Optional <AirbyteSchemaHistoryStorage > schemaHistoryManager ) {
57
- this .properties = properties ;
58
- this .config = config ;
59
- this .catalog = catalog ;
60
- this .offsetManager = offsetManager ;
61
- this .schemaHistoryManager = schemaHistoryManager ;
45
+ this .debeziumPropertiesManager = new DebeziumPropertiesManager (properties , config , catalog , offsetManager ,
46
+ schemaHistoryManager );
62
47
this .hasClosed = new AtomicBoolean (false );
63
48
this .isClosing = new AtomicBoolean (false );
64
49
this .thrownError = new AtomicReference <>();
@@ -68,7 +53,7 @@ public DebeziumRecordPublisher(final Properties properties,
68
53
69
54
public void start (final Queue <ChangeEvent <String , String >> queue ) {
70
55
engine = DebeziumEngine .create (Json .class )
71
- .using (getDebeziumProperties ())
56
+ .using (debeziumPropertiesManager . getDebeziumProperties ())
72
57
.using (new OffsetCommitPolicy .AlwaysCommitOffsetPolicy ())
73
58
.notifying (e -> {
74
59
// debezium outputs a tombstone event that has a value of null. this is an artifact of how it
@@ -120,69 +105,4 @@ public void close() throws Exception {
120
105
}
121
106
}
122
107
123
- protected Properties getDebeziumProperties () {
124
- final Properties props = new Properties ();
125
- props .putAll (properties );
126
-
127
- // debezium engine configuration
128
- props .setProperty ("name" , "engine" );
129
- props .setProperty ("offset.storage" , "org.apache.kafka.connect.storage.FileOffsetBackingStore" );
130
- props .setProperty ("offset.storage.file.filename" , offsetManager .getOffsetFilePath ().toString ());
131
- props .setProperty ("offset.flush.interval.ms" , "1000" ); // todo: make this longer
132
- // default values from debezium CommonConnectorConfig
133
- props .setProperty ("max.batch.size" , "2048" );
134
- props .setProperty ("max.queue.size" , "8192" );
135
-
136
- if (schemaHistoryManager .isPresent ()) {
137
- // https://debezium.io/documentation/reference/1.9/operations/debezium-server.html#debezium-source-database-history-class
138
- // https://debezium.io/documentation/reference/development/engine.html#_in_the_code
139
- // As mentioned in the documents above, debezium connector for MySQL needs to track the schema
140
- // changes. If we don't do this, we can't fetch records for the table.
141
- props .setProperty ("database.history" , "io.debezium.relational.history.FileDatabaseHistory" );
142
- props .setProperty ("database.history.file.filename" , schemaHistoryManager .get ().getPath ().toString ());
143
- }
144
-
145
- // https://debezium.io/documentation/reference/configuration/avro.html
146
- props .setProperty ("key.converter.schemas.enable" , "false" );
147
- props .setProperty ("value.converter.schemas.enable" , "false" );
148
-
149
- // debezium names
150
- props .setProperty ("name" , config .get (JdbcUtils .DATABASE_KEY ).asText ());
151
- props .setProperty ("database.server.name" , config .get (JdbcUtils .DATABASE_KEY ).asText ());
152
-
153
- // db connection configuration
154
- props .setProperty ("database.hostname" , config .get (JdbcUtils .HOST_KEY ).asText ());
155
- props .setProperty ("database.port" , config .get (JdbcUtils .PORT_KEY ).asText ());
156
- props .setProperty ("database.user" , config .get (JdbcUtils .USERNAME_KEY ).asText ());
157
- props .setProperty ("database.dbname" , config .get (JdbcUtils .DATABASE_KEY ).asText ());
158
-
159
- if (config .has (JdbcUtils .PASSWORD_KEY )) {
160
- props .setProperty ("database.password" , config .get (JdbcUtils .PASSWORD_KEY ).asText ());
161
- }
162
-
163
- // By default "decimal.handing.mode=precise" which's caused returning this value as a binary.
164
- // The "double" type may cause a loss of precision, so set Debezium's config to store it as a String
165
- // explicitly in its Kafka messages for more details see:
166
- // https://debezium.io/documentation/reference/1.9/connectors/postgresql.html#postgresql-decimal-types
167
- // https://debezium.io/documentation/faq/#how_to_retrieve_decimal_field_from_binary_representation
168
- props .setProperty ("decimal.handling.mode" , "string" );
169
-
170
- // table selection
171
- final String tableWhitelist = getTableWhitelist (catalog );
172
- props .setProperty ("table.include.list" , tableWhitelist );
173
-
174
- return props ;
175
- }
176
-
177
- @ VisibleForTesting
178
- public static String getTableWhitelist (final ConfiguredAirbyteCatalog catalog ) {
179
- return catalog .getStreams ().stream ()
180
- .filter (s -> s .getSyncMode () == SyncMode .INCREMENTAL )
181
- .map (ConfiguredAirbyteStream ::getStream )
182
- .map (stream -> stream .getNamespace () + "." + stream .getName ())
183
- // debezium needs commas escaped to split properly
184
- .map (x -> StringUtils .escape (x , new char [] {',' }, "\\ ," ))
185
- .collect (Collectors .joining ("," ));
186
- }
187
-
188
108
}
0 commit comments