Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Connector catalog

faucet-stream ships 37 sources and 29 sinks. Each is a Cargo feature (source-<name> / sink-<name>) and an independently published crate. Full API docs are on docs.rs.

Run faucet list to see what’s compiled into your binary, and faucet schema source <name> / faucet schema sink <name> for a connector’s exact config fields. Not sure which to pick? See Choosing a connector.

Legend: ✓ supported · ✗ not applicable. Tier: T1 = passes the faucet-conformance battery in CI; T2 = not yet wired into the battery.

Two “tier” signals, distinct on purpose. The Tier column below (T1/T2/T3) tracks whether a connector is wired into the reusable faucet-conformance test battery in CI. Separately, faucet conformance computes a maturity tier — 🟢 Stable / 🟡 Experimental / 🟠 Beta / ⚪ Draft — from each connector’s config schema and advertised capabilities; that tier shows in faucet list and cli/connectors/registry.json. See Connector conformance & tiers.

Sources

ConnectorTier¹¹FeatureStreams¹Resumable²Effectively-once³CompressionDiscover¹⁰Underlying primitive
RESTT1 ✅ᵐsource-restHTTP + 6 pagination styles, JSONPath extraction
GraphQLT1 ✅ᵐsource-graphqlcursor pagination, variable injection
XML / SOAPT1 ✅ᵐsource-xmlstreaming XML→JSON, dot-path extraction, first-class soap: block (envelope + headers + fault handling)
gRPCT1 ✅source-grpc✓⁴dynamic protobuf; unary + server-streaming
PostgreSQLT1 ✅source-postgresSQL query, rows as JSON
PostgreSQL CDCT1 ✅source-postgres-cdclogical replication (pgoutput), LSN bookmarks
MySQLT1 ✅source-mysqlSQL query, rows as JSON
MySQL CDCT1 ✅source-mysql-cdcbinlog row events, file/pos or GTID bookmarks
Microsoft SQL ServerT1 ✅source-mssql✓⁸SQL query (tiberius), rows as JSON
Microsoft SQL Server CDCT1 ✅source-mssql-cdcCDC change tables (fn_cdc_get_all_changes), LSN bookmarks, __op-normalized
SQLiteT1 ✅source-sqliteSQL query, rows as JSON
DuckDBT2source-duckdbSQL query (file or :memory:), rows as JSON; blocking-task + channel streaming
AWS SQST2source-sqslong-poll ReceiveMessage, delete-after-emit (at-least-once), idle/max-messages termination
NATST2source-natssubject subscription or JetStream durable consumer; idle/max-messages termination
SFTPT2source-sftplist/glob a remote dir over SSH; JSONL, JSON array, raw text
AWS S3T1 ✅source-s3✓⁵object reader: JSONL, JSON array, raw text
Google Cloud StorageT2source-gcs✓⁵object reader: JSONL, JSON array, raw text
Azure Blob / ADLS Gen2T1 ✅source-azure-blob✓⁵object reader (object_store): JSONL, JSON array, raw text
MongoDBT1 ✅source-mongodbfind() with filter/projection/sort
MongoDB CDCT1 ✅source-mongodb-cdcChange Streams, resumeToken bookmarks; max_staged_records buffer cap
RedisT1 ✅source-redisstreams, lists, key patterns
WebhookT2source-webhook✗⁶temporary HTTP server collecting POSTs
WebSocketT1 ✅source-websocketlive push feed; subscribe frames, reconnect, ping keepalive
CSVT1 ✅source-csvCSV files as JSON; strict field count by default (flexible: true to tolerate ragged rows)
ElasticsearchT1 ✅ᵐsource-elasticsearchsearch/scroll API
Apache KafkaT1 ✅source-kafkaconsumer; idle/max-messages termination, offset bookmarks
AWS KinesisT1 ✅source-kinesisper-shard GetRecords workers; sequence-number bookmarks, idle/max-messages termination
Google Cloud Pub/SubT1 ✅ᵉsource-pubsubstreaming pull; per-message records + attributes, ack at durable page boundary (at-least-once), idle/max-messages termination
Apache ParquetT1 ✅source-parquetlocal/glob/S3, vectorized Arrow reader, projection
Apache Delta LakeT1 ✅source-deltalocal FS or S3/Azure/GCS; time travel (version/timestamp), projection pushdown, partition reconstruction
Databricks SQLT1 ✅ᵐsource-databricksStatement Execution API; async poll, chunk pagination, typed decode, incremental ${bookmark}
Amazon RedshiftT1 ✅source-redshiftPostgreSQL wire; SQL query, rows as JSON; incremental replication
ClickHouseT1 ✅source-clickhouseHTTP interface, FORMAT JSONEachRow streaming; incremental replication
BigQueryT1 ✅ᵐsource-bigqueryjobs.query + pageToken pagination
SnowflakeT1 ✅ᵐsource-snowflakeSQL REST API, server-side partitions
Cloud SpannerT1 ✅ᵉsource-spanner✓⁸streaming SQL (gRPC), incremental @bookmark replication, stale reads, PK-range sharding
Singer bridge ⚠️T2 ⚠️source-singer✓⁹runs an external Singer tap; NDJSON over stdout, STATE→bookmark. Tier-2 / experimental

