s2_sourcereads S2 streams into Arrow batches for any downstream Streamling transform or sink — exact stream names or a prefix, with newly created streams discovered automatically. Records can flow through as the raw envelope (stream,seq_num,timestamp,headers,body) or as typed columns decoded from JSON bodies.s2_sinkappends rows from any Streamling source or transform as JSON records to an S2 stream, with optional per-row routing across streams via a template likeevents/{tenant}.
s2_source when S2 is the durable input to a Streamling pipeline, and s2_sink when S2 is the durable output. The examples below use ClickHouse and Postgres CDC to show useful Streamling sources and sinks in both directions, but the S2 plugins compose with Streamling’s other sources, sinks, and transforms.
The two plugins also compose into CDC round trips: the sink carries each row’s operation as a Debezium-style dbz.op record header, and the source restores it, so inserts, updates, and deletes survive a trip through S2 and apply correctly in a downstream database.
Reference
Configuration tables for both plugins are in the streamling-community-plugins README.Prerequisites
Install the community plugins
Download the shared library for your platform from the releases page and tell Streamling where to find it:Use the
- macOS
- Linux
darwin-x86_64 asset on Intel Macs.Example: Events into ClickHouse
This example sendsPOST → S2 → Streamling → ClickHouse: services append JSON events to S2 streams over the REST API, and a Streamling pipeline tails every stream under a prefix, decodes the events into typed columns, and upserts them into ClickHouse.
Create event streams and append some events
Write the pipeline
Create The
events-to-clickhouse.yaml:schema option decodes JSON bodies into typed columns, and include_metadata adds _s2_stream, _s2_seq_num, and _s2_timestamp columns. The ClickHouse sink creates the table on first write.Run it
Connection details are passed as environment variables, keeping secrets out of the pipeline definition:The pipeline reads both streams from the beginning and keeps tailing them for new records. Leave it running while you query ClickHouse in the next step.
Query ClickHouse
In another terminal:Append more events with
curl and they show up in ClickHouse within moments. You can even create a brand new events-* stream: the source re-lists the prefix every 60 seconds (tunable via update_streams_interval_secs) and starts reading it automatically.Events posted to S2 are flowing into ClickHouse as typed rows.
Example: Postgres CDC into S2
This example sendsPostgres CDC → Streamling → S2: Streamling’s postgres_cdc_source performs an initial table copy followed by continuous logical replication, and s2_sink appends every change to an S2 stream with the operation preserved as a dbz.op record header.
Write the pipeline
Create The target stream is created automatically on first append (
postgres-cdc-to-s2.yaml:ensure_stream defaults to true).memory_backpressure_enabled: false suits local machines, where system memory use often sits above the plugin’s default backpressure threshold. See the plugin README for production guidance, including least-privilege Postgres permissions.Read the changes from S2
In another terminal, the initial table copy is already there, each record carrying its operation as a header:Leave the read session tailing and make some changes:The update and delete stream in with
u and d ops:Postgres changes are flowing into your S2 stream in order, with CDC semantics intact.
Example: Postgres to ClickHouse through S2
Becauses2_source maps dbz.op headers back to row operations, the CDC stream from the previous section can drive a second pipeline into ClickHouse — S2 acting as the durable, replayable changelog between the two.
If the ClickHouse container from the first example is not already running, start it:
cdc-to-clickhouse.yaml:
?) since delete records carry only the primary key. Querying ClickHouse shows the update applied and the deleted row gone:
Streamling checkpoints source positions in its state backend — by default a SQLite file at
./state.db, so a restarted pipeline resumes where it left off rather than re-reading streams. Postgres is available as a state backend for production. Delivery is at-least-once end to end, and replays apply cleanly since sinks upsert by primary key.
