mq-bridge-py-basic 0.3.3__tar.gz → 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/workflows/benchmark.yml +22 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/CONFIGURATION.md +16 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/Cargo.lock +84 -4
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/Cargo.toml +22 -3
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/PKG-INFO +19 -4
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/README.md +48 -20
- mq_bridge_py_basic-0.3.4/benches/ETL_BENCHMARKS.md +107 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/mq-bridge.schema.json +362 -2
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/mq_bridge/__init__.pyi +6 -1
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/mq_bridge/config.py +6 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/mq_bridge/config.pyi +50 -1
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/README.md +18 -3
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/src/lib.rs +60 -0
- mq_bridge_py_basic-0.3.4/src/checkpoint.rs +520 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/command_handler.rs +80 -90
- mq_bridge_py_basic-0.3.4/src/endpoints/clickhouse.rs +706 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/file.rs +353 -17
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/kafka.rs +80 -44
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/mod.rs +171 -9
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/mongodb.rs +972 -3
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/mqtt.rs +61 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/nats.rs +15 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/poll.rs +64 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/postgres/mod.rs +422 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/postgres/pgoutput/decoder.rs +772 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/postgres/pgoutput/messages.rs +246 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/postgres/pgoutput/mod.rs +106 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/postgres/pgoutput/registry.rs +123 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/postgres/pgoutput/values.rs +516 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/postgres/replication.rs +258 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/postgres/state.rs +67 -0
- mq_bridge_py_basic-0.3.4/src/endpoints/sqlx.rs +2576 -0
- mq_bridge_py_basic-0.3.4/src/lib.rs +77 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/models.rs +273 -1
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/publisher.rs +11 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/route.rs +288 -0
- mq_bridge_py_basic-0.3.4/tests/integration/clickhouse.rs +178 -0
- mq_bridge_py_basic-0.3.4/tests/integration/docker-compose/clickhouse.yml +21 -0
- mq_bridge_py_basic-0.3.4/tests/integration/docker-compose/postgres_cdc.yml +29 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/mod.rs +4 -0
- mq_bridge_py_basic-0.3.4/tests/integration/postgres.rs +376 -0
- mq_bridge_py_basic-0.3.4/tests/integration/postgres_cdc.rs +209 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration_test.rs +53 -0
- mq_bridge_py_basic-0.3.3/src/endpoints/sqlx.rs +0 -993
- mq_bridge_py_basic-0.3.3/src/lib.rs +0 -40
- mq_bridge_py_basic-0.3.3/tests/integration/postgres.rs +0 -202
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/workflows/ci.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/workflows/ibm-mq.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/workflows/node.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/workflows/publish-node.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/workflows/publish-python.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/workflows/python.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/workflows/release.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.github/workflows/test-matrix.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/.gitignore +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/ARCHITECTURE.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/CONTRIBUTING.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/LICENSE +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/LICENSE.python +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/benches/performance_bench.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/benches/router_bench.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/bindings-common/Cargo.toml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/bindings-common/src/lib.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/bindings-common/src/logging.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/build.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/claude.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/examples/ipc_worker_queue.yaml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/mq_bridge/__init__.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/mq_bridge/py.typed +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/package-lock.json +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/pyproject.toml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/Cargo.toml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/LICENSE.python +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/analysis/bench_http_native.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/example.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/examples/bench_memory.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/examples/bench_memory.yaml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/examples/consume_pull.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/examples/dlt_source.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/examples/json_route.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/examples/memory.yaml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/examples/raw_route.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/pyproject-basic.toml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/pyproject-reduced.toml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/scripts/gen_config_types.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/tests/test_config_types.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/tests/test_logging.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/tests/test_packaging.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/tests/test_performance_smoke.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/tests/test_public_api.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/tests/test_routes.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/python/mq-bridge-py/uv.lock +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/analysis/HTTP_STREAM_ENDPOINT_PLAN.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/analysis/benches/metadata_bench.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/analysis/debug_ibm_mq_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/analysis/http/analyze_xctrace_time_profile.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/analysis/http/http_flamegraph.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/analysis/http/mq_bridge_http_profile.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/analysis/http/run_http_ladder.sh +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/ci/format_bench_output.sh +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/httparena/README.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/httparena/frameworks/mq-bridge-py/Dockerfile +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/httparena/frameworks/mq-bridge-py/meta.json +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/httparena/frameworks/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/techempower/Python/mq-bridge-py/README.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/techempower/Python/mq-bridge-py/benchmark_config.json +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/techempower/Python/mq-bridge-py/mq-bridge-py.dockerfile +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/techempower/Python/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/techempower/README.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/techempower/postgres.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/techempower/seed.sql +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/techempower/verify.sh +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/the-benchmarker/README.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/the-benchmarker/python/mq-bridge-py/config.yaml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/the-benchmarker/python/mq-bridge-py/pyproject.toml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/scripts/the-benchmarker/python/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/canonical_message.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/connection_registry.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/amqp.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/aws.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/fanout.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/grpc.proto +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/grpc.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/http.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/http_stream.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/ibm_mq.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/memory/endpoint.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/memory/ipc_unix.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/memory/ipc_windows.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/memory/memory_transport.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/memory/mod.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/memory/transport.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/null.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/reader.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/redis_streams.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/response.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/sled.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/static_endpoint.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/stream_buffer.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/switch.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/websocket.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/endpoints/zeromq.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/errors.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/event_handler.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/event_store.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/extensions.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/buffer.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/cookie_jar.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/deduplication.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/delay.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/dlq.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/limiter.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/metrics.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/mod.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/random_panic.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/retry.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/middleware/weak_join.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/outcomes.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/response.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/test_utils.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/traits.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/src/type_handler.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/README.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/armature_integration.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/amqp.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/aws.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/config.toml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/amqp.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/aws.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/ibm-mq-certs/client.crl +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/ibm-mq-certs/client.kdb +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/ibm-mq-certs/client.p12 +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/ibm-mq-certs/client.rdb +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/ibm-mq-certs/client.sth +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/ibm_mq.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/ibm_mq_tls.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/kafka-tls.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/kafka.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mariadb.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mongodb-replica.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mongodb-tls.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mongodb.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mosquitto.conf +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mosquitto_performance.conf +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mosquitto_persistence.conf +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mq_init.mqsc +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mq_init_tls.mqsc +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mqtt.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mqtt_performance.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/mysql.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/nats.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/postgres.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/docker-compose/redis.yml +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/file.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/grpc.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/grpc_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/http.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/http_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/ibm_mq.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/ibm_mq_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/ipc.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/kafka.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/kafka_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/logic_test.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/mariadb.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/memory.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/mongodb.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/mongodb_raw.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/mongodb_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/mqtt.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/mysql.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/nats.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/performance_static.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/redis_streams.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/route.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/scripts/gen_certs.sh +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/sqlite.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/tls_helpers.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/websocket.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/integration/zeromq.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/memory_leak_test.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/ref_test.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/sqlite_test.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/tls_example.rs +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/unit_tests.md +0 -0
- {mq_bridge_py_basic-0.3.3 → mq_bridge_py_basic-0.3.4}/tests/websocket_test.rs +0 -0
|
@@ -179,6 +179,11 @@ jobs:
|
|
|
179
179
|
name: Collect Benchmark Results
|
|
180
180
|
needs: [benchmark-streams, benchmark-database, benchmark-dockerless]
|
|
181
181
|
runs-on: ubuntu-latest
|
|
182
|
+
# Serialize gh-pages pushes so overlapping runs don't race the Pages build
|
|
183
|
+
# (concurrent pushes make GitHub cancel the superseded deployment -> "failed").
|
|
184
|
+
concurrency:
|
|
185
|
+
group: gh-pages-publish
|
|
186
|
+
cancel-in-progress: false
|
|
182
187
|
permissions:
|
|
183
188
|
contents: write
|
|
184
189
|
deployments: write
|
|
@@ -248,7 +253,24 @@ jobs:
|
|
|
248
253
|
tool: 'cargo'
|
|
249
254
|
output-file-path: output.txt
|
|
250
255
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
256
|
+
# Push on both main and dev so dev commits appear in the benchmark graph.
|
|
257
|
+
# This mints a gh-pages deployment per run; the next step prunes old ones.
|
|
251
258
|
auto-push: true
|
|
252
259
|
alert-threshold: '40000%'
|
|
253
260
|
comment-on-alert: true
|
|
254
261
|
fail-on-alert: false
|
|
262
|
+
|
|
263
|
+
# Keep only the most recent gh-pages deployments so the environment's
|
|
264
|
+
# deployment list doesn't grow unbounded (one is minted per push above).
|
|
265
|
+
- name: Prune old gh-pages deployments
|
|
266
|
+
env:
|
|
267
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
268
|
+
KEEP: 20
|
|
269
|
+
run: |
|
|
270
|
+
gh api "repos/${{ github.repository }}/deployments?environment=github-pages&per_page=100" --paginate -q '.[].id' \
|
|
271
|
+
| tail -n +$((KEEP + 1)) \
|
|
272
|
+
| while read -r id; do
|
|
273
|
+
gh api -X POST "repos/${{ github.repository }}/deployments/$id/statuses" -f state=inactive >/dev/null 2>&1 \
|
|
274
|
+
|| echo "::warning::failed to mark deployment $id inactive; attempting delete anyway"
|
|
275
|
+
gh api -X DELETE "repos/${{ github.repository }}/deployments/$id" >/dev/null 2>&1 && echo "pruned $id"
|
|
276
|
+
done
|
|
@@ -139,6 +139,22 @@ For example, to set the Kafka topic for the `kafka_to_nats` route:
|
|
|
139
139
|
```sh
|
|
140
140
|
export MQB__KAFKA_TO_NATS__INPUT__KAFKA__TOPIC="my-other-topic"
|
|
141
141
|
```
|
|
142
|
+
#### Postgres CDC example
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
orders_cdc:
|
|
146
|
+
input:
|
|
147
|
+
postgres_cdc:
|
|
148
|
+
url: "postgres://user:pass@localhost:5432/app"
|
|
149
|
+
publication: "orders_pub" # CREATE PUBLICATION orders_pub FOR TABLE orders;
|
|
150
|
+
slot_name: "mqb_orders" # created if missing (permanent slot, resumable)
|
|
151
|
+
output:
|
|
152
|
+
nats:
|
|
153
|
+
subject: "orders.changes"
|
|
154
|
+
url: "nats://localhost:4222"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Each change arrives as a `CanonicalMessage` whose payload is the flat row and whose `postgres.operation` metadata marks the operation — the same convention as MongoDB CDC, so typed handlers work identically across both. The replication transport uses the published [`pgwire-replication`](https://crates.io/crates/pgwire-replication) crate.
|
|
142
158
|
|
|
143
159
|
### NATS JetStream Notes
|
|
144
160
|
|
|
@@ -218,6 +218,18 @@ dependencies = [
|
|
|
218
218
|
"tokio",
|
|
219
219
|
]
|
|
220
220
|
|
|
221
|
+
[[package]]
|
|
222
|
+
name = "async-compression"
|
|
223
|
+
version = "0.4.42"
|
|
224
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
225
|
+
checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac"
|
|
226
|
+
dependencies = [
|
|
227
|
+
"compression-codecs",
|
|
228
|
+
"compression-core",
|
|
229
|
+
"pin-project-lite",
|
|
230
|
+
"tokio",
|
|
231
|
+
]
|
|
232
|
+
|
|
221
233
|
[[package]]
|
|
222
234
|
name = "async-executor"
|
|
223
235
|
version = "1.14.0"
|
|
@@ -1097,6 +1109,23 @@ dependencies = [
|
|
|
1097
1109
|
"tokio-util",
|
|
1098
1110
|
]
|
|
1099
1111
|
|
|
1112
|
+
[[package]]
|
|
1113
|
+
name = "compression-codecs"
|
|
1114
|
+
version = "0.4.38"
|
|
1115
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1116
|
+
checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf"
|
|
1117
|
+
dependencies = [
|
|
1118
|
+
"compression-core",
|
|
1119
|
+
"flate2",
|
|
1120
|
+
"memchr",
|
|
1121
|
+
]
|
|
1122
|
+
|
|
1123
|
+
[[package]]
|
|
1124
|
+
name = "compression-core"
|
|
1125
|
+
version = "0.4.32"
|
|
1126
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1127
|
+
checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789"
|
|
1128
|
+
|
|
1100
1129
|
[[package]]
|
|
1101
1130
|
name = "concurrent-queue"
|
|
1102
1131
|
version = "2.5.0"
|
|
@@ -2952,6 +2981,12 @@ dependencies = [
|
|
|
2952
2981
|
"digest 0.11.3",
|
|
2953
2982
|
]
|
|
2954
2983
|
|
|
2984
|
+
[[package]]
|
|
2985
|
+
name = "md5"
|
|
2986
|
+
version = "0.7.0"
|
|
2987
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2988
|
+
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
|
2989
|
+
|
|
2955
2990
|
[[package]]
|
|
2956
2991
|
name = "memchr"
|
|
2957
2992
|
version = "2.8.2"
|
|
@@ -3119,7 +3154,7 @@ dependencies = [
|
|
|
3119
3154
|
|
|
3120
3155
|
[[package]]
|
|
3121
3156
|
name = "mq-bridge"
|
|
3122
|
-
version = "0.3.
|
|
3157
|
+
version = "0.3.4"
|
|
3123
3158
|
dependencies = [
|
|
3124
3159
|
"anyhow",
|
|
3125
3160
|
"arc-swap",
|
|
@@ -3130,6 +3165,7 @@ dependencies = [
|
|
|
3130
3165
|
"aws-sdk-sns",
|
|
3131
3166
|
"aws-sdk-sqs",
|
|
3132
3167
|
"base64",
|
|
3168
|
+
"byteorder",
|
|
3133
3169
|
"bytes",
|
|
3134
3170
|
"chrono",
|
|
3135
3171
|
"config",
|
|
@@ -3138,6 +3174,7 @@ dependencies = [
|
|
|
3138
3174
|
"flate2",
|
|
3139
3175
|
"futures",
|
|
3140
3176
|
"h2 0.4.14",
|
|
3177
|
+
"hex",
|
|
3141
3178
|
"http-body 1.0.1",
|
|
3142
3179
|
"http-body-util",
|
|
3143
3180
|
"hyper 1.10.1",
|
|
@@ -3151,6 +3188,8 @@ dependencies = [
|
|
|
3151
3188
|
"mq-bridge",
|
|
3152
3189
|
"mqi",
|
|
3153
3190
|
"once_cell",
|
|
3191
|
+
"percent-encoding",
|
|
3192
|
+
"pgwire-replication",
|
|
3154
3193
|
"prost",
|
|
3155
3194
|
"protoc-bin-vendored",
|
|
3156
3195
|
"rand 0.10.1",
|
|
@@ -3188,7 +3227,7 @@ dependencies = [
|
|
|
3188
3227
|
|
|
3189
3228
|
[[package]]
|
|
3190
3229
|
name = "mq-bridge-bindings-common"
|
|
3191
|
-
version = "0.3.
|
|
3230
|
+
version = "0.3.4"
|
|
3192
3231
|
dependencies = [
|
|
3193
3232
|
"anyhow",
|
|
3194
3233
|
"fast-uuid-v7",
|
|
@@ -3202,7 +3241,7 @@ dependencies = [
|
|
|
3202
3241
|
|
|
3203
3242
|
[[package]]
|
|
3204
3243
|
name = "mq-bridge-node"
|
|
3205
|
-
version = "0.3.
|
|
3244
|
+
version = "0.3.4"
|
|
3206
3245
|
dependencies = [
|
|
3207
3246
|
"anyhow",
|
|
3208
3247
|
"async-trait",
|
|
@@ -3223,7 +3262,7 @@ dependencies = [
|
|
|
3223
3262
|
|
|
3224
3263
|
[[package]]
|
|
3225
3264
|
name = "mq-bridge-py"
|
|
3226
|
-
version = "0.3.
|
|
3265
|
+
version = "0.3.4"
|
|
3227
3266
|
dependencies = [
|
|
3228
3267
|
"anyhow",
|
|
3229
3268
|
"async-trait",
|
|
@@ -3689,6 +3728,28 @@ dependencies = [
|
|
|
3689
3728
|
"indexmap",
|
|
3690
3729
|
]
|
|
3691
3730
|
|
|
3731
|
+
[[package]]
|
|
3732
|
+
name = "pgwire-replication"
|
|
3733
|
+
version = "0.3.2"
|
|
3734
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
3735
|
+
checksum = "7eec6334b2466a0163bd91c29e06a25b391c155231934aee80381b99560e240e"
|
|
3736
|
+
dependencies = [
|
|
3737
|
+
"anyhow",
|
|
3738
|
+
"base64",
|
|
3739
|
+
"bytes",
|
|
3740
|
+
"hmac 0.12.1",
|
|
3741
|
+
"md5",
|
|
3742
|
+
"rand 0.9.4",
|
|
3743
|
+
"rustls 0.23.40",
|
|
3744
|
+
"sha2 0.10.9",
|
|
3745
|
+
"thiserror 1.0.69",
|
|
3746
|
+
"tokio",
|
|
3747
|
+
"tokio-rustls 0.26.4",
|
|
3748
|
+
"tokio-util",
|
|
3749
|
+
"tracing",
|
|
3750
|
+
"webpki-roots 0.26.11",
|
|
3751
|
+
]
|
|
3752
|
+
|
|
3692
3753
|
[[package]]
|
|
3693
3754
|
name = "phf"
|
|
3694
3755
|
version = "0.13.1"
|
|
@@ -4496,6 +4557,8 @@ dependencies = [
|
|
|
4496
4557
|
"rustls 0.23.40",
|
|
4497
4558
|
"rustls-pki-types",
|
|
4498
4559
|
"rustls-platform-verifier",
|
|
4560
|
+
"serde",
|
|
4561
|
+
"serde_urlencoded",
|
|
4499
4562
|
"sync_wrapper",
|
|
4500
4563
|
"tokio",
|
|
4501
4564
|
"tokio-rustls 0.26.4",
|
|
@@ -5027,6 +5090,18 @@ dependencies = [
|
|
|
5027
5090
|
"serde_core",
|
|
5028
5091
|
]
|
|
5029
5092
|
|
|
5093
|
+
[[package]]
|
|
5094
|
+
name = "serde_urlencoded"
|
|
5095
|
+
version = "0.7.1"
|
|
5096
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5097
|
+
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
|
5098
|
+
dependencies = [
|
|
5099
|
+
"form_urlencoded",
|
|
5100
|
+
"itoa",
|
|
5101
|
+
"ryu",
|
|
5102
|
+
"serde",
|
|
5103
|
+
]
|
|
5104
|
+
|
|
5030
5105
|
[[package]]
|
|
5031
5106
|
name = "serde_with"
|
|
5032
5107
|
version = "3.21.0"
|
|
@@ -5948,12 +6023,17 @@ version = "0.6.11"
|
|
|
5948
6023
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5949
6024
|
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
|
|
5950
6025
|
dependencies = [
|
|
6026
|
+
"async-compression",
|
|
5951
6027
|
"bitflags 2.13.0",
|
|
5952
6028
|
"bytes",
|
|
6029
|
+
"futures-core",
|
|
5953
6030
|
"futures-util",
|
|
5954
6031
|
"http 1.4.2",
|
|
5955
6032
|
"http-body 1.0.1",
|
|
6033
|
+
"http-body-util",
|
|
5956
6034
|
"pin-project-lite",
|
|
6035
|
+
"tokio",
|
|
6036
|
+
"tokio-util",
|
|
5957
6037
|
"tower",
|
|
5958
6038
|
"tower-layer",
|
|
5959
6039
|
"tower-service",
|
|
@@ -16,7 +16,7 @@ members = ["bindings-common", "python/mq-bridge-py"]
|
|
|
16
16
|
resolver = "2"
|
|
17
17
|
|
|
18
18
|
[workspace.package]
|
|
19
|
-
version = "0.3.
|
|
19
|
+
version = "0.3.4"
|
|
20
20
|
|
|
21
21
|
[workspace.dependencies]
|
|
22
22
|
anyhow = "1.0"
|
|
@@ -134,6 +134,11 @@ aws-sdk-sns = { version = "1.93", optional = true, default-features = false, fea
|
|
|
134
134
|
# SQLx
|
|
135
135
|
sqlx = { version = "0.9", features = ["runtime-tokio", "tls-rustls", "postgres", "mysql", "sqlite", "any"], optional = true }
|
|
136
136
|
|
|
137
|
+
# ClickHouse — talks the ClickHouse HTTP interface directly (JSONEachRow in/out).
|
|
138
|
+
# reqwest is used rather than the typed `clickhouse` crate because that crate is
|
|
139
|
+
# RowBinary/`Row`-derive only and cannot round-trip arbitrary dynamic JSON payloads.
|
|
140
|
+
reqwest = { version = "0.13", default-features = false, features = ["rustls", "http2", "charset", "query", "gzip"], optional = true }
|
|
141
|
+
|
|
137
142
|
# ZeroMQ
|
|
138
143
|
zeromq = { version = "0.6", optional = true }
|
|
139
144
|
|
|
@@ -149,6 +154,13 @@ prost = { version = "0.14", optional = true, default-features = false, features
|
|
|
149
154
|
tokio-stream = { version = "0.1", optional = true, features = ["net"] }
|
|
150
155
|
|
|
151
156
|
url = { version = "2.5", optional = true }
|
|
157
|
+
percent-encoding = { version = "2.3", optional = true }
|
|
158
|
+
|
|
159
|
+
# Postgres CDC (logical replication / pgoutput). pgwire-replication is a
|
|
160
|
+
# narrowly-scoped wire-protocol client (COPY-both, LSN/standby, TLS/SCRAM) that
|
|
161
|
+
# deliberately avoids tokio-postgres; byteorder backs the pgoutput byte decoder.
|
|
162
|
+
pgwire-replication = { version = "0.3", optional = true, default-features = false, features = ["tls-rustls", "scram", "md5"] }
|
|
163
|
+
byteorder = { version = "1.5", optional = true }
|
|
152
164
|
|
|
153
165
|
# Generate JSON schema
|
|
154
166
|
schemars = { version = "1.2", optional = true }
|
|
@@ -168,7 +180,7 @@ middleware = ["metrics", "dedup"]
|
|
|
168
180
|
# runtime the client is loaded lazily only when an ibm-mq endpoint is opened
|
|
169
181
|
# (clear error if the client isn't installed). Use `ibm-mq-static` instead for
|
|
170
182
|
# static linking.
|
|
171
|
-
full = ["middleware", "mongodb", "kafka", "amqp", "nats", "mqtt", "http", "aws", "zeromq", "redis-streams", "sled", "grpc", "sqlx", "websocket", "rustls-aws-lc", "ibm-mq"]
|
|
183
|
+
full = ["middleware", "mongodb", "kafka", "amqp", "nats", "mqtt", "http", "aws", "zeromq", "redis-streams", "sled", "grpc", "sqlx", "clickhouse", "websocket", "postgres-cdc", "rustls-aws-lc", "ibm-mq"]
|
|
172
184
|
|
|
173
185
|
# Functionality features
|
|
174
186
|
dedup = ["sled"]
|
|
@@ -194,7 +206,7 @@ schema = ["dep:schemars"]
|
|
|
194
206
|
kafka = ["rdkafka"]
|
|
195
207
|
amqp = ["lapin", "rustls", "rustls-pemfile", "url"]
|
|
196
208
|
nats = ["async-nats", "rustls", "rustls-pemfile"]
|
|
197
|
-
mongodb = ["dep:mongodb"]
|
|
209
|
+
mongodb = ["dep:mongodb", "url"]
|
|
198
210
|
mqtt = ["rumqttc", "rustls", "tokio-rustls", "rustls-pemfile", "url"]
|
|
199
211
|
http = ["dep:arc-swap", "hyper", "hyper-util", "hyper-rustls", "http-body-util", "dep:http-body", "rustls", "tokio-rustls", "rustls-pemfile", "webpki-roots", "h2", "dep:flate2", "dep:base64"]
|
|
200
212
|
websocket = ["dep:tokio-websockets"]
|
|
@@ -216,6 +228,11 @@ redis-streams = ["dep:redis"]
|
|
|
216
228
|
# Note: The grpc feature requires protobuf-src which can significantly increase build times.
|
|
217
229
|
grpc = ["tonic", "prost", "tokio-stream", "tonic-prost", "tonic-prost-build", "protoc-bin-vendored", "rustls"]
|
|
218
230
|
sqlx = ["dep:sqlx", "url"]
|
|
231
|
+
clickhouse = ["dep:reqwest", "dep:flate2", "url"]
|
|
232
|
+
# Postgres logical-replication CDC (pgoutput). Reuses the sqlx dependency for the
|
|
233
|
+
# slot-lifecycle control plane; the streaming wire protocol is pgwire-replication.
|
|
234
|
+
# Needs Rust 1.88 (pgwire-replication MSRV), like the amqp feature gate.
|
|
235
|
+
postgres-cdc = ["dep:pgwire-replication", "dep:byteorder", "sqlx", "dep:base64", "url", "dep:percent-encoding"]
|
|
219
236
|
|
|
220
237
|
[dev-dependencies]
|
|
221
238
|
config = { version = "0.15" }
|
|
@@ -223,6 +240,8 @@ serde_yaml_ng = "0.10"
|
|
|
223
240
|
tempfile = "3.10"
|
|
224
241
|
chrono = "0.4"
|
|
225
242
|
metrics-util = "0.20"
|
|
243
|
+
# pgoutput decoder unit tests decode hex-encoded wire fixtures.
|
|
244
|
+
hex = "0.4"
|
|
226
245
|
criterion = { version = "0.8", features = ["async", "async_tokio"] }
|
|
227
246
|
# Used by the router_bench microbench to model the proposed lock-free route table.
|
|
228
247
|
arc-swap = "1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mq-bridge-py-basic
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: License :: OSI Approved :: MIT License
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -113,9 +113,24 @@ schema = config_schema() # the JSON Schema as a dict
|
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
`config_schema()` is handy for editor validation of YAML configs too — dump it
|
|
116
|
-
to a file and point your `# yaml-language-server: $schema=` line at it.
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
to a file and point your `# yaml-language-server: $schema=` line at it.
|
|
117
|
+
|
|
118
|
+
### Regenerating the config types
|
|
119
|
+
|
|
120
|
+
`mq_bridge/config.pyi` and `mq_bridge/config.py` are **generated — do not edit by
|
|
121
|
+
hand**. Regenerate them whenever the Rust config models change (e.g. adding a
|
|
122
|
+
field or a new endpoint). Because the generator reads the schema from the
|
|
123
|
+
compiled extension, you must rebuild first:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# from python/mq-bridge-py/
|
|
127
|
+
uv run maturin develop # rebuild the extension
|
|
128
|
+
uv run --no-sync python scripts/gen_config_types.py # regenerate config.pyi / config.py
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Then commit the updated `config.pyi`/`config.py`. `tests/test_config_types.py`
|
|
132
|
+
asserts the checked-in output matches the schema, so CI (the "Python package
|
|
133
|
+
smoke test" job) fails if you skip this after a models change.
|
|
119
134
|
|
|
120
135
|
## Running a route
|
|
121
136
|
|
|
@@ -19,6 +19,22 @@
|
|
|
19
19
|
|
|
20
20
|
It is not only a forwarder. A route can transform, filter, fan out, retry, rate-limit, deduplicate, or turn a request into a response before the message reaches the next system. The core is built on Tokio and keeps the transport details at the edge, so application code can mostly work with `CanonicalMessage`s and handlers.
|
|
21
21
|
|
|
22
|
+
## Move data from A to B — inside your own service
|
|
23
|
+
|
|
24
|
+
If you need to move data reliably between systems and you write code (Rust, Python, or Node), `mq-bridge` is a strong default. It is a **library you embed**, not a daemon or control plane you operate.
|
|
25
|
+
|
|
26
|
+
**Prefer not to write code?** [`mq-bridge-app`](https://github.com/marcomq/mq-bridge-app) runs the exact same engine as a **standalone, zero-code ETL service** configured entirely by **YAML or environment variables** — move data from A to B without writing a line. It ships a **Postman-style UI** to build, send, and inspect messages against a route, and can **import Postman collections and AsyncAPI documents** to scaffold routes and endpoints for you.
|
|
27
|
+
|
|
28
|
+
* **16+ transports, one API**: Kafka, NATS, AMQP (RabbitMQ), MQTT, MongoDB, **Postgres CDC** (logical replication), PostgreSQL / MySQL / SQLite (SQLx), ClickHouse, HTTP, WebSocket, gRPC, ZeroMQ, Redis Streams, AWS SQS/SNS, IBM MQ, files, and in-memory channels — all behind the same `receive_batch` / `send_batch` shape.
|
|
29
|
+
* **Change Data Capture**: stream row-level changes from **Postgres** (logical replication / `pgoutput`) and **MongoDB** (change streams) as flat rows with an operation marker.
|
|
30
|
+
* **Restart-safe delivery**: batch-aware ack/nack with commit sequencing for cumulative-ack brokers; the integration suite shows **no data loss during in-flight broker restarts**, including a Postgres CDC restart-safety test.
|
|
31
|
+
* **Reliability middleware, not a framework**: retries, dead-letter queues, deduplication, rate limiting, and cookie/session persistence wrap any endpoint.
|
|
32
|
+
* **Polyglot on one engine**: the same Rust core ships as native **Python** and **Node.js** bindings; routing, batching, and broker I/O stay in Rust.
|
|
33
|
+
* **TLS everywhere, one config shape**: a single `TlsConfig` block (CA bundle, client cert/key for mTLS, insecure-skip) is reused across transports.
|
|
34
|
+
* **Self-hosted, no daemon**: generate config in the optional UI, paste it into your code, run it in-process. No hosted control plane, no separate scheduler.
|
|
35
|
+
|
|
36
|
+
> **Throughput.** In our own benchmarks, the same engine — driven the zero-code way through [`mq-bridge-app`](https://github.com/marcomq/mq-bridge-app) — sustained ~303,000 rows/s copying 1,000,000 rows from Postgres to JSONL on commodity hardware, keeping it well in the range of dedicated data-movement tools. On a CSV→JSONL file conversion (1,000,000 mixed-type rows, ~116 MiB), it sustained **833,333 rows/s**, about **~43x faster than Meltano** (`tap-csv` → `target-jsonl`, ~19,500 rows/s). Full setup, methodology, and the exact parameters are in [`benches/ETL_BENCHMARKS.md`](benches/ETL_BENCHMARKS.md).
|
|
37
|
+
|
|
22
38
|
|
|
23
39
|
## Language Bindings
|
|
24
40
|
|
|
@@ -56,7 +72,8 @@ For implementation details and quick start examples for each usage type, see the
|
|
|
56
72
|
|
|
57
73
|
## Features
|
|
58
74
|
|
|
59
|
-
* **Supported Backends**: Kafka, NATS, AMQP (RabbitMQ), MQTT, MongoDB, SQL Databases (PostgreSQL, MySQL, SQLite via sqlx), HTTP, WebSocket, ZeroMQ, Redis Streams, Files, AWS (SQS/SNS), IBM MQ, and in-memory channels.
|
|
75
|
+
* **Supported Backends**: Kafka, NATS, AMQP (RabbitMQ), MQTT, MongoDB, SQL Databases (PostgreSQL, MySQL, SQLite via sqlx), **Postgres CDC** (logical replication), ClickHouse, gRPC, HTTP, WebSocket, ZeroMQ, Redis Streams, Files, AWS (SQS/SNS), IBM MQ, and in-memory channels.
|
|
76
|
+
* **Change Data Capture**: PostgreSQL logical replication (`postgres_cdc`) and MongoDB change streams, surfaced as flat rows with an `*.operation` marker (`insert`/`update`/`delete`/`truncate`).
|
|
60
77
|
> **Note**: IBM MQ is included in the `full` feature set via the `ibm-mq` feature, which loads the IBM MQ client library at runtime via dlopen — **no IBM SDK is needed to build**. The IBM MQ redistributable client only has to be present at runtime, and only if you actually use an IBM MQ endpoint (it is loaded lazily on first connect). If the client is missing, the affected route fails fast with a non-retryable error instead of reconnecting forever. The loader finds the client via the platform default name, `MQ_INSTALLATION_PATH` (e.g. `/opt/mqm`), or an explicit `MQB_IBM_MQ_LIB` path. To link the client statically at build time instead, use the `ibm-mq-static` feature (requires the IBM MQ SDK). See the [mqi crate](https://crates.io/crates/mqi/) for details.
|
|
61
78
|
>
|
|
62
79
|
> **TLS**: IBM MQ has its own TLS config shape (`IbmTlsConfig`), since the native client doesn't consume PEM files. The field names mirror the generic `TlsConfig` for config parity, but carry MQ-native semantics: `tls.cert_file` (alias `key_repository`) is a CMS key repository path (e.g. `/path/to/tls` for `tls.kdb`), not a PEM file. The repository can either be passwordless (backed by a `.sth` stash file next to the `.kdb`) or password-protected via `tls.cert_password` (alias `key_repository_password`; requires an IBM MQ client/server at 9.3.0.0 or later, which is the capability level `ibm-mq`/`ibm-mq-static` build against; 9.2 is EOL).
|
|
@@ -83,31 +100,32 @@ The error handling follows the same idea. Batch publishing can report partial su
|
|
|
83
100
|
|
|
84
101
|
What it does not try to be: a domain framework, an actor runtime, or a full stream processor. You can build CQRS-ish flows with it, but the library cares more about transport, routing, and delivery behavior than about prescribing your domain model.
|
|
85
102
|
|
|
86
|
-
|
|
103
|
+
### How it compares
|
|
87
104
|
|
|
88
|
-
|
|
105
|
+
`mq-bridge` overlaps with config-driven ETL / pipeline runners, but comes in two shapes: a **library you embed in your own service**, or [`mq-bridge-app`](https://github.com/marcomq/mq-bridge-app) — the **same engine as a zero-code, config-driven ETL service**. You get the developer-first path and the no-code path from one codebase.
|
|
89
106
|
|
|
90
|
-
|
|
91
|
-
-
|
|
92
|
-
|
|
93
|
-
- subscribe/event and response patterns if those are not available natively
|
|
94
|
-
- NATS without JetStream
|
|
95
|
-
- TLS setups, which are usually non-trivial and have just been tested automatically, but not in detail.
|
|
107
|
+
* **Choose the `mq-bridge` library** when you write the code that moves the data — in Rust, Python, or Node — and want batching, retries/DLQ/dedup, request-reply, CDC, and TLS behind one small API you run in-process. No separate scheduler, no daemon to operate.
|
|
108
|
+
* **Choose `mq-bridge-app` for zero-code ETL** when you'd rather move data A→B purely by config: define routes and endpoints in YAML/env, then use its **Postman-style UI** to send test messages and watch them flow — and **import Postman collections or AsyncAPI specs** through the UI to generate the config. Same reliability engine, no build step.
|
|
109
|
+
* **Choose a dedicated stream processor** when you need windowed joins or aggregations over time.
|
|
96
110
|
|
|
97
|
-
|
|
111
|
+
Either way it stays transport-first on purpose: it moves and routes data reliably rather than prescribing a domain model or a platform.
|
|
98
112
|
|
|
99
|
-
|
|
113
|
+
## Status
|
|
100
114
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
`mq-bridge` is young (created in 2025) but its reliability behavior is exercised by an automated integration + performance suite across **every** supported endpoint:
|
|
116
|
+
|
|
117
|
+
* Integration and performance tests cover all endpoints in both queue and subscriber modes, request-reply where supported, and protocol-specific behavior.
|
|
118
|
+
* All endpoints have automated integration tests that showed **no data loss during in-flight broker restarts**; MQTT publish-confirmation was hardened until a chaos test drove in-flight loss to **zero**.
|
|
119
|
+
* Postgres CDC ships with a **restart-safety integration test**: an un-acked, in-flight batch is redelivered after a database restart with no loss and no gap into already-acknowledged changes.
|
|
120
|
+
* Throughput is tracked continuously on a public [benchmark dashboard](https://marcomq.github.io/mq-bridge/dev/bench/); ETL/CDC-shaped, like-for-like scenarios and their fixed methodology are documented in [`benches/ETL_BENCHMARKS.md`](benches/ETL_BENCHMARKS.md).
|
|
121
|
+
|
|
122
|
+
Known rough edges (be deliberate here):
|
|
123
|
+
|
|
124
|
+
- old or very new broker-server versions, or unusual broker settings
|
|
125
|
+
- subscribe/event and response patterns where the backend has no native equivalent (emulated)
|
|
126
|
+
- NATS without JetStream
|
|
127
|
+
- **TLS**: one unified `TlsConfig` shape is reused across transports and exercised by automated tests; per-backend certificate matrices (self-signed / CA-signed / mTLS / expired-cert rejection) are being expanded — treat non-trivial TLS setups as needing your own verification for now.
|
|
109
128
|
|
|
110
|
-
All endpoints have automated integration tests and did not show data loss during simple in-flight broker restarts.
|
|
111
129
|
|
|
112
130
|
## Test Notes
|
|
113
131
|
|
|
@@ -157,6 +175,7 @@ The table below summarizes the capabilities and configuration for each backend:
|
|
|
157
175
|
| **MongoDB** | Set `change_stream: true` | Emulated (Metadata) | **Yes** (Unlock) |
|
|
158
176
|
| **MQTT** | Set `clean_session: true` | Emulated (Property) | Eventual (Skip Ack) |
|
|
159
177
|
| **NATS** | Set `subscriber_mode: true` | **Native** (Inbox) | **Yes** (JetStream Nak) |
|
|
178
|
+
| **Postgres CDC** | N/A (streams committed changes) | No | **Yes** (confirmed LSN not advanced) |
|
|
160
179
|
| **Redis Streams** | Set `subscriber_mode: true` | No | Eventual (PEL, un-acked) |
|
|
161
180
|
| **Sled** | Set `delete_after_read: false` | No | **Yes** (Tx Rollback) |
|
|
162
181
|
| **SQLx** | Not supported | No | Eventual (Skip Delete) |
|
|
@@ -169,6 +188,15 @@ The table below summarizes the capabilities and configuration for each backend:
|
|
|
169
188
|
* **Emulated**: Publishes a new message to a reply destination (specified by the `reply_to` metadata field) carrying a `correlation_id` metadata field.
|
|
170
189
|
* **Nack Support**: If "Yes", the backend supports explicit negative acknowledgement triggering redelivery. "Eventual" means redelivery depends on timeout or connection drop. "Simulated" is handled in-memory by the bridge.
|
|
171
190
|
|
|
191
|
+
### Database Sources: Change Capture vs Polling
|
|
192
|
+
Databases have no native pub/sub, so `mq-bridge` reads them as a source in one of two ways:
|
|
193
|
+
|
|
194
|
+
* **Change Data Capture (CDC)** — tails the database's own change log, capturing inserts **and** updates/deletes and resuming from a durable log position.
|
|
195
|
+
* On **PostgreSQL**, the `postgres_cdc` endpoint streams a logical-replication slot (`pgoutput`). It emits flat JSON rows tagged with `postgres.operation` metadata (`insert`/`update`/`delete`/`truncate`). Acking a batch confirms the LSN back to the server (`standby_status_update`); a nack (or an interrupted run) does **not** advance the confirmed LSN, so replication resumes from the last acknowledged position on reconnect — at-least-once, verified by a restart-safety integration test. Enable with the `postgres-cdc` feature; requires `wal_level = logical` and a publication. See below.
|
|
196
|
+
* On **MongoDB**, set `consume: capture_new` to watch an existing collection for changes from now on, or `consume: capture_all` to read the existing documents first and then keep capturing changes (no gap, at-least-once). It emits `insert`/`update`/`replace`/`delete` events tagged with `mongodb.operation` metadata and checkpoints progress under `cursor_id`. These use the change stream (full post-image via `updateLookup`) and need a replica set; on a standalone server `capture_all` falls back to an insert-only read.
|
|
197
|
+
* **Cursor polling** — pages an existing table by a monotonic `cursor_column` (`WHERE col > $last ORDER BY col ASC`), persisting the last read value under `cursor_id`. Captures **appends only** — updates and deletes are not observed. Available on **SQLx** (PostgreSQL / MySQL / MariaDB / SQLite) and **ClickHouse**; while idle the poll interval backs off exponentially between `polling_interval_ms` and `max_polling_interval_ms`. **SQLite** and **ClickHouse** are polling-only — they have no server-side change log.
|
|
198
|
+
|
|
199
|
+
|
|
172
200
|
### Response Endpoint
|
|
173
201
|
The `response` output endpoint sends a reply back to the original requester. This is useful for synchronous request-reply flows, for example HTTP-to-NATS-to-HTTP. Use `response: {}` as the output endpoint configuration.
|
|
174
202
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# ETL / CDC-comparable benchmark methodology
|
|
2
|
+
|
|
3
|
+
This suite exists so that mq-bridge can be evaluated **like-for-like** against
|
|
4
|
+
config-driven ETL/CDC tools for moving Postgres data — not against
|
|
5
|
+
microbenchmarks of unrelated primitives. Credibility is the asset here: every
|
|
6
|
+
number below is reproducible from a pinned workload and a pinned backend
|
|
7
|
+
version, and the methodology is stated up front rather than cherry-picked.
|
|
8
|
+
|
|
9
|
+
The existing criterion harness (`benches/performance_bench.rs`, published to the
|
|
10
|
+
[benchmark dashboard](https://marcomq.github.io/mq-bridge/dev/bench/)) already
|
|
11
|
+
measures per-backend batched throughput. This document defines the additional
|
|
12
|
+
**ETL/CDC-shaped scenarios** and the fixed parameters they run under.
|
|
13
|
+
|
|
14
|
+
## Fixed parameters (keep constant across runs and across tools)
|
|
15
|
+
|
|
16
|
+
| Parameter | Value |
|
|
17
|
+
| ---------------- | ------------------------------------------------- |
|
|
18
|
+
| Message payload | 256 B and 4 KiB JSON rows (report both) |
|
|
19
|
+
| Message count | 100_000 per run |
|
|
20
|
+
| Batch sizes | 1 (unbatched) and 128 (batched) |
|
|
21
|
+
| Concurrency | 1 and 4 route workers |
|
|
22
|
+
| Postgres | `postgres:16-alpine`, `wal_level=logical` |
|
|
23
|
+
| Warm-up | criterion default + 5_000-message pre-roll |
|
|
24
|
+
| Machine | record CPU model, cores, RAM in the published run |
|
|
25
|
+
|
|
26
|
+
Report the **backend version and the exact parameters next to every number**.
|
|
27
|
+
Never publish a single favourable figure without its methodology row.
|
|
28
|
+
|
|
29
|
+
## Scenarios
|
|
30
|
+
|
|
31
|
+
1. **Bulk-insert throughput** — `memory → sqlx(postgres)` sink, measured in
|
|
32
|
+
rows/second at batch sizes 1 and 128. Answers "how fast can mq-bridge load a
|
|
33
|
+
table". Uses `docker-compose/postgres_cdc.yml`.
|
|
34
|
+
|
|
35
|
+
2. **CDC event-to-sink latency** — insert a row into a captured table and
|
|
36
|
+
measure the time until the corresponding change event is delivered to a
|
|
37
|
+
`memory` sink through the `postgres_cdc` endpoint (p50 / p95 / p99). Answers
|
|
38
|
+
"how quickly does a change propagate". Uses `docker-compose/postgres_cdc.yml`.
|
|
39
|
+
|
|
40
|
+
3. **Batched vs. unbatched throughput** — the same source→sink route at
|
|
41
|
+
`batch_size: 1` vs `batch_size: 128`, per backend, to quantify the batching
|
|
42
|
+
lever the README documents. Runs on `memory` (no external service) and on
|
|
43
|
+
each broker that has a compose file.
|
|
44
|
+
|
|
45
|
+
## Running
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Batched-vs-unbatched (no external services)
|
|
49
|
+
cargo bench --features test-utils --bench performance_bench
|
|
50
|
+
|
|
51
|
+
# CDC latency + bulk-insert (needs Docker Postgres with logical replication)
|
|
52
|
+
docker compose -f tests/integration/docker-compose/postgres_cdc.yml up -d --wait
|
|
53
|
+
cargo bench --features "test-utils postgres-cdc" --bench performance_bench -- cdc
|
|
54
|
+
docker compose -f tests/integration/docker-compose/postgres_cdc.yml down -v
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Results
|
|
58
|
+
|
|
59
|
+
The scenarios are run through
|
|
60
|
+
[`mq-bridge-app`](https://github.com/marcomq/mq-bridge-app) (the zero-code, config-driven
|
|
61
|
+
path a real user takes) so the numbers are comparable to how other ETL/CDC tools publish
|
|
62
|
+
theirs. The run brief lives in [`mq-bridge-app-benchmark-prompt.md`](mq-bridge-app-benchmark-prompt.md).
|
|
63
|
+
|
|
64
|
+
> ⚠️ **Preliminary — measured on battery power.** The number below was measured on a
|
|
65
|
+
> laptop running on battery, where macOS throttles the CPU. It is **not yet authoritative**
|
|
66
|
+
> and is pending a re-run on AC (mains) power before publication. Treat it as a floor, not a
|
|
67
|
+
> confirmed figure.
|
|
68
|
+
|
|
69
|
+
**Environment:** Apple M1, 8 cores, 8 GB RAM, macOS (battery power) · via `mq-bridge-app` 0.2.6 · postgres:16
|
|
70
|
+
|
|
71
|
+
### Validated run — bulk copy Postgres → JSONL
|
|
72
|
+
|
|
73
|
+
| Scenario | Payload | Batch | Concurrency | Source → Sink | Result |
|
|
74
|
+
| --- | --- | --- | --- | --- | --- |
|
|
75
|
+
| Bulk-copy throughput | 7-col mixed-type rows, 1,000,000 rows | 1024 | 4 | postgres (keyset cursor) → file (JSONL) | **303,398 rows/s** (3.296 s) |
|
|
76
|
+
|
|
77
|
+
Command: `mq-bridge-app copy --from postgres://…?table=…&cursor_column=id --to file://…?format=raw --drain --batch-size 1024 --concurrency 4`, wall-clocked. Requires an index on the cursor column (`CREATE INDEX ON <table>(id)`) — without it the keyset-pagination reader (`WHERE id > $cursor ORDER BY id LIMIT batch`) does a full scan per batch (near-quadratic).
|
|
78
|
+
|
|
79
|
+
### Validated run — Postgres → JSONL vs. Meltano
|
|
80
|
+
|
|
81
|
+
Same source table, same Postgres instance, same machine, both one-shot full-table syncs to a local JSONL file — mq-bridge-app's `copy` CLI vs. Meltano's `tap-postgres` → `target-jsonl` (median of 5 timed runs each side).
|
|
82
|
+
|
|
83
|
+
| Scenario | Payload | Batch | Concurrency | Source → Sink | Result |
|
|
84
|
+
| --- | --- | --- | --- | --- | --- |
|
|
85
|
+
| mq-bridge-app `copy` | 256 B, 100,000 rows | 1024 | 1 | postgres (keyset cursor) → file (JSONL) | **197,628 rows/s** |
|
|
86
|
+
| Meltano (`tap-postgres` → `target-jsonl`) | 256 B, 100,000 rows | default Singer config | — | postgres → JSONL | 16,330 rows/s |
|
|
87
|
+
|
|
88
|
+
**~12.1x faster** than Meltano in this scenario. Full setup (including the Meltano project config) is in [mq-bridge-app's `benches/etl/README.md`](https://github.com/marcomq/mq-bridge-app/blob/dev/benches/etl/README.md#5--postgres--jsonl-vs-meltano-tap-postgres--target-jsonl).
|
|
89
|
+
|
|
90
|
+
### Validated run — CSV → JSONL vs. Meltano
|
|
91
|
+
|
|
92
|
+
Same seeded dataset both sides, same machine, one-shot full-file CSV → local JSONL (one JSON object per input row) — mq-bridge-app's `copy` CLI vs. Meltano's `tap-csv` → `target-jsonl`.
|
|
93
|
+
|
|
94
|
+
| Scenario | Payload | Batch | Concurrency | Source → Sink | Result |
|
|
95
|
+
| --- | --- | --- | --- | --- | --- |
|
|
96
|
+
| mq-bridge-app `copy` | 7-col mixed-type, 1,000,000 rows (~116 MiB) | 1024 | 1 | file (CSV) → file (JSONL) | **833,333 rows/s** |
|
|
97
|
+
| Meltano (`tap-csv` → `target-jsonl`) | 7-col mixed-type, 1,000,000 rows | default Singer config | — | file (CSV) → JSONL | ~19,500 rows/s |
|
|
98
|
+
|
|
99
|
+
**~43x faster** than Meltano in this scenario. Full setup is in [mq-bridge-app's `benches/etl/README.md`](https://github.com/marcomq/mq-bridge-app/blob/dev/benches/etl/README.md#6--csv--jsonl-vs-meltano-faucet-streams-headline-benchmark).
|
|
100
|
+
|
|
101
|
+
## Status
|
|
102
|
+
|
|
103
|
+
Scenario 3 (batched vs. unbatched) is covered by the existing harness. Scenarios
|
|
104
|
+
1 and 2 (bulk-insert, CDC latency) share the Docker Postgres environment with the
|
|
105
|
+
CDC integration tests (`tests/integration/postgres_cdc.rs`); their criterion
|
|
106
|
+
wiring is added and validated together with that Docker run so the published
|
|
107
|
+
numbers are real, not estimated.
|