¹⁰ Discover = enumerates the datasets behind the connection for faucet discover (tables / collections / indices / prefixes with schemas + row estimates where the catalog provides them). ¹ Streams = yields records in bounded-memory batches rather than buffering the whole result. ² Resumable = persists a bookmark to a state store so re-runs continue where they left off (incremental replication / CDC / Kafka offsets). ³ Effectively-once = the source emits a complete resume position on every page and replaying from a bookmark continues the record stream at exactly that position (immutable-log sources: CDC WAL/binlog/change streams, Kafka partition offsets); required for the atomic-watermark mechanism behind delivery: exactly_once — see Effectively-once delivery. ⁴ gRPC streams natively in server-streaming mode; unary buffers the single response. ⁵ S3/GCS stream in JSONL and raw-text modes; JSON-array mode buffers one object. ⁶ Webhook is buffer-shaped by nature (it collects POSTs over a window). ⁸ MSSQL is resumable only in replication: incremental mode (it persists a tracking-column bookmark); in full mode it is not. ⁹ The Singer bridge is resumable via the tap’s STATE messages, but the granularity of resume (and whether re-emitted rows overlap) depends on the individual tap — pair it with a keyed/upsert sink for clean, effectively-once (idempotent at-least-once) behavior.

Support tiers (the Tier column above). A connector is Tier-1 ✅ when it invokes and passes the faucet-conformance battery in CI against the connector’s real backend — config-schema validity, bounded-memory streaming, and (where applicable) bookmark round-trip, idempotent replay, truthful capabilities, and errors-not-panics (see the Faucet Connector Protocol spec, docs/spec/faucet-connector-spec-v0.md). Each Tier-1 connector wires the battery from its own tests/conformance.rs; that battery is the tiering mechanism — there is no separate scheme.

marks a connector whose battery runs in CI against a wiremock HTTP mock, not a live service instance — the rest, graphql, xml, elasticsearch, bigquery, snowflake, and databricks sources and the http sink. The mock faithfully drives the paging, schema, and error-handling behavior the checks assert, but it is not an end-to-end test against the real system (no credentialed cloud/service backend runs in CI). marks a connector whose battery runs against an official emulator in Docker — a real implementation, closer to end-to-end than a wiremock but still not the managed service: the Cloud Spanner pair (Spanner emulator, gRPC), the Pub/Sub source and sink (Pub/Sub emulator, gRPC), and the Azure Blob sink (Azurite). Unmarked T1 ✅ connectors run against a real backend with no emulator caveat — a local filesystem (delta, parquet, csv), or a testcontainers-launched real server (postgres, mysql, mongodb, redis, clickhouse, kafka, …).

