mq-bridge-py-basic 0.3.4__tar.gz → 0.3.5__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.4 → mq_bridge_py_basic-0.3.5}/CONFIGURATION.md +2 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/Cargo.lock +91 -8
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/Cargo.toml +9 -3
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/PKG-INFO +1 -1
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/README.md +30 -3
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/benches/ETL_BENCHMARKS.md +18 -18
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/benches/performance_bench.rs +122 -64
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/mq-bridge.schema.json +158 -7
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/mq_bridge/config.py +6 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/mq_bridge/config.pyi +23 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/analysis/http/mq_bridge_http_profile.rs +188 -1
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/checkpoint.rs +198 -7
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/file.rs +85 -62
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/http.rs +104 -22
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/mod.rs +42 -0
- mq_bridge_py_basic-0.3.5/src/endpoints/object_store.rs +707 -0
- mq_bridge_py_basic-0.3.5/src/endpoints/postgres/VENDORED.md +81 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/postgres/mod.rs +11 -1
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/postgres/pgoutput/mod.rs +1 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/postgres/replication.rs +145 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/postgres/state.rs +1 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/zeromq.rs +322 -15
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/lib.rs +6 -1
- mq_bridge_py_basic-0.3.5/src/middleware/weak_join.rs +467 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/models.rs +131 -6
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/route.rs +243 -60
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/test_utils.rs +93 -20
- mq_bridge_py_basic-0.3.5/tests/integration/docker-compose/object_store.yml +15 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/mod.rs +2 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/mongodb.rs +229 -2
- mq_bridge_py_basic-0.3.5/tests/integration/object_store.rs +165 -0
- mq_bridge_py_basic-0.3.5/tests/integration/postgres_cdc.rs +430 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration_test.rs +71 -0
- mq_bridge_py_basic-0.3.4/src/middleware/weak_join.rs +0 -269
- mq_bridge_py_basic-0.3.4/tests/integration/postgres_cdc.rs +0 -209
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/workflows/benchmark.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/workflows/ci.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/workflows/ibm-mq.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/workflows/node.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/workflows/publish-node.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/workflows/publish-python.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/workflows/python.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/workflows/release.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.github/workflows/test-matrix.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/.gitignore +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/ARCHITECTURE.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/CONTRIBUTING.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/LICENSE +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/LICENSE.python +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/benches/router_bench.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/bindings-common/Cargo.toml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/bindings-common/src/lib.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/bindings-common/src/logging.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/build.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/claude.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/examples/ipc_worker_queue.yaml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/mq_bridge/__init__.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/mq_bridge/__init__.pyi +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/mq_bridge/py.typed +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/package-lock.json +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/pyproject.toml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/Cargo.toml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/LICENSE.python +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/README.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/analysis/bench_http_native.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/example.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/examples/bench_memory.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/examples/bench_memory.yaml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/examples/consume_pull.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/examples/dlt_source.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/examples/json_route.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/examples/memory.yaml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/examples/raw_route.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/pyproject-basic.toml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/pyproject-reduced.toml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/scripts/gen_config_types.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/src/lib.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/tests/test_config_types.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/tests/test_logging.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/tests/test_packaging.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/tests/test_performance_smoke.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/tests/test_public_api.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/tests/test_routes.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/python/mq-bridge-py/uv.lock +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/analysis/HTTP_STREAM_ENDPOINT_PLAN.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/analysis/benches/metadata_bench.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/analysis/debug_ibm_mq_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/analysis/http/analyze_xctrace_time_profile.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/analysis/http/http_flamegraph.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/analysis/http/run_http_ladder.sh +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/ci/format_bench_output.sh +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/httparena/README.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/httparena/frameworks/mq-bridge-py/Dockerfile +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/httparena/frameworks/mq-bridge-py/meta.json +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/httparena/frameworks/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/techempower/Python/mq-bridge-py/README.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/techempower/Python/mq-bridge-py/benchmark_config.json +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/techempower/Python/mq-bridge-py/mq-bridge-py.dockerfile +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/techempower/Python/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/techempower/README.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/techempower/postgres.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/techempower/seed.sql +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/techempower/verify.sh +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/the-benchmarker/README.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/the-benchmarker/python/mq-bridge-py/config.yaml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/the-benchmarker/python/mq-bridge-py/pyproject.toml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/scripts/the-benchmarker/python/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/canonical_message.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/command_handler.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/connection_registry.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/amqp.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/aws.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/clickhouse.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/fanout.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/grpc.proto +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/grpc.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/http_stream.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/ibm_mq.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/kafka.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/memory/endpoint.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/memory/ipc_unix.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/memory/ipc_windows.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/memory/memory_transport.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/memory/mod.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/memory/transport.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/mongodb.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/mqtt.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/nats.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/null.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/poll.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/postgres/pgoutput/decoder.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/postgres/pgoutput/messages.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/postgres/pgoutput/registry.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/postgres/pgoutput/values.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/reader.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/redis_streams.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/response.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/sled.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/sqlx.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/static_endpoint.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/stream_buffer.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/switch.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/endpoints/websocket.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/errors.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/event_handler.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/event_store.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/extensions.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/buffer.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/cookie_jar.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/deduplication.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/delay.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/dlq.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/limiter.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/metrics.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/mod.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/random_panic.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/middleware/retry.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/outcomes.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/publisher.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/response.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/traits.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/src/type_handler.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/README.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/armature_integration.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/amqp.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/aws.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/clickhouse.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/config.toml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/amqp.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/aws.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/clickhouse.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/ibm-mq-certs/client.crl +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/ibm-mq-certs/client.kdb +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/ibm-mq-certs/client.p12 +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/ibm-mq-certs/client.rdb +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/ibm-mq-certs/client.sth +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/ibm_mq.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/ibm_mq_tls.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/kafka-tls.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/kafka.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mariadb.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mongodb-replica.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mongodb-tls.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mongodb.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mosquitto.conf +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mosquitto_performance.conf +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mosquitto_persistence.conf +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mq_init.mqsc +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mq_init_tls.mqsc +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mqtt.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mqtt_performance.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/mysql.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/nats.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/postgres.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/postgres_cdc.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/docker-compose/redis.yml +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/file.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/grpc.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/grpc_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/http.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/http_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/ibm_mq.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/ibm_mq_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/ipc.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/kafka.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/kafka_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/logic_test.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/mariadb.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/memory.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/mongodb_raw.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/mongodb_tls.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/mqtt.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/mysql.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/nats.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/performance_static.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/postgres.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/redis_streams.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/route.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/scripts/gen_certs.sh +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/sqlite.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/tls_helpers.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/websocket.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/integration/zeromq.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/memory_leak_test.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/ref_test.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/sqlite_test.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/tls_example.rs +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/unit_tests.md +0 -0
- {mq_bridge_py_basic-0.3.4 → mq_bridge_py_basic-0.3.5}/tests/websocket_test.rs +0 -0
|
@@ -116,6 +116,8 @@ zeromq_pipeline:
|
|
|
116
116
|
url: "tcp://localhost:5556"
|
|
117
117
|
socket_type: "push"
|
|
118
118
|
bind: false
|
|
119
|
+
# format: "raw" # send/receive raw payload bytes per frame instead of JSON-wrapping (e.g. JPEG, Protobuf)
|
|
120
|
+
# format: "raw_framed" # like "raw" but prepends a JSON metadata frame so headers still travel. Default "json".
|
|
119
121
|
|
|
120
122
|
# Route 8: PostgreSQL via SQLx
|
|
121
123
|
sqlx_postgres_route:
|
|
@@ -1002,6 +1002,7 @@ dependencies = [
|
|
|
1002
1002
|
"iana-time-zone",
|
|
1003
1003
|
"js-sys",
|
|
1004
1004
|
"num-traits",
|
|
1005
|
+
"serde",
|
|
1005
1006
|
"wasm-bindgen",
|
|
1006
1007
|
"windows-link",
|
|
1007
1008
|
]
|
|
@@ -1279,6 +1280,16 @@ version = "2.5.0"
|
|
|
1279
1280
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1280
1281
|
checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
|
|
1281
1282
|
|
|
1283
|
+
[[package]]
|
|
1284
|
+
name = "crc-fast"
|
|
1285
|
+
version = "1.10.0"
|
|
1286
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1287
|
+
checksum = "e75b2483e97a5a7da73ac68a05b629f9c53cff58d8ed1c77866079e18b00dba5"
|
|
1288
|
+
dependencies = [
|
|
1289
|
+
"digest 0.10.7",
|
|
1290
|
+
"spin 0.10.1",
|
|
1291
|
+
]
|
|
1292
|
+
|
|
1282
1293
|
[[package]]
|
|
1283
1294
|
name = "crc32fast"
|
|
1284
1295
|
version = "1.5.0"
|
|
@@ -1771,9 +1782,9 @@ dependencies = [
|
|
|
1771
1782
|
|
|
1772
1783
|
[[package]]
|
|
1773
1784
|
name = "fast-uuid-v7"
|
|
1774
|
-
version = "0.1.
|
|
1785
|
+
version = "0.1.6"
|
|
1775
1786
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1776
|
-
checksum = "
|
|
1787
|
+
checksum = "e8eaa2987e8b03da0c1659e70392d20480a595047e3db7922094ffe364e9d7b0"
|
|
1777
1788
|
dependencies = [
|
|
1778
1789
|
"rand 0.9.4",
|
|
1779
1790
|
]
|
|
@@ -1826,7 +1837,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
|
|
1826
1837
|
dependencies = [
|
|
1827
1838
|
"futures-core",
|
|
1828
1839
|
"futures-sink",
|
|
1829
|
-
"spin",
|
|
1840
|
+
"spin 0.9.8",
|
|
1830
1841
|
]
|
|
1831
1842
|
|
|
1832
1843
|
[[package]]
|
|
@@ -1837,7 +1848,7 @@ checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be"
|
|
|
1837
1848
|
dependencies = [
|
|
1838
1849
|
"futures-core",
|
|
1839
1850
|
"futures-sink",
|
|
1840
|
-
"spin",
|
|
1851
|
+
"spin 0.9.8",
|
|
1841
1852
|
]
|
|
1842
1853
|
|
|
1843
1854
|
[[package]]
|
|
@@ -2344,6 +2355,12 @@ version = "1.0.3"
|
|
|
2344
2355
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2345
2356
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|
2346
2357
|
|
|
2358
|
+
[[package]]
|
|
2359
|
+
name = "humantime"
|
|
2360
|
+
version = "2.4.0"
|
|
2361
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2362
|
+
checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15"
|
|
2363
|
+
|
|
2347
2364
|
[[package]]
|
|
2348
2365
|
name = "hybrid-array"
|
|
2349
2366
|
version = "0.4.12"
|
|
@@ -2679,6 +2696,15 @@ dependencies = [
|
|
|
2679
2696
|
"either",
|
|
2680
2697
|
]
|
|
2681
2698
|
|
|
2699
|
+
[[package]]
|
|
2700
|
+
name = "itertools"
|
|
2701
|
+
version = "0.15.0"
|
|
2702
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2703
|
+
checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc"
|
|
2704
|
+
dependencies = [
|
|
2705
|
+
"either",
|
|
2706
|
+
]
|
|
2707
|
+
|
|
2682
2708
|
[[package]]
|
|
2683
2709
|
name = "itoa"
|
|
2684
2710
|
version = "1.0.18"
|
|
@@ -3154,7 +3180,7 @@ dependencies = [
|
|
|
3154
3180
|
|
|
3155
3181
|
[[package]]
|
|
3156
3182
|
name = "mq-bridge"
|
|
3157
|
-
version = "0.3.
|
|
3183
|
+
version = "0.3.5"
|
|
3158
3184
|
dependencies = [
|
|
3159
3185
|
"anyhow",
|
|
3160
3186
|
"arc-swap",
|
|
@@ -3187,6 +3213,7 @@ dependencies = [
|
|
|
3187
3213
|
"mongodb",
|
|
3188
3214
|
"mq-bridge",
|
|
3189
3215
|
"mqi",
|
|
3216
|
+
"object_store",
|
|
3190
3217
|
"once_cell",
|
|
3191
3218
|
"percent-encoding",
|
|
3192
3219
|
"pgwire-replication",
|
|
@@ -3227,7 +3254,7 @@ dependencies = [
|
|
|
3227
3254
|
|
|
3228
3255
|
[[package]]
|
|
3229
3256
|
name = "mq-bridge-bindings-common"
|
|
3230
|
-
version = "0.3.
|
|
3257
|
+
version = "0.3.5"
|
|
3231
3258
|
dependencies = [
|
|
3232
3259
|
"anyhow",
|
|
3233
3260
|
"fast-uuid-v7",
|
|
@@ -3241,7 +3268,7 @@ dependencies = [
|
|
|
3241
3268
|
|
|
3242
3269
|
[[package]]
|
|
3243
3270
|
name = "mq-bridge-node"
|
|
3244
|
-
version = "0.3.
|
|
3271
|
+
version = "0.3.5"
|
|
3245
3272
|
dependencies = [
|
|
3246
3273
|
"anyhow",
|
|
3247
3274
|
"async-trait",
|
|
@@ -3262,7 +3289,7 @@ dependencies = [
|
|
|
3262
3289
|
|
|
3263
3290
|
[[package]]
|
|
3264
3291
|
name = "mq-bridge-py"
|
|
3265
|
-
version = "0.3.
|
|
3292
|
+
version = "0.3.5"
|
|
3266
3293
|
dependencies = [
|
|
3267
3294
|
"anyhow",
|
|
3268
3295
|
"async-trait",
|
|
@@ -3490,6 +3517,46 @@ dependencies = [
|
|
|
3490
3517
|
"syn",
|
|
3491
3518
|
]
|
|
3492
3519
|
|
|
3520
|
+
[[package]]
|
|
3521
|
+
name = "object_store"
|
|
3522
|
+
version = "0.14.1"
|
|
3523
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
3524
|
+
checksum = "d354792e39fa5f0009e47623cf8b15b099bf9a652fa55c6f817fe28ac84fea50"
|
|
3525
|
+
dependencies = [
|
|
3526
|
+
"async-trait",
|
|
3527
|
+
"aws-lc-rs",
|
|
3528
|
+
"base64",
|
|
3529
|
+
"bytes",
|
|
3530
|
+
"chrono",
|
|
3531
|
+
"crc-fast",
|
|
3532
|
+
"form_urlencoded",
|
|
3533
|
+
"futures-channel",
|
|
3534
|
+
"futures-core",
|
|
3535
|
+
"futures-util",
|
|
3536
|
+
"http 1.4.2",
|
|
3537
|
+
"http-body-util",
|
|
3538
|
+
"httparse",
|
|
3539
|
+
"humantime",
|
|
3540
|
+
"hyper 1.10.1",
|
|
3541
|
+
"itertools 0.15.0",
|
|
3542
|
+
"md-5 0.11.0",
|
|
3543
|
+
"parking_lot 0.12.5",
|
|
3544
|
+
"percent-encoding",
|
|
3545
|
+
"quick-xml",
|
|
3546
|
+
"rand 0.10.1",
|
|
3547
|
+
"reqwest",
|
|
3548
|
+
"rustls-pki-types",
|
|
3549
|
+
"serde",
|
|
3550
|
+
"serde_json",
|
|
3551
|
+
"serde_urlencoded",
|
|
3552
|
+
"thiserror 2.0.18",
|
|
3553
|
+
"tokio",
|
|
3554
|
+
"tracing",
|
|
3555
|
+
"url",
|
|
3556
|
+
"wasm-bindgen-futures",
|
|
3557
|
+
"web-time",
|
|
3558
|
+
]
|
|
3559
|
+
|
|
3493
3560
|
[[package]]
|
|
3494
3561
|
name = "oid-registry"
|
|
3495
3562
|
version = "0.8.1"
|
|
@@ -4157,6 +4224,16 @@ dependencies = [
|
|
|
4157
4224
|
"winapi",
|
|
4158
4225
|
]
|
|
4159
4226
|
|
|
4227
|
+
[[package]]
|
|
4228
|
+
name = "quick-xml"
|
|
4229
|
+
version = "0.41.0"
|
|
4230
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
4231
|
+
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
|
|
4232
|
+
dependencies = [
|
|
4233
|
+
"memchr",
|
|
4234
|
+
"serde",
|
|
4235
|
+
]
|
|
4236
|
+
|
|
4160
4237
|
[[package]]
|
|
4161
4238
|
name = "quinn"
|
|
4162
4239
|
version = "0.11.9"
|
|
@@ -5328,6 +5405,12 @@ dependencies = [
|
|
|
5328
5405
|
"lock_api",
|
|
5329
5406
|
]
|
|
5330
5407
|
|
|
5408
|
+
[[package]]
|
|
5409
|
+
name = "spin"
|
|
5410
|
+
version = "0.10.1"
|
|
5411
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
5412
|
+
checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3"
|
|
5413
|
+
|
|
5331
5414
|
[[package]]
|
|
5332
5415
|
name = "spki"
|
|
5333
5416
|
version = "0.7.3"
|
|
@@ -16,13 +16,13 @@ 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.5"
|
|
20
20
|
|
|
21
21
|
[workspace.dependencies]
|
|
22
22
|
anyhow = "1.0"
|
|
23
23
|
async-trait = "0.1"
|
|
24
24
|
bytes = "1"
|
|
25
|
-
fast-uuid-v7 = "0.1.
|
|
25
|
+
fast-uuid-v7 = "0.1.6"
|
|
26
26
|
serde = "1.0"
|
|
27
27
|
serde_json = "1.0"
|
|
28
28
|
serde_yaml_ng = "0.10"
|
|
@@ -97,6 +97,10 @@ lapin = { version = "4.10", default-features = false, features = ["tokio", "rust
|
|
|
97
97
|
# MongoDB
|
|
98
98
|
mongodb = { version = "3.6", optional = true, features = ["cert-key-password"] }
|
|
99
99
|
|
|
100
|
+
# Object store (checkpoint backend): cloud REST via reqwest, not aws-sdk. 0.12 is the
|
|
101
|
+
# last release supporting rust-version 1.75 (0.14 needs 1.85).
|
|
102
|
+
object_store = { version = "0.14", optional = true, default-features = false, features = ["aws", "gcp", "azure", "http"] }
|
|
103
|
+
|
|
100
104
|
# NATS
|
|
101
105
|
async-nats = { version = "0.49", optional = true }
|
|
102
106
|
|
|
@@ -180,7 +184,7 @@ middleware = ["metrics", "dedup"]
|
|
|
180
184
|
# runtime the client is loaded lazily only when an ibm-mq endpoint is opened
|
|
181
185
|
# (clear error if the client isn't installed). Use `ibm-mq-static` instead for
|
|
182
186
|
# static linking.
|
|
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"]
|
|
187
|
+
full = ["middleware", "mongodb", "kafka", "amqp", "nats", "mqtt", "http", "aws", "zeromq", "redis-streams", "sled", "grpc", "sqlx", "clickhouse", "websocket", "postgres-cdc", "object-store", "rustls-aws-lc", "ibm-mq"]
|
|
184
188
|
|
|
185
189
|
# Functionality features
|
|
186
190
|
dedup = ["sled"]
|
|
@@ -229,6 +233,8 @@ redis-streams = ["dep:redis"]
|
|
|
229
233
|
grpc = ["tonic", "prost", "tokio-stream", "tonic-prost", "tonic-prost-build", "protoc-bin-vendored", "rustls"]
|
|
230
234
|
sqlx = ["dep:sqlx", "url"]
|
|
231
235
|
clickhouse = ["dep:reqwest", "dep:flate2", "url"]
|
|
236
|
+
# Cloud object-store checkpoint backend (S3/GCS/Azure/R2). Talks cloud REST via reqwest.
|
|
237
|
+
object-store = ["dep:object_store", "url"]
|
|
232
238
|
# Postgres logical-replication CDC (pgoutput). Reuses the sqlx dependency for the
|
|
233
239
|
# slot-lifecycle control plane; the streaming wire protocol is pgwire-replication.
|
|
234
240
|
# Needs Rust 1.88 (pgwire-replication MSRV), like the amqp feature gate.
|
|
@@ -25,7 +25,7 @@ If you need to move data reliably between systems and you write code (Rust, Pyth
|
|
|
25
25
|
|
|
26
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
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.
|
|
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, cloud object storage (S3 / GCS / Azure), IBM MQ, files, and in-memory channels — all behind the same `receive_batch` / `send_batch` shape.
|
|
29
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
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
31
|
* **Reliability middleware, not a framework**: retries, dead-letter queues, deduplication, rate limiting, and cookie/session persistence wrap any endpoint.
|
|
@@ -33,7 +33,7 @@ If you need to move data reliably between systems and you write code (Rust, Pyth
|
|
|
33
33
|
* **TLS everywhere, one config shape**: a single `TlsConfig` block (CA bundle, client cert/key for mTLS, insecure-skip) is reused across transports.
|
|
34
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
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 ~
|
|
36
|
+
> **Throughput & footprint.** In our own benchmarks, the same engine — driven the zero-code way through [`mq-bridge-app`](https://github.com/marcomq/mq-bridge-app) — sustained ~267,000 rows/s copying 1,000,000 rows from Postgres to JSONL on commodity hardware at **~20 MiB peak RSS**, 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** at the same ~20 MiB, about **~43x faster** and **~22x leaner in memory** than Meltano (`tap-csv` → `target-jsonl`, ~19,500 rows/s / ~444 MiB). Full setup, methodology, and the exact parameters are in [`benches/ETL_BENCHMARKS.md`](benches/ETL_BENCHMARKS.md).
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
## Language Bindings
|
|
@@ -72,7 +72,7 @@ For implementation details and quick start examples for each usage type, see the
|
|
|
72
72
|
|
|
73
73
|
## Features
|
|
74
74
|
|
|
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.
|
|
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, cloud object storage (S3 / GCS / Azure), AWS (SQS/SNS), IBM MQ, and in-memory channels.
|
|
76
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`).
|
|
77
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.
|
|
78
78
|
>
|
|
@@ -197,6 +197,33 @@ Databases have no native pub/sub, so `mq-bridge` reads them as a source in one o
|
|
|
197
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
198
|
|
|
199
199
|
|
|
200
|
+
### Cloud Object Storage (S3 / GCS / Azure)
|
|
201
|
+
The `object_store` endpoint (alias `s3`) reads and writes cloud object stores — Amazon S3, Google Cloud Storage, Azure Blob, Cloudflare R2, and anything else the [`object_store`](https://crates.io/crates/object_store) crate speaks — behind the same `receive_batch` / `send_batch` API. Enable it with the `object-store` feature. Credentials and backend options are read from the process environment (`AWS_ACCESS_KEY_ID`, `AWS_ENDPOINT`, `AWS_REGION`, `GOOGLE_SERVICE_ACCOUNT`, `AZURE_STORAGE_ACCOUNT`, ...); the URL scheme picks the backend (`s3://`, `gs://`, `az://`).
|
|
202
|
+
|
|
203
|
+
* **As a sink**, each flushed batch is encoded with the same file endpoint formats (`normal` JSONL, `json`, `text`, `raw`) and written as **one immutable object** at `<prefix>/[YYYY/MM/DD/]<uuidv7>.<ext>`. Objects are write-once — nothing is appended or mutated. The uuidv7 name already sorts by write time; the optional `date_partition` prefix (on by default, derived from that same id's timestamp) is a readability / lifecycle-rule convenience.
|
|
204
|
+
* **As a source**, objects under the prefix are listed in key order, fetched whole, split on the delimiter, and emitted. Progress is a durable cursor holding the last fully-acked object key: set `cursor_id` and an external `checkpoint_store` (`file://`, `s3://`, `postgres://`, `mongodb://`) so a restart resumes without re-emitting. Objects are **never deleted or rewritten** — resume is non-destructive and at-least-once at object granularity (a nacked batch is redelivered; the cursor only advances once an object is fully acked). `csv` is supported on the source only.
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
archive_to_s3:
|
|
208
|
+
input:
|
|
209
|
+
memory: { topic: "events" }
|
|
210
|
+
output:
|
|
211
|
+
object_store:
|
|
212
|
+
url: "s3://my-bucket/events"
|
|
213
|
+
format: normal # JSONL; also json / text / raw
|
|
214
|
+
|
|
215
|
+
replay_from_s3:
|
|
216
|
+
input:
|
|
217
|
+
object_store:
|
|
218
|
+
url: "s3://my-bucket/events"
|
|
219
|
+
cursor_id: "replayer-1"
|
|
220
|
+
checkpoint_store: "file:///var/lib/mqb/s3-cursor.json"
|
|
221
|
+
output:
|
|
222
|
+
nats: { subject: "events.replay", url: "nats://localhost:4222" }
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
> Point `checkpoint_store` at a **different** bucket or prefix than the source reads; a cursor object written under the source prefix would be listed and re-read as data (the source rejects an overlapping object-store checkpoint location).
|
|
226
|
+
|
|
200
227
|
### Response Endpoint
|
|
201
228
|
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.
|
|
202
229
|
|
|
@@ -61,14 +61,14 @@ The scenarios are run through
|
|
|
61
61
|
path a real user takes) so the numbers are comparable to how other ETL/CDC tools publish
|
|
62
62
|
theirs. The run brief lives in [`mq-bridge-app-benchmark-prompt.md`](mq-bridge-app-benchmark-prompt.md).
|
|
63
63
|
|
|
64
|
-
> ⚠️ **Preliminary — measured on battery power.** The
|
|
65
|
-
> laptop running on battery, where macOS throttles the CPU.
|
|
66
|
-
> and
|
|
67
|
-
> confirmed
|
|
64
|
+
> ⚠️ **Preliminary — measured on battery power.** The numbers below were measured on a
|
|
65
|
+
> laptop running on battery, where macOS throttles the CPU. They are **not yet authoritative**
|
|
66
|
+
> and are pending a re-run on AC (mains) power before publication. Treat them as a floor, not
|
|
67
|
+
> confirmed figures.
|
|
68
68
|
|
|
69
69
|
**Environment:** Apple M1, 8 cores, 8 GB RAM, macOS (battery power) · via `mq-bridge-app` 0.2.6 · postgres:16
|
|
70
70
|
|
|
71
|
-
###
|
|
71
|
+
### Preliminary run — bulk copy Postgres → JSONL
|
|
72
72
|
|
|
73
73
|
| Scenario | Payload | Batch | Concurrency | Source → Sink | Result |
|
|
74
74
|
| --- | --- | --- | --- | --- | --- |
|
|
@@ -76,27 +76,27 @@ theirs. The run brief lives in [`mq-bridge-app-benchmark-prompt.md`](mq-bridge-a
|
|
|
76
76
|
|
|
77
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
78
|
|
|
79
|
-
###
|
|
79
|
+
### Preliminary run — Postgres → JSONL vs. Meltano
|
|
80
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
|
|
81
|
+
Same source table (`bench`: 1,000,000 rows, 7 mixed-type columns), 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 timed runs each side, row-count parity verified).
|
|
82
82
|
|
|
83
|
-
| Scenario | Payload | Batch | Concurrency | Source → Sink |
|
|
84
|
-
| --- | --- | --- | --- | --- | --- |
|
|
85
|
-
| mq-bridge-app `copy` |
|
|
86
|
-
| Meltano (`tap-postgres` → `target-jsonl`) |
|
|
83
|
+
| Scenario | Payload | Batch | Concurrency | Source → Sink | Throughput | Peak RSS |
|
|
84
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
85
|
+
| mq-bridge-app `copy` | 7-col, 1,000,000 rows | 1024 | 1 | postgres (keyset cursor) → file (JSONL) | **266,951 rows/s** | 19.9 MiB |
|
|
86
|
+
| Meltano (`tap-postgres` → `target-jsonl`) | 7-col, 1,000,000 rows | default Singer config | — | postgres → JSONL | 15,356 rows/s | 599.7 MiB |
|
|
87
87
|
|
|
88
|
-
**~
|
|
88
|
+
**~17.4x faster and ~30x leaner in peak memory** 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
89
|
|
|
90
|
-
###
|
|
90
|
+
### Preliminary run — CSV → JSONL vs. Meltano
|
|
91
91
|
|
|
92
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
93
|
|
|
94
|
-
| Scenario | Payload | Batch | Concurrency | Source → Sink |
|
|
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 |
|
|
94
|
+
| Scenario | Payload | Batch | Concurrency | Source → Sink | Throughput | Peak RSS |
|
|
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** | 20.0 MiB |
|
|
97
|
+
| Meltano (`tap-csv` → `target-jsonl`) | 7-col mixed-type, 1,000,000 rows | default Singer config | — | file (CSV) → JSONL | ~19,500 rows/s | 443.8 MiB |
|
|
98
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
|
|
99
|
+
**~43x faster and ~22x leaner in peak memory** 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).
|
|
100
100
|
|
|
101
101
|
## Status
|
|
102
102
|
|
|
@@ -360,7 +360,7 @@ pub mod aws_helper {
|
|
|
360
360
|
pub mod zeromq_helper {
|
|
361
361
|
use super::PERF_TEST_MESSAGE_COUNT;
|
|
362
362
|
use mq_bridge::endpoints::zeromq::{ZeroMqConsumer, ZeroMqPublisher};
|
|
363
|
-
use mq_bridge::models::{ZeroMqConfig, ZeroMqSocketType};
|
|
363
|
+
use mq_bridge::models::{ZeroMqConfig, ZeroMqFormat, ZeroMqSocketType};
|
|
364
364
|
use mq_bridge::traits::{MessageConsumer, MessagePublisher};
|
|
365
365
|
use once_cell::sync::Lazy;
|
|
366
366
|
use rand::RngExt;
|
|
@@ -381,6 +381,7 @@ pub mod zeromq_helper {
|
|
|
381
381
|
bind: false,
|
|
382
382
|
internal_buffer_size: Some(PERF_TEST_MESSAGE_COUNT + 1),
|
|
383
383
|
topic: None,
|
|
384
|
+
format: ZeroMqFormat::Json,
|
|
384
385
|
};
|
|
385
386
|
Arc::new(ZeroMqPublisher::new(&config).await.unwrap())
|
|
386
387
|
}
|
|
@@ -395,6 +396,7 @@ pub mod zeromq_helper {
|
|
|
395
396
|
bind: true,
|
|
396
397
|
internal_buffer_size: Some(PERF_TEST_MESSAGE_COUNT + 1),
|
|
397
398
|
topic: None,
|
|
399
|
+
format: ZeroMqFormat::Json,
|
|
398
400
|
};
|
|
399
401
|
Arc::new(Mutex::new(ZeroMqConsumer::new(&config).await.unwrap()))
|
|
400
402
|
}
|
|
@@ -595,83 +597,88 @@ pub mod file_delete_helper {
|
|
|
595
597
|
#[cfg(feature = "http")]
|
|
596
598
|
pub mod http_helper {
|
|
597
599
|
use mq_bridge::endpoints::http::{HttpConsumer, HttpPublisher};
|
|
598
|
-
use mq_bridge::endpoints::memory::{MemoryConsumer, MemoryPublisher};
|
|
599
600
|
use mq_bridge::models::HttpConfig;
|
|
600
601
|
use mq_bridge::traits::{MessageConsumer, MessageDisposition, MessagePublisher};
|
|
601
|
-
use once_cell::sync::Lazy;
|
|
602
602
|
use std::sync::Arc;
|
|
603
|
-
use std::sync::Mutex as StdMutex;
|
|
604
|
-
use tokio::sync::Mutex;
|
|
605
|
-
|
|
606
|
-
static CURRENT_URL: Lazy<StdMutex<String>> = Lazy::new(|| StdMutex::new(String::new()));
|
|
607
603
|
|
|
608
|
-
|
|
604
|
+
/// HTTP is a synchronous request/reply transport, so it does not fit the generic
|
|
605
|
+
/// write-then-read harness: that fills a buffer during the write phase and drains it during
|
|
606
|
+
/// the read phase, but for HTTP the buffer is an in-process channel — so "read" measures
|
|
607
|
+
/// memory, not the transport. There is no independent read for request/reply; the POST is
|
|
608
|
+
/// both the write and the read. We therefore measure the *coupled* round-trip throughput.
|
|
609
|
+
///
|
|
610
|
+
/// This sets up a reliable HTTP server (a 200 is returned only once the message is
|
|
611
|
+
/// committed) whose consumer is continuously drained by a background task that commits
|
|
612
|
+
/// every message. The returned publisher thus completes a full network round-trip on every
|
|
613
|
+
/// send, so timing sends yields the true HTTP throughput. Abort the returned handle to stop
|
|
614
|
+
/// the drain task, then await it (see `finish_drain`) so receive/commit failures surface.
|
|
615
|
+
#[allow(clippy::type_complexity)]
|
|
616
|
+
pub async fn setup_coupled() -> (
|
|
617
|
+
Arc<dyn MessagePublisher>,
|
|
618
|
+
tokio::task::JoinHandle<anyhow::Result<()>>,
|
|
619
|
+
) {
|
|
609
620
|
#[cfg(feature = "rustls")]
|
|
610
621
|
crate::ensure_rustls_installed();
|
|
611
622
|
|
|
612
|
-
let
|
|
623
|
+
let consumer_config = HttpConfig {
|
|
613
624
|
url: "127.0.0.1:0".to_string(),
|
|
614
|
-
|
|
615
|
-
internal_buffer_size: Some(super::PERF_TEST_MESSAGE_COUNT * 2),
|
|
625
|
+
internal_buffer_size: Some(super::PERF_TEST_CONCURRENCY * 4),
|
|
616
626
|
concurrency_limit: Some(super::PERF_TEST_CONCURRENCY * 2),
|
|
617
627
|
request_timeout_ms: Some(10000),
|
|
618
|
-
fire_and_forget: false, //
|
|
628
|
+
fire_and_forget: false, // reliable: 200 is sent only after commit -> real round-trip
|
|
619
629
|
..Default::default()
|
|
620
630
|
};
|
|
621
|
-
|
|
622
|
-
let mut http_consumer = HttpConsumer::new(&http_config)
|
|
631
|
+
let mut consumer = HttpConsumer::new(&consumer_config)
|
|
623
632
|
.await
|
|
624
633
|
.expect("Failed to create HttpConsumer");
|
|
625
|
-
let addr =
|
|
634
|
+
let addr = consumer
|
|
626
635
|
.bound_addr()
|
|
627
636
|
.expect("HttpConsumer should have bound addr");
|
|
628
637
|
let url = format!("http://{}", addr);
|
|
629
638
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
topic,
|
|
640
|
-
capacity: Some(super::PERF_TEST_MESSAGE_COUNT * 10),
|
|
641
|
-
..Default::default()
|
|
642
|
-
};
|
|
643
|
-
let mem_publisher = MemoryPublisher::new(&mem_config).unwrap();
|
|
644
|
-
let mem_consumer = MemoryConsumer::new(&mem_config).unwrap();
|
|
645
|
-
|
|
646
|
-
// Background task to bridge Http -> Memory.
|
|
647
|
-
// We only ACK the HTTP request once the message is safely accepted by the memory queue.
|
|
648
|
-
tokio::spawn(async move {
|
|
649
|
-
while let Ok(batch) = http_consumer.receive_batch(100).await {
|
|
639
|
+
// Drain + commit so every POST gets its 200 (this is what couples the round-trip).
|
|
640
|
+
// Propagate receive/commit failures instead of swallowing them, so a broken round-trip
|
|
641
|
+
// fails the benchmark rather than reporting bogus throughput.
|
|
642
|
+
let drain = tokio::spawn(async move {
|
|
643
|
+
loop {
|
|
644
|
+
let batch = consumer
|
|
645
|
+
.receive_batch(128)
|
|
646
|
+
.await
|
|
647
|
+
.map_err(|e| anyhow::anyhow!("drain receive_batch failed: {e}"))?;
|
|
650
648
|
let count = batch.messages.len();
|
|
651
|
-
if count
|
|
652
|
-
|
|
649
|
+
if count == 0 {
|
|
650
|
+
continue;
|
|
653
651
|
}
|
|
652
|
+
(batch.commit)(vec![MessageDisposition::Ack; count])
|
|
653
|
+
.await
|
|
654
|
+
.map_err(|e| anyhow::anyhow!("drain commit failed: {e}"))?;
|
|
654
655
|
}
|
|
655
656
|
});
|
|
656
657
|
|
|
657
|
-
Arc::new(
|
|
658
|
+
let publisher: Arc<dyn MessagePublisher> = Arc::new(
|
|
659
|
+
HttpPublisher::new(&HttpConfig {
|
|
660
|
+
url,
|
|
661
|
+
request_timeout_ms: Some(10000),
|
|
662
|
+
pool_idle_timeout_ms: Some(1000),
|
|
663
|
+
tcp_keepalive_ms: Some(1000),
|
|
664
|
+
..Default::default()
|
|
665
|
+
})
|
|
666
|
+
.await
|
|
667
|
+
.unwrap(),
|
|
668
|
+
);
|
|
669
|
+
(publisher, drain)
|
|
658
670
|
}
|
|
659
671
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
pool_idle_timeout_ms: Some(1000),
|
|
671
|
-
tcp_keepalive_ms: Some(1000),
|
|
672
|
-
..Default::default()
|
|
673
|
-
};
|
|
674
|
-
Arc::new(HttpPublisher::new(&config).await.unwrap())
|
|
672
|
+
/// Stop the drain task and surface any failure. Aborts the task, then awaits it: an
|
|
673
|
+
/// expected cancellation is fine, but a task error or panic fails the benchmark.
|
|
674
|
+
pub async fn finish_drain(drain: tokio::task::JoinHandle<anyhow::Result<()>>) {
|
|
675
|
+
drain.abort();
|
|
676
|
+
match drain.await {
|
|
677
|
+
Ok(Ok(())) => {}
|
|
678
|
+
Ok(Err(e)) => panic!("http drain task failed: {e}"),
|
|
679
|
+
Err(e) if e.is_cancelled() => {}
|
|
680
|
+
Err(e) => std::panic::resume_unwind(e.into_panic()),
|
|
681
|
+
}
|
|
675
682
|
}
|
|
676
683
|
}
|
|
677
684
|
|
|
@@ -1127,17 +1134,68 @@ fn performance_benchmarks(c: &mut Criterion) {
|
|
|
1127
1134
|
PERF_TEST_CONCURRENCY,
|
|
1128
1135
|
std::time::Duration::from_millis(1000)
|
|
1129
1136
|
);
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1137
|
+
// HTTP is request/reply, so it can't be measured as decoupled write-then-read like the
|
|
1138
|
+
// other endpoints (its "read" would just drain an in-process buffer at memory speed).
|
|
1139
|
+
// Measure the coupled round-trip throughput and report it as both write and read.
|
|
1140
|
+
#[cfg(feature = "http")]
|
|
1141
|
+
if mq_bridge::test_utils::should_run_benchmark("http") {
|
|
1142
|
+
group.bench_function("http_batch", |b| {
|
|
1143
|
+
b.to_async(&rt).iter_custom(|iters| async move {
|
|
1144
|
+
let (publisher, drain) = http_helper::setup_coupled().await;
|
|
1145
|
+
tokio::time::sleep(Duration::from_millis(200)).await;
|
|
1146
|
+
let mut total = Duration::ZERO;
|
|
1147
|
+
for _ in 0..iters {
|
|
1148
|
+
total += mq_bridge::test_utils::measure_write_performance(
|
|
1149
|
+
"http_batch",
|
|
1150
|
+
std::sync::Arc::clone(&publisher),
|
|
1151
|
+
PERF_TEST_MESSAGE_COUNT,
|
|
1152
|
+
PERF_TEST_CONCURRENCY,
|
|
1153
|
+
)
|
|
1154
|
+
.await;
|
|
1155
|
+
}
|
|
1156
|
+
http_helper::finish_drain(drain).await;
|
|
1157
|
+
let msgs_per_sec =
|
|
1158
|
+
(iters as f64 * PERF_TEST_MESSAGE_COUNT as f64) / total.as_secs_f64();
|
|
1159
|
+
let mut results = BENCH_RESULTS.lock().await;
|
|
1160
|
+
let stats = results.entry("http".to_string()).or_default();
|
|
1161
|
+
stats.write_performance = msgs_per_sec;
|
|
1162
|
+
stats.read_performance = msgs_per_sec;
|
|
1163
|
+
println!(
|
|
1164
|
+
"\nhttp batch (coupled round-trip): {:.2} msgs/sec",
|
|
1165
|
+
msgs_per_sec
|
|
1166
|
+
);
|
|
1167
|
+
total
|
|
1168
|
+
})
|
|
1169
|
+
});
|
|
1170
|
+
group.bench_function("http_single", |b| {
|
|
1171
|
+
b.to_async(&rt).iter_custom(|iters| async move {
|
|
1172
|
+
let (publisher, drain) = http_helper::setup_coupled().await;
|
|
1173
|
+
tokio::time::sleep(Duration::from_millis(200)).await;
|
|
1174
|
+
let mut total = Duration::ZERO;
|
|
1175
|
+
for _ in 0..iters {
|
|
1176
|
+
total += mq_bridge::test_utils::measure_single_write_performance(
|
|
1177
|
+
"http_single",
|
|
1178
|
+
std::sync::Arc::clone(&publisher),
|
|
1179
|
+
PERF_TEST_MESSAGE_COUNT,
|
|
1180
|
+
PERF_TEST_CONCURRENCY,
|
|
1181
|
+
)
|
|
1182
|
+
.await;
|
|
1183
|
+
}
|
|
1184
|
+
http_helper::finish_drain(drain).await;
|
|
1185
|
+
let msgs_per_sec =
|
|
1186
|
+
(iters as f64 * PERF_TEST_MESSAGE_COUNT as f64) / total.as_secs_f64();
|
|
1187
|
+
let mut results = BENCH_RESULTS.lock().await;
|
|
1188
|
+
let stats = results.entry("http".to_string()).or_default();
|
|
1189
|
+
stats.single_write_performance = msgs_per_sec;
|
|
1190
|
+
stats.single_read_performance = msgs_per_sec;
|
|
1191
|
+
println!(
|
|
1192
|
+
"\nhttp single (coupled round-trip): {:.2} msgs/sec",
|
|
1193
|
+
msgs_per_sec
|
|
1194
|
+
);
|
|
1195
|
+
total
|
|
1196
|
+
})
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1141
1199
|
bench_backend!(
|
|
1142
1200
|
"websocket",
|
|
1143
1201
|
"websocket",
|