GIP Debezium — MySQL and Mongo bootstrap
How zwing-connect apps/debezium-connector provisions Kafka Connect for GIP MySQL (Debezium) and GIP Mongo (Mongo source), with shared config in libs/dip-v2
Scope
This page documents GIP-specific Debezium / Kafka Connect flows implemented in apps/debezium-connector/ in the zwing-connect monorepo:
- GIP MySQL — one bootstrap MySQL connector built from DIP outbound events + vendor database metadata, deployed via PUT to Kafka Connect.
- GIP Mongo — one MongoDB source connector per requested database, deployed via POST to Kafka Connect.
Other tasks in the same app (for example CreateDebeziumConnectorTask and the OutboundConnector debezium-connectors/outbound path) build non-GIP “ZWING” bulk outbound connectors with different topic routing (zwing.connect.outbound.$2). Those are out of scope here except where noted for name comparison.
Shared MySQL connector config for GIP is centralized in @libs/dip-v2: GipMysqlDebeziumConfigService (libs/dip-v2/src/services/gip-mysql-debezium-config.ts).
HTTP entry points and auth
Base path: controllers are registered on the app; the run commands live in apps/debezium-connector/src/http/controllers/run-command.ts.
| GET path | Handler |
|---|---|
/run-command/create-gip-mysql-connector | GipService.createGipMysqlConnector() |
/run-command/create-gip-mongo-connector.<suffix> | Suffix: URL-encoded comma-separated Mongo database names → createGipMongoConnectors(databases) |
Authentication: ApiGuard requires header x-api-secret to equal services.apiSecret from config. Missing or wrong secret returns a GenericException.
Kafka Connect base URL: services.debezium.host (used to build new URL('connectors', dbzHost) and similar). GIP MySQL bootstrap uses PUT .../connectors/{name}/config inside GipConnectorTask’s putConnectorConfig; GIP Mongo uses DebeziumService.create which POSTs to {dbzHost}/connectors (trailing path depends on dbzHost value — see implementation).
GIP MySQL: createGipMysqlConnector
File: apps/debezium-connector/src/services/gip.ts.
1. Discover outbound events
dipV2Service.eventsRepo.findMappedOutboundEvents() returns outbound events that:
- Have a non-empty
listenerConfig.kafkaTopicName, and - Appear in at least one connector–event mapping (
$lookuponconnector_event_mappings).
Convention: for MySQL GIP, kafkaTopicName is treated as a MySQL table name in the vendor’s database (not the literal Kafka topic name the outbound listener might use in other layers).
2. Resolve vendors (master MySQL)
GipConnectorTask.fetchVendorsByEnterpriseIds loads vendor rows: db_name, client_id, db_config_id keyed by sso_enterprise_id. Enterprises with events but no vendor are skipped (logged as errors in gip.ts).
3. Build include lists
- For each qualifying event, resolve
vendor.dbNameand add{dbName}.{tableName}to the table set wheretableName === kafkaTopicName(after trimming / Mongo check below). isMongoDBCollection: ifkafkaTopicName(case-insensitive) iscustomers, that “table” is excluded from the MySQL GIP connector (CRM Mongo is handled separately; see GIP Mongo).
If no table entries remain after resolution, the API throws: no MySQL GIP connector can be created.
4. Anchor db_config and createBootstrapConnector
The code picks an anchor vendor (first resolvable) and loads credentials via fetchDbConfig from the db_config table. One bootstrap connector is created that includes all resolved databases and tables in the merged lists. That implies a shared host/credentials model: every included tenant’s DB is expected to be reachable on that same MySQL endpoint (as stored for the anchor), which is a deployment constraint operators must validate.
GipConnectorTask.createBootstrapConnector (gip-connector-task.ts):
- Generates a unique connector name
gip-bootstrap-{shortUlid}. - Random MySQL
database.server.id, adatabaseServerName, and a uniqueschema.history.internal.kafka.topic(schema history topic per bootstrap). - Merges
GipMysqlDebeziumConfigService.buildBaseConfigwith:database.include.list(comma-separated DB names)table.include.list(comma-separateddb.tablepairs)topic.prefix=app.env
- Sends configuration with PUT to Kafka Connect:
connectors/{connectorName}/config.
5. Base Debezium MySQL config (GipMysqlDebeziumConfigService)
File: libs/dip-v2/src/services/gip-mysql-debezium-config.ts
Notable keys in buildBaseConfig (excerpted conceptually; full map is in the source file):
connector.class:io.debezium.connector.mysql.MySqlConnectorsnapshot.mode:schema_only— no full data snapshot, schema capture onlyinclude.schema.changes:falsetransforms:routeToDbTopic,filterwith a Groovy Filter SMT and RegexRouterrouteToDbTopic— topic rewritezw.out.$2(DB segment dropped, table name in position$2of the regex)- Schema history:
schema.history.internal.kafka.bootstrap.serversfromKAFKA_BROKERS, topic from identity or default
Topic naming difference: the older OutboundConnector payload in debezium-connectors/outbound.ts uses transforms.routeToDbTopic.replacement zwing.connect.outbound.$2. GIP bootstrap uses zw.out.$2. When debugging Kafka topics, use the connector that actually ran (GIP bootstrap name prefix gip-bootstrap- vs ZWING_CONNECT_OUTBOUND_CONNECTOR_).
6. Failure cases
createGipMysqlConnector throws (400/500) when:
- No mapped outbound events with
kafkaTopicNameexist - No vendor can be resolved for any enterprise
- No MySQL tables can be built after filtering
services.debezium.hostis missing (inGipConnectorTaskbefore PUT)
GIP Mongo: createGipMongoConnectors
File: apps/debezium-connector/src/services/gip.ts
- Requires
mongo.crm.uri(or env alias documented in your deployment; code readsmongo.crm.uriand mentionsCRM_MONGO_URIin the error message if unset). - For each database name in the command suffix:
- Connector name:
gip-mongo-{slug}-{ulid}(slug from sanitizeddbName). connector.classcom.mongodb.kafka.connect.MongoSourceConnectorbuildConfig:connection.uri,database,collection: customers,topic.prefix: crm.out,output.format.value/key: json, change stream / full document settings, and a RegexRouter that normalizes tocrm.out.$1for topics.
- Connector name:
Creation uses DebeziumService.create (HTTP POST .../connectors with body { name, config }).
Incremental MySQL GIP table patch (ACS, different connector name)
The debezium-connector app is not the only place GIP MySQL is updated. The App Connector Service task DebeziumConnectorPatchTask (apps/app-connector-service/src/tasks/debezium-connector-patch.ts):
- Targets a fixed connector name
GIP-connector-v001 - Uses the same
GipMysqlDebeziumConfigServicemergeTableIntoConfigandneedsRefreshto append a newdb.tablewhen a new mapped table is added for a tenant - Skips the
customers“table” for the same reason as the bootstrap (Mongo)
Operators should be aware: bootstrap may create gip-bootstrap-*, while runtime patches from ACS may update GIP-connector-v001. Environments can differ; confirm in Kafka Connect which connector is authoritative.
Environment and config checklist
| Key | Used for |
|---|---|
services.debezium.host | Kafka Connect base URL (GIP MySQL PUT, GIP Mongo POST) |
KAFKA_BROKERS | Debezium MySQL schema history (buildBaseConfig) |
app.env | GIP MySQL topic.prefix in bootstrap config |
mongo.crm.uri | GIP Mongo connection.uri in buildConfig |
MYSQL_SERVER_ID (optional) | Default server id in buildBaseConfig if identity omitted |
Flow (mermaid)
flowchart LR
subgraph dip [DipV2]
Ev[outbound event kafkaTopicName]
Map[connector event mapping]
end
subgraph gip [GipService in debezium-connector]
V[Vendor and db_config from MySQL]
T[Build database and table include lists]
C[GipConnectorTask createBootstrapConnector]
end
subgraph kc [Kafka Connect]
MyC[Debezium MySQL gip-bootstrap]
MoC[Mongo source gip-mongo]
end
Ev --> T
Map --> T
V --> T
T --> C
C -->|PUT config| MyC
Mgo[createGipMongoConnectors] -->|POST| MoC
MyC --> KTopics[zw.out topics]
MoC --> Crm[crm.out topics]Related
- Data model reference —
listenerConfig, events - Kafka — environments and topic naming
- GIP blackout window — separate concern from CDC provisioning