The connectors still marked Tier-2 are the ones whose full battery cannot run in CI (so they are not conformance-certified — Tier-2 means “not certified,” not “low quality”; they keep their own extensive wiremock/testcontainers tests): the BigQuery and Snowflake sinks and the Elasticsearch sink are cloud-only and tested against wiremock, which cannot validate real idempotent dedup; the GCS source’s bounded-memory check needs a real gRPC backend (the emulator is REST-only); the GCS sink cannot be durably counted against the emulator; the webhook source is buffer-shaped (no bounded-memory page check); and the Iceberg sink is append-only with a terminal flush that does not fit the effectively-once replay check on iceberg-rust 0.9.1. The Singer bridge ⚠️ passes the battery but is additionally experimental (v0, single-stream).

Streaming: native vs. buffered

The Streams¹ column above is not all-or-nothing — every source participates in the bounded-memory streaming loop, but there are two ways it gets there:

  • Native streaming (override). The source reads from its underlying primitive incrementally — a database cursor, a WAL/binlog/change stream, an object read line by line, a scroll cursor, a Kafka partition — and emits each StreamPage as it goes. Memory stays at O(batch_size) no matter how large the result set. These sources override Source::stream_pages.
  • Buffered fallback (default). The source implements only the one required method, fetch_with_context; the default stream_pages calls it, buffers the whole result, then chunks the buffer into pages. Correct and still streamed to the sink, but peak memory is the full result set because the fetch buffered it first.

A connector author gets the buffered path for free and opts into native streaming only where the primitive supports it — see ADR 0001 and the stream-pages architecture note.

Sources that override stream_pages for native streaming: rest, graphql, xml, postgres, postgres-cdc, mysql, mysql-cdc, mssql, mssql-cdc, sqlite, mongodb, mongodb-cdc, s3/gcs/azure-blob (JSONL & raw-text modes), parquet, csv, elasticsearch (scroll), kafka, kinesis, spanner, websocket, redis, and grpc (server-streaming mode).

Sources that intentionally keep the buffered default: grpc unary mode (a single response — no paging primitive) and webhook (buffer-shaped by nature — it collects POSTs over a window). S3/GCS/Azure fall back to buffered for the JSON-array format only (one array object must be parsed whole).

Sinks

Every sink exposes a batch_size knob for write-side re-chunking. For the file/append sinks (jsonl, csv, stdout) it’s a no-op — they write per record.

ConnectorTier¹¹Featurebatch_sizeCompressionUpsert⁸Effectively-once⁷Write unit
BigQueryT2sink-bigquerytabledata.insertAll streaming; in-place MERGE for upsert + effectively-once
PostgreSQLT1 ✅sink-postgresmulti-row INSERT (JSONB or mapped cols); COPY FROM STDIN fast-path for append (write_method: copy)
JSON LinesT1 ✅sink-jsonlno-opbuffered file append
SnowflakeT2sink-snowflakeSQL REST API; multi-statement BEGIN;INSERT;MERGE;COMMIT transaction for effectively-once
Amazon RedshiftT1 ✅sink-redshiftCOPY-from-S3 (staged) or multi-row INSERT; append-only
ClickHouseT1 ✅sink-clickhouseINSERT … FORMAT JSONEachRow; optional async_insert; append-only
MySQLT1 ✅sink-mysqlmulti-row INSERT
Microsoft SQL ServerT1 ✅sink-mssqlmulti-row INSERT (2100-param auto-split, per-row DLQ)
SQLiteT1 ✅sink-sqlitetransaction-wrapped batch
DuckDBT2sink-duckdbtransaction-wrapped multi-row INSERT (JSON column or auto-mapped); append-only
AWS SQST2sink-sqsbatched SendMessageBatch (10/req), per-entry partial-failure retry; FIFO group/dedup
NATST2sink-natspublish to a subject (optional subject-per-record), flush per batch
SFTPT2sink-sftpJSONL files over SSH; atomic temp-then-rename upload
AWS S3T1 ✅sink-s3JSONL objects, parallel uploads
Google Cloud StorageT2sink-gcsJSONL objects
Azure Blob / ADLS Gen2T1 ✅ᵉsink-azure-blobJSONL blobs (object_store), batch/byte rollover
MongoDBT1 ✅sink-mongodbinsert_many; multi-document transaction for effectively-once (replica set required)
RedisT1 ✅sink-redisstreams, lists, key-value (pipelined); MULTI/EXEC transaction for effectively-once
CSVT1 ✅sink-csvno-opbuffered file rows; column set frozen from first batch (on_unknown_field: warn/error)
ElasticsearchT2sink-elasticsearch_bulk NDJSON (per-row DLQ)
HTTPT1 ✅ᵐsink-httpPOST, concurrent under a semaphore
StdoutT1 ✅sink-stdoutno-opJSON Lines / pretty JSON / TSV
Apache KafkaT1 ✅sink-kafkaproducer, batched sends, multi-topic routing; transactional producer + compacted watermark side-topic for effectively-once
AWS KinesisT1 ✅sink-kinesisbatched PutRecords; partition-key routing, per-entry partial-failure retry (DLQ-routable)
Google Cloud Pub/SubT1 ✅ᵉsink-pubsubbatched publish; optional ordering key, per-entry partial-failure retry (DLQ-routable)
Cloud SpannerT1 ✅ᵉsink-spannerbatched mutations (insert / insert_or_update / delete), cell-budget chunking, commit-token transaction for effectively-once
Apache ParquetT1 ✅sink-parquet✗⁶local/S3, schema inference (re-inferred per file on rollover), row/byte rollover
Apache Delta LakeT1 ✅sink-delta✗⁶append-only; local FS or S3/Azure/GCS; schema-inferred table creation, partitioning, one commit per flush
Apache IcebergT2sink-iceberg✗⁶REST/Glue/SQL/HMS catalog, local + cloud (S3/GCS) warehouses, fast_append snapshot, Parquet data files

