B S Mohammed Ashfaq
10/20/2026, 5:46 AMMETA-INF/services.
Maven Shade’s default handling caused some of these files to be lost, which broke name resolution at runtime. The resulting error was quite misleading and took some digging to trace back to the packaging configuration.
The fix was to add ServicesResourceTransformer to the Maven Shade configuration so that the service files are merged correctly.
This isn't specific to Qdrant; it’s something worth watching out for when packaging connectors that depend on gRPC.
I also added schema validation at table-creation time so unsupported types, such as DECIMAL, TIMESTAMP, and nested types, are rejected early with the offending column identified.
2. DuckDB — handling concurrent writes
DuckDB's local database file uses process-level locking, which creates a problem when multiple Flink sink subtasks try to write to the same file.
Rather than allowing the job to get stuck or encounter unpredictable write behavior, the connector validates the sink parallelism and rejects values greater than 1 for local file targets.
For remote targets that support concurrent access, such as MotherDuck, higher parallelism can be supported.
I also made the delivery behavior explicit: regular inserts provide at-least-once semantics, while upserts configured with key-fields use ON CONFLICT to make retries idempotent.
Using the connectors
Both connectors follow the usual Flink SQL workflow: define a table and use INSERT INTO.
For example, with Qdrant:
CREATE TABLE embeddings (
id STRING,
text STRING,
embedding ARRAY<FLOAT>
) WITH (
'connector' = 'qdrant',
'host' = 'localhost',
'collection-name' = 'my_collection',
'vector-field' = 'embedding',
'id-field' = 'id'
);
INSERT INTO embeddings
SELECT id, text, embedding FROM source_stream;
And with DuckDB:
CREATE TABLE sink_table (
id BIGINT,
name STRING,
score DOUBLE
) WITH (
'connector' = 'duckdb',
'database-path' = '/data/my.db',
'table-name' = 't',
'key-fields' = 'id'
);
INSERT INTO sink_table
SELECT id, name, score FROM source_stream;
Current status
Both connectors target Flink 1.20 and are released under the Apache 2.0 license. They currently support the sink side only, and the target table or collection must already exist.
• Qdrant: 13/13 tests passing, including an integration test using a real Qdrant Testcontainer.
• DuckDB: 14/14 tests passing against an actual embedded database file, without requiring Docker.
These are fresh projects, so there's still room to improve them based on real-world usage.
Sharing them here in case anyone is working with Flink and Qdrant or DuckDB, or has run into similar issues with gRPC shading or concurrent writes to local database files.
Feedback, suggestions, and issues are welcome!
GitHub repositories:
• Qdrant: github.com/Ashfaqbs/flink-connector-qdrant
• DuckDB: github.com/Ashfaqbs/flink-connector-duckdb