Skip to content

Commit 5583d0a

Browse files
committed
modernize demos
1 parent 99bc567 commit 5583d0a

File tree

11 files changed

+41
-42
lines changed

11 files changed

+41
-42
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
FROM node:14
1+
FROM node:22-slim
22
ARG NODE_ENV
33
ENV NODE_ENV $NODE_ENV
44

55
WORKDIR /usr/src/app
66
COPY package*.json ./
77

8-
RUN npm install --only=production
8+
RUN npm install --omit=dev
99

1010
COPY . .
1111
CMD ["node", "index.js"]
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
FROM ubuntu:latest AS builder
1+
FROM ubuntu:24.04 AS builder
22
WORKDIR /opt
33
RUN apt-get update && apt-get install -y curl wget unzip jq
44
RUN curl -s https://api.github.com/repos/questdb/kafka-questdb-connector/releases/latest | jq -r '.assets[]|select(.content_type == "application/zip")|.browser_download_url'|wget -qi -
55
RUN unzip kafka-questdb-connector-*-bin.zip
66

7-
FROM confluentinc/cp-kafka-connect:7.7.0
7+
FROM confluentinc/cp-kafka-connect:7.8.0
88
COPY --from=builder /opt/kafka-questdb-connector/*.jar /usr/share/java/kafka/

kafka-questdb-connector-samples/faker/docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
services:
22
questdb:
3-
image: questdb/questdb:8.1.0
3+
image: questdb/questdb:9.3.2
44
expose:
55
- "9000"
66
ports:
77
- "19000:9000"
88
environment:
99
- QDB_LINE_DEFAULT_PARTITION_BY=YEAR
1010
kafka:
11-
image: apache/kafka:latest
11+
image: apache/kafka:3.8.1
1212
container_name: broker
1313
environment:
1414
KAFKA_NODE_ID: 1

kafka-questdb-connector-samples/faker/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ async function ingest() {
1818
let batch = [];
1919
for (let j = 0; j < 100; j++) {
2020
const data = {
21-
firstname: faker.name.firstName(),
22-
lastname: faker.name.lastName(),
23-
birthday: faker.date.birthdate({min: year, max: year}),
21+
firstname: faker.person.firstName(),
22+
lastname: faker.person.lastName(),
23+
birthday: faker.date.birthdate({min: year, max: year, mode: 'year'}),
2424
};
2525
batch.push({value: JSON.stringify(data)})
2626
}

kafka-questdb-connector-samples/faker/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"author": "",
1111
"license": "Apache-2.0",
1212
"dependencies": {
13-
"@faker-js/faker": "^7.5.0",
14-
"kafkajs": "^2.2.0"
13+
"@faker-js/faker": "^9.5.1",
14+
"kafkajs": "^2.2.4"
1515
}
1616
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
FROM maven:3.8-jdk-11-slim AS builder
1+
FROM maven:3.9-eclipse-temurin-17 AS builder
22
COPY ./pom.xml /opt/stocks/pom.xml
33
COPY ./src ./opt/stocks/src
44
WORKDIR /opt/stocks
55
RUN mvn clean install -DskipTests
66

7-
FROM azul/zulu-openjdk:11-latest
7+
FROM eclipse-temurin:17-jre
88
COPY --from=builder /opt/stocks/target/kafka-samples-stocks-*.jar /stocks.jar
99
CMD ["java", "-jar", "/stocks.jar"]
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
FROM ubuntu:latest AS builder
1+
FROM ubuntu:24.04 AS builder
22
WORKDIR /opt
33
RUN apt-get update && apt-get install -y curl wget unzip jq
44
RUN curl -s https://api.github.com/repos/questdb/kafka-questdb-connector/releases/latest | jq -r '.assets[]|select(.content_type == "application/zip")|.browser_download_url'|wget -qi -
55
RUN unzip kafka-questdb-connector-*-bin.zip
66

7-
FROM debezium/connect:1.9.6.Final
7+
FROM debezium/connect:2.7.3.Final
88
COPY --from=builder /opt/kafka-questdb-connector/*.jar /kafka/connect/questdb-connector/

kafka-questdb-connector-samples/stocks/docker-compose.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
services:
22
questdb:
3-
image: questdb/questdb:8.1.0
3+
image: questdb/questdb:9.3.2
44
expose:
55
- "9000"
66
ports:
@@ -9,7 +9,7 @@ services:
99
- QDB_LINE_DEFAULT_PARTITION_BY=YEAR
1010
- QDB_PG_SELECT_CACHE_ENABLED=false
1111
grafana:
12-
image: grafana/grafana-oss:9.2.1
12+
image: grafana/grafana-oss:11.4.0
1313
ports:
1414
- "3000:3000"
1515
volumes:
@@ -29,7 +29,7 @@ services:
2929
- GF_AUTH_ANONYMOUS_ORG_NAME=Main Org.
3030
- GF_AUTH_ANONYMOUS_ORG_ID=1
3131
kafka:
32-
image: apache/kafka:latest
32+
image: apache/kafka:3.8.1
3333
container_name: broker
3434
environment:
3535
KAFKA_NODE_ID: 1
@@ -45,7 +45,7 @@ services:
4545
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
4646
KAFKA_NUM_PARTITIONS: 3
4747
postgres:
48-
image: debezium/postgres:14-alpine
48+
image: debezium/postgres:16-alpine
4949
ports:
5050
- "5432:5432"
5151
environment:

kafka-questdb-connector-samples/stocks/pom.xml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99

1010
<properties>
1111
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
12-
<maven.compiler.source>8</maven.compiler.source>
13-
<maven.compiler.target>8</maven.compiler.target>
14-
<spring.boot.version>2.7.4</spring.boot.version>
15-
<testcontainers.version>1.17.3</testcontainers.version>
12+
<maven.compiler.source>17</maven.compiler.source>
13+
<maven.compiler.target>17</maven.compiler.target>
14+
<spring.boot.version>3.3.7</spring.boot.version>
15+
<testcontainers.version>1.20.4</testcontainers.version>
1616

1717
</properties>
1818

@@ -33,7 +33,6 @@
3333
<dependency>
3434
<groupId>org.springframework</groupId>
3535
<artifactId>spring-test</artifactId>
36-
<version>5.3.23</version>
3736
<scope>test</scope>
3837
</dependency>
3938
<dependency>
@@ -52,7 +51,7 @@
5251
<dependency>
5352
<groupId>org.postgresql</groupId>
5453
<artifactId>postgresql</artifactId>
55-
<version>42.5.0</version>
54+
<version>42.7.4</version>
5655
</dependency>
5756
<dependency>
5857
<groupId>org.testcontainers</groupId>

kafka-questdb-connector-samples/stocks/readme.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Sample Project: Feeding changes from Postgres to QuestDB
22
## What does this sample do?
3-
This sample project demonstrates how to feed changes from a Postgres table to QuestDB. It uses the [Debezium Postgres connector](https://debezium.io/documentation/reference/1.9/connectors/postgresql.html) to capture changes from a [Postgres database](https://www.postgresql.org/) and feed them to a [Kafka](https://kafka.apache.org/) topic. The [Kafka QuestDB connector](https://github.com/questdb/kafka-questdb-connector) then reads from the Kafka topic and writes the changes to a [QuestDB](questdb.io/) table. QuestDB is used for analytical queries on data and to feed the data to a Grafana dashboard for visualization.
3+
This sample project demonstrates how to feed changes from a Postgres table to QuestDB. It uses the [Debezium Postgres connector](https://debezium.io/documentation/reference/stable/connectors/postgresql.html) to capture changes from a [Postgres database](https://www.postgresql.org/) and feed them to a [Kafka](https://kafka.apache.org/) topic. The [Kafka QuestDB connector](https://github.com/questdb/kafka-questdb-connector) then reads from the Kafka topic and writes the changes to a [QuestDB](questdb.io/) table. QuestDB is used for analytical queries on data and to feed the data to a Grafana dashboard for visualization.
44

55
The project can be seen as a reference architecture for a data pipeline that feeds changes from a Postgres database to QuestDB. Postgres is an excellent [transaction/OLTP](https://en.wikipedia.org/wiki/Online_transaction_processing) database. It excels with simple short-running queries. Hence, the `stock` table contains only the most recent snapshot of the data. It stores no history at all.
66

@@ -18,13 +18,13 @@ Bear in mind the sample starts multiple containers. It's running fine on my mach
1818
## Running the sample
1919
1. Clone this repository via `git clone https://github.com/questdb/kafka-questdb-connector.git`
2020
2. `cd kafka-questdb-connector/kafka-questdb-connector-samples/stocks/` to enter the directory with this sample.
21-
3. Run `docker-compose build` to build docker images with the sample project. This will take a few minutes.
22-
4. Run `docker-compose up` to start Postgres, Java stock price updater app, Apache Kafka, Kafka Connect with Debezium and QuestDB connectors, QuestDB and Grafana. This will take a few minutes.
21+
3. Run `docker compose build` to build docker images with the sample project. This will take a few minutes.
22+
4. Run `docker compose up` to start Postgres, Java stock price updater app, Apache Kafka, Kafka Connect with Debezium and QuestDB connectors, QuestDB and Grafana. This will take a few minutes.
2323
5. The previous command will generate a lot of log messages. Eventually logging should cease. This means all containers are running.
2424
6. At this point we have all infrastructure running, the Java application keeps updating stock prices in Postgres. However, the rest of the pipeline is not yet running. We need to start the Kafka Connect connectors. Kafka Connect has a REST API, so we can use `curl` to start the connectors.
2525
7. In a separate shell, execute following command to start Debezium connector:
2626
```shell
27-
curl -X POST -H "Content-Type: application/json" -d '{"name":"debezium_source","config":{"tasks.max":1,"database.hostname":"postgres","database.port":5432,"database.user":"postgres","database.password":"postgres","connector.class":"io.debezium.connector.postgresql.PostgresConnector","database.dbname":"postgres","database.server.name":"dbserver1"}} ' localhost:8083/connectors
27+
curl -X POST -H "Content-Type: application/json" -d '{"name":"debezium_source","config":{"tasks.max":1,"database.hostname":"postgres","database.port":5432,"database.user":"postgres","database.password":"postgres","connector.class":"io.debezium.connector.postgresql.PostgresConnector","database.dbname":"postgres","topic.prefix":"dbserver1"}} ' localhost:8083/connectors
2828
```
2929
It starts the Debezium connector that will capture changes from Postgres and feed them to Kafka.
3030
8. Execute following command to start QuestDB Kafka Connect sink:
@@ -49,7 +49,7 @@ Bear in mind the sample starts multiple containers. It's running fine on my mach
4949
max(price)
5050
FROM stock
5151
where symbol = 'IBM'
52-
SAMPLE by 1m align to calendar;
52+
SAMPLE by 1m;
5353
```
5454
It returns the average, minimum and maximum stock price for IBM in each minute. You can change the `1m` to `1s` to get data aggregated by second. The `SAMPLE by` shows a bit of QuestDB syntax sugar to make time-related queries more readable.
5555
13. Don't forget to stop the containers when you're done. The project generates a lot of data and you could run out of disk space.
@@ -64,15 +64,15 @@ If you like what you see and want to learn more about the internals of the proje
6464
6. Grafana
6565
6666
### Postgres
67-
The docker-compose start Postgres image. It's using a container image provided by the Debezium project as they maintain a Postgres which is preconfigured for Debezium.
67+
The docker compose start Postgres image. It's using a container image provided by the Debezium project as they maintain a Postgres which is preconfigured for Debezium.
6868
6969
### Java stock price updater
7070
It's a Spring Boot application which during startup creates a table in Postgres and populates it with initial data.
7171
You can see the SQL executed in the [schema.sql](src/main/resources/schema.sql) file. The table has always one row per each stock symbol.
7272
7373
Once the application is started, it starts updating stock prices in regular intervals. The `price` and `last_update` columns are updated every time a new price is received for the stock symbol. It mimics a real-world scenario where you would have a Postgres table with the latest prices for each stock symbol. Such table would be typically used by a transactional system to get the latest prices for each stock symbol. It our case the transactional system is simulated by a [simple Java application](src/main/java/io/questdb/kafka/samples/StockService.java) which is randomly updating prices for each stock symbol in the Postgres table. The application generates 1000s of updates each second.
7474
75-
The application is built and packaged as a container image when executing `docker-compose build`. Inside the [docker-compose file](docker-compose.yml) you can see the container called `producer`. That's our Java application.
75+
The application is built and packaged as a container image when executing `docker compose build`. Inside the [docker-compose file](docker-compose.yml) you can see the container called `producer`. That's our Java application.
7676
```Dockerfile
7777
producer:
7878
image: kafka-questdb-connector-samples-stocks-generator
@@ -87,26 +87,26 @@ The application is built and packaged as a container image when executing `docke
8787
```
8888
The Dockefile is rather trivial:
8989
```Dockerfile
90-
FROM maven:3.8-jdk-11-slim AS builder
90+
FROM maven:3.9-eclipse-temurin-17 AS builder
9191
COPY ./pom.xml /opt/stocks/pom.xml
9292
COPY ./src ./opt/stocks/src
9393
WORKDIR /opt/stocks
9494
RUN mvn clean install -DskipTests
9595
96-
FROM azul/zulu-openjdk:11-latest
96+
FROM eclipse-temurin:17-jre
9797
COPY --from=builder /opt/stocks/target/kafka-samples-stocks-*.jar /stocks.jar
9898
CMD ["java", "-jar", "/stocks.jar"]
9999
```
100-
It uses Maven to build the application and then copies the resulting JAR file to the container image. The container image is based on Zulu OpenJDK 11. The application is started with `java -jar /stocks.jar` command.
100+
It uses Maven to build the application and then copies the resulting JAR file to the container image. The container image is based on Eclipse Temurin JDK 17. The application is started with `java -jar /stocks.jar` command.
101101
102102
### Debezium Postgres connector
103103
Debezium is an open source project which provides connectors for various databases. It is used to capture changes from a database and feed them to a Kafka topic. In other words: Whenever there is a change in a database table, Debezium will read the change and feed it to a Kafka topic. This way it translates operations such as INSERT or UPDATE into events which can be consumed by other systems. Debezium supports a wide range of databases. In this sample we use the Postgres connector.
104104
105-
The Debezium Postgres connector is implemented as a Kafka Connect source connector. Inside the [docker-compose file](docker-compose.yml) it's called `connect` and its container image is also built during `docker-compose build`. The [Dockerfile](../../Dockerfile-Samples) uses Debezium image. The Debezium image contains Kafka Connect runtime and Debezium connectors. Our Dockerfile amends it with Kafka Connect QuestDB Sink.
105+
The Debezium Postgres connector is implemented as a Kafka Connect source connector. Inside the [docker-compose file](docker-compose.yml) it's called `connect` and its container image is also built during `docker compose build`. The [Dockerfile](../../Dockerfile-Samples) uses Debezium image. The Debezium image contains Kafka Connect runtime and Debezium connectors. Our Dockerfile amends it with Kafka Connect QuestDB Sink.
106106
107107
What's important: When this container start it just connects to Kafka broker, but it does not start any connectors. We need to start the connectors using `curl` command. This is how we started the Debezium connector:
108108
```shell
109-
curl -X POST -H "Content-Type: application/json" -d '{"name":"debezium_source","config":{"tasks.max":1,"database.hostname":"postgres","database.port":5432,"database.user":"postgres","database.password":"postgres","connector.class":"io.debezium.connector.postgresql.PostgresConnector","database.dbname":"postgres","database.server.name":"dbserver1"}} ' localhost:8083/connectors
109+
curl -X POST -H "Content-Type: application/json" -d '{"name":"debezium_source","config":{"tasks.max":1,"database.hostname":"postgres","database.port":5432,"database.user":"postgres","database.password":"postgres","connector.class":"io.debezium.connector.postgresql.PostgresConnector","database.dbname":"postgres","topic.prefix":"dbserver1"}} ' localhost:8083/connectors
110110
```
111111
It uses Kafka Connect REST interface to start a new connector with a given configuration. Let's have a closer look at the configuration. This is how it looks like when formatted for readability:
112112
```json
@@ -120,11 +120,11 @@ It uses Kafka Connect REST interface to start a new connector with a given confi
120120
"database.password": "postgres",
121121
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
122122
"database.dbname": "postgres",
123-
"database.server.name": "dbserver1"
123+
"topic.prefix": "dbserver1"
124124
}
125125
}
126126
```
127-
Most of the fields are self-explanatory. The only non-obvious one is `database.server.name`. It's a unique name of the database server. It's used by Kafka Connect to store offsets. It's important that it's unique for each database server. If you have multiple Postgres databases, you need to use different `database.server.name` for each of them. It's used by Debezium to generate Kafka topic names. The topic name is generated as `database.server.name`.`schema`.`table`. In our case it's `dbserver1.public.stock`.
127+
Most of the fields are self-explanatory. The only non-obvious one is `topic.prefix`. It's used by Debezium to generate Kafka topic names. The topic name is generated as `topic.prefix`.`schema`.`table`. In our case it's `dbserver1.public.stock`. It's important that it's unique for each database server. If you have multiple Postgres databases, you need to use different `topic.prefix` for each of them.
128128
129129
### Kafka QuestDB connector
130130
The Kafka QuestDB connector re-uses the same Kafka Connect runtime as the Debezium connector. It's also started using `curl` command. This is how we started the QuestDB connector:
@@ -172,7 +172,7 @@ Let's focus on the ExtractNewRecordState transform a bit more. Why is it needed
172172
"last_update": 1666172978269856
173173
},
174174
"source": {
175-
"version": "1.9.6.Final",
175+
"version": "2.7.3.Final",
176176
"connector": "postgresql",
177177
"name": "dbserver1",
178178
"ts_ms": 1666172978272,
@@ -207,7 +207,7 @@ This is the actual change in a table. It's a JSON object which contains the new
207207
We cannot feed a full change object to Kafka Connect QuestDB Sink, because the sink would create a column for each field in the change object, including all metadata, for example the source part of the JSON:
208208
```json
209209
"source": {
210-
"version": "1.9.6.Final",
210+
"version": "2.7.3.Final",
211211
"connector": "postgresql",
212212
"name": "dbserver1",
213213
"ts_ms": 1666172978272,

0 commit comments

Comments
 (0)