⁶ Parquet and Iceberg both handle compression internally at the Parquet column level, so the file-level compression feature doesn’t apply to either. ⁷ Effectively-once = commits data and a watermark token atomically; required for delivery: exactly_once. The BigQuery sink does this via a multi-statement MERGE transaction (distinct from its default streaming insertAll path); the Kafka sink uses a transactional producer that writes each page’s records plus a commit-token record into a compacted side-topic in one Kafka transaction; the Snowflake sink runs one multi-statement BEGIN;INSERT;MERGE;COMMIT request; the Redis sink wraps the page plus a _faucet_commit_token:<scope> key in one MULTI/EXEC; the MongoDB sink commits the page plus a watermark document in one multi-document transaction (replica set required); the Cloud Spanner sink buffers the page’s mutations plus a faucet_commit_token row in one read-write transaction. Sinks configured with write_mode: upsert + key also reach effectively-once via keyed dedup, with any source. See Effectively-once delivery. ⁸ Upsert = supports write_mode: upsert / delete (insert-or-update and delete by key) in addition to plain append. The SQL sinks require column-mapping mode (auto_map, or auto_columns for mssql) and a UNIQUE/PRIMARY KEY on key; the schemaless sinks (MongoDB, Elasticsearch) map key to a match filter / _id. Iceberg upsert is not yet supported (a follow-up, blocked on iceberg-rust). See Upsert / mirror tables.

Arrow columnar (Parquet) fast path

An opt-in, additive Arrow columnar path (RFC 0002 / #375, behind a crate-local arrow feature) lets a run move records end-to-end as Arrow RecordBatches with no serde_json::Value materialization. It engages automatically when both ends of the pipeline are Arrow-native and no Value-shaped transform is configured; otherwise the pipeline transparently falls back to the row path.

Arrow-native connectors:

  • Parquet source/sink and Delta Lake source/sink — Arrow-native by nature.
  • AWS S3 and Google Cloud Storage source — with file_format: parquet.
  • AWS S3 and Google Cloud Storage sink — with format: parquet (each object is a self-contained ZSTD-compressed Parquet file).
  • Databricks SQL source — with arrow_native: true (fetches EXTERNAL_LINKS + ARROW_STREAM; requires replication: full).
  • BigQuery source — with read_api: true + read_table (reads the table via the Storage Read API gRPC service as Arrow; full extract only).
  • BigQuery sink — with a bulk_load block (Parquet on a GCS staging bucket then a PARQUET load job; append only).
  • Snowflake sink — with a bulk_load block (Parquet uploaded to an external stage then COPY INTO … FILE_FORMAT=(TYPE=PARQUET); append only). The Snowflake source has no Arrow path (its v2 SQL API is jsonv2-only).

So chains like s3(parquet) → parquet, gcs(parquet) → delta, databricks(arrow) → parquet, bigquery(read-api) → parquet, or parquet → snowflake(bulk-load) run Arrow end-to-end. See each connector’s README for the exact config field and feature flag.

Data-integrity notes

A few connectors enforce defaults that prevent silent data loss or corruption. Inspect the exact fields with faucet schema source <name> / faucet schema sink <name>.

  • CSV source — strict by default. A row whose field count differs from the header raises an error naming the offending line. Set flexible: true to tolerate ragged rows (the pre-1.x behaviour). (Breaking default change.)
  • CSV sink — the column set is frozen from the first batch (the header cannot be rewritten in place). A field that first appears in a later page is dropped; on_unknown_field: warn (default) emits a one-shot warning naming the dropped field(s), while on_unknown_field: error aborts with a typed error.
  • Parquet sink — the Arrow schema is re-inferred per output file on rollover, so a file written after the source widens picks up the new schema. A Parquet file’s schema is immutable once opened, so a field appearing only later within a single file is dropped with a per-file one-shot warning.
  • MongoDB CDC sourcemax_staged_records (default unbounded) caps the in-memory change-event buffer (including under batch_size: 0) and aborts with a typed error rather than risking OOM, mirroring postgres-cdc / mysql-cdc.

Schema evolution

The pipeline-level schema: block detects when an incoming page’s top-level shape diverges from the sink’s destination schema and applies one policy (warn / ignore / fail / quarantine / evolve). Which sinks can actually act on it varies:

SinkSchema evolution
postgres, mysql, mssql, sqlite, bigquery✓ evolve — in-place additive/widening DDL
elasticsearch✓ evolve — can add fields only (existing-field type change is incompatible)
spanner✓ evolve — additive columns + NOT NULL relax; base-type widening is not supported by Spanner (use allow_type_widening: false)
icebergdetect-only — warn/ignore/fail/quarantine work; evolve blocked on upstream iceberg-rust (#255)
jsonl, csv, stdout, mongodb, redis, http, kafka, s3, gcs, snowflake, parquet— (schemaless; the schema: policy is inert)

on_drift: evolve against a detect-only or schemaless sink is rejected at config-load. See Schema drift for the per-sink nuances (e.g. SQLite widening is a no-op; Elasticsearch can only add fields).

Authentication at a glance

FamilyAuth options
REST / GraphQL / XMLBearer, Basic, ApiKey (header), ApiKeyQuery, OAuth2 (client-credentials), TokenEndpoint, Custom headers — see Auth cookbook
BigQueryservice-account key (path or inline JSON), application-default credentials
SnowflakeJWT key-pair, OAuth
Cloud Spannerservice-account key (path or inline JSON), application-default credentials
KafkaSASL (PLAIN/SCRAM) + TLS
WebSocketnone, Bearer token, Custom headers
Elasticsearchbasic, API key, bearer, none
S3 / GCScloud SDK credential chains (env, profile, metadata)
SQL databasesconnection URL (with embedded credentials / TLS params)

Inspect any connector’s exact auth shape with faucet schema source <name> / faucet schema sink <name>.

Batching

Default batch_size is 1000; max is 1,000,000. batch_size: 0 means “no batching” — the source emits the whole result set in one page and the sink writes it in one request (good for small lookup tables or load-job-style sinks). See Performance tuning.


¹¹ Tier = conformance status. T1 ✅ means the connector adds a tests/conformance.rs that invokes the reusable faucet-conformance battery against the real connector and passes it in CI (valid config schema, bounded-memory streaming, honest capabilities, and the further checks as they land) — that battery is the single source of truth for the tier. T2 means the connector is not yet wired into the battery; most still have their own integration tests, so T2 does not mean low quality. See the Faucet Connector Protocol (FCP v0) for the full contract.