mq-bridge-py-basic 0.2.21__tar.gz → 0.3.0__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.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/ci.yml +54 -79
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/publish-node.yml +43 -3
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/CONFIGURATION.md +80 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/Cargo.lock +3 -3
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/Cargo.toml +1 -1
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/PKG-INFO +1 -1
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/README.md +2 -2
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq-bridge.schema.json +48 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/config.pyi +6 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/src/lib.rs +12 -3
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/tests/test_config_types.py +7 -2
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/httparena/README.md +1 -1
- mq_bridge_py_basic-0.3.0/src/connection_registry.rs +280 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/amqp.rs +21 -17
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/aws.rs +5 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/file.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/grpc.rs +33 -2
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/http.rs +66 -31
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/ibm_mq.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/kafka.rs +57 -13
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/endpoint.rs +176 -48
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/mongodb.rs +33 -1
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/mqtt.rs +8 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/nats.rs +39 -5
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/sled.rs +5 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/sqlx.rs +41 -1
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/static_endpoint.rs +4 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/stream_buffer.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/websocket.rs +13 -31
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/zeromq.rs +5 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/event_store.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/lib.rs +1 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/buffer.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/cookie_jar.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/deduplication.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/delay.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/limiter.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/metrics.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/random_panic.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/weak_join.rs +3 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/models.rs +30 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/route.rs +213 -13
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/test_utils.rs +10 -1
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/traits.rs +176 -43
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/http_tls.rs +1 -1
- mq_bridge_py_basic-0.3.0/tests/memory_leak_test.rs +134 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/benchmark.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/ibm-mq.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/node.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/publish-python.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/python.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/release.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/test-matrix.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.gitignore +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/ARCHITECTURE.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/CONTRIBUTING.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/LICENSE +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/LICENSE.python +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/benches/performance_bench.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/benches/router_bench.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/build.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/claude.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/examples/ipc_worker_queue.yaml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/__init__.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/__init__.pyi +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/config.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/py.typed +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/package-lock.json +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/pyproject.toml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/Cargo.toml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/LICENSE.python +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/README.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/analysis/bench_http_native.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/example.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/bench_memory.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/bench_memory.yaml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/json_route.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/memory.yaml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/raw_route.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/pyproject-basic.toml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/scripts/gen_config_types.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/tests/test_packaging.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/tests/test_performance_smoke.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/tests/test_public_api.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/tests/test_routes.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/uv.lock +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/HTTP_STREAM_ENDPOINT_PLAN.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/benches/metadata_bench.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/debug_ibm_mq_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/http/analyze_xctrace_time_profile.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/http/http_flamegraph.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/http/mq_bridge_http_profile.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/http/run_http_ladder.sh +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/httparena/frameworks/mq-bridge-py/Dockerfile +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/httparena/frameworks/mq-bridge-py/meta.json +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/httparena/frameworks/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/Python/mq-bridge-py/README.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/Python/mq-bridge-py/benchmark_config.json +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/Python/mq-bridge-py/mq-bridge-py.dockerfile +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/Python/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/README.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/postgres.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/seed.sql +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/verify.sh +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/the-benchmarker/README.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/the-benchmarker/python/mq-bridge-py/config.yaml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/the-benchmarker/python/mq-bridge-py/pyproject.toml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/the-benchmarker/python/mq-bridge-py/server.py +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/canonical_message.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/command_handler.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/fanout.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/grpc.proto +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/http_stream.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/ipc_unix.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/ipc_windows.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/memory_transport.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/mod.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/transport.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/mod.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/null.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/reader.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/response.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/switch.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/errors.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/event_handler.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/extensions.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/dlq.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/mod.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/retry.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/outcomes.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/publisher.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/response.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/type_handler.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/README.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/armature_integration.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/amqp.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/aws.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/config.toml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/amqp.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/aws.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/ibm-mq-certs/client.crl +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/ibm-mq-certs/client.kdb +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/ibm-mq-certs/client.p12 +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/ibm-mq-certs/client.rdb +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/ibm-mq-certs/client.sth +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/ibm_mq.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/ibm_mq_tls.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/kafka-tls.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/kafka.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mariadb.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mongodb-replica.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mongodb-tls.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mongodb.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mosquitto.conf +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mosquitto_performance.conf +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mosquitto_persistence.conf +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mq_init.mqsc +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mq_init_tls.mqsc +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mqtt.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mqtt_performance.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mysql.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/nats.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/postgres.yml +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/file.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/grpc.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/grpc_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/http.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/ibm_mq.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/ibm_mq_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/ipc.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/kafka.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/kafka_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/logic_test.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mariadb.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/memory.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mod.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mongodb.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mongodb_raw.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mongodb_tls.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mqtt.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mysql.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/nats.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/performance_static.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/postgres.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/route.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/scripts/gen_certs.sh +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/sqlite.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/tls_helpers.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/websocket.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/zeromq.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration_test.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/ref_test.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/sqlite_test.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/tls_example.rs +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/unit_tests.md +0 -0
- {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/websocket_test.rs +0 -0
|
@@ -23,17 +23,13 @@ jobs:
|
|
|
23
23
|
with:
|
|
24
24
|
components: rustfmt, clippy
|
|
25
25
|
|
|
26
|
-
- name: Cache cargo
|
|
27
|
-
uses:
|
|
26
|
+
- name: Cache cargo build
|
|
27
|
+
uses: Swatinem/rust-cache@v2
|
|
28
28
|
with:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
target/
|
|
34
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
35
|
-
restore-keys: |
|
|
36
|
-
${{ runner.os }}-cargo-
|
|
29
|
+
# Differentiates the matrix `check` job per feature set; evaluates to an
|
|
30
|
+
# empty string (no-op) in non-matrix jobs. rust-cache already keys on the
|
|
31
|
+
# job id, OS, rustc version, and lockfiles automatically.
|
|
32
|
+
key: ${{ matrix.features }}
|
|
37
33
|
|
|
38
34
|
- name: Check formatting
|
|
39
35
|
run: cargo fmt --all -- --check
|
|
@@ -65,24 +61,20 @@ jobs:
|
|
|
65
61
|
- name: Install Rust toolchain
|
|
66
62
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
|
67
63
|
|
|
68
|
-
- name: Cache cargo
|
|
69
|
-
uses:
|
|
64
|
+
- name: Cache cargo build
|
|
65
|
+
uses: Swatinem/rust-cache@v2
|
|
70
66
|
with:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
target/
|
|
76
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
77
|
-
restore-keys: |
|
|
78
|
-
${{ runner.os }}-cargo-
|
|
67
|
+
# Differentiates the matrix `check` job per feature set; evaluates to an
|
|
68
|
+
# empty string (no-op) in non-matrix jobs. rust-cache already keys on the
|
|
69
|
+
# job id, OS, rustc version, and lockfiles automatically.
|
|
70
|
+
key: ${{ matrix.features }}
|
|
79
71
|
|
|
80
|
-
- name:
|
|
72
|
+
- name: Check
|
|
81
73
|
run: |
|
|
82
74
|
if [ -z "${{ matrix.features }}" ]; then
|
|
83
|
-
cargo
|
|
75
|
+
cargo check --all-targets
|
|
84
76
|
else
|
|
85
|
-
cargo
|
|
77
|
+
cargo check --all-targets --features "${{ matrix.features }}"
|
|
86
78
|
fi
|
|
87
79
|
|
|
88
80
|
# Unit tests
|
|
@@ -95,24 +87,26 @@ jobs:
|
|
|
95
87
|
- name: Install Rust toolchain
|
|
96
88
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
|
97
89
|
|
|
98
|
-
- name: Cache cargo
|
|
99
|
-
uses:
|
|
90
|
+
- name: Cache cargo build
|
|
91
|
+
uses: Swatinem/rust-cache@v2
|
|
100
92
|
with:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
target/
|
|
106
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
107
|
-
restore-keys: |
|
|
108
|
-
${{ runner.os }}-cargo-
|
|
93
|
+
# Differentiates the matrix `check` job per feature set; evaluates to an
|
|
94
|
+
# empty string (no-op) in non-matrix jobs. rust-cache already keys on the
|
|
95
|
+
# job id, OS, rustc version, and lockfiles automatically.
|
|
96
|
+
key: ${{ matrix.features }}
|
|
109
97
|
|
|
110
98
|
- name: Run unit tests
|
|
111
|
-
run: cargo test --lib --features=full
|
|
99
|
+
run: cargo test --lib --features=full
|
|
112
100
|
# currently - just random panic test
|
|
113
101
|
# - name: Run long-running unit tests
|
|
114
102
|
# run: cargo test --lib --features=full -- --ignored --nocapture
|
|
115
103
|
|
|
104
|
+
- name: Run non-Docker integration tests
|
|
105
|
+
run: cargo test --test ref_test --test sqlite_test --test tls_example --test websocket_test --features=full
|
|
106
|
+
|
|
107
|
+
- name: Memory leak soak (route commit-task JoinSet)
|
|
108
|
+
run: cargo test --test memory_leak_test --features=full -- --ignored --nocapture
|
|
109
|
+
|
|
116
110
|
# Integration tests (requires Docker) split into parallel jobs
|
|
117
111
|
integration-others:
|
|
118
112
|
name: Integration — Others
|
|
@@ -127,17 +121,13 @@ jobs:
|
|
|
127
121
|
- name: Install Rust toolchain
|
|
128
122
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
|
129
123
|
|
|
130
|
-
- name: Cache cargo
|
|
131
|
-
uses:
|
|
124
|
+
- name: Cache cargo build
|
|
125
|
+
uses: Swatinem/rust-cache@v2
|
|
132
126
|
with:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
target/
|
|
138
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
139
|
-
restore-keys: |
|
|
140
|
-
${{ runner.os }}-cargo-
|
|
127
|
+
# Differentiates the matrix `check` job per feature set; evaluates to an
|
|
128
|
+
# empty string (no-op) in non-matrix jobs. rust-cache already keys on the
|
|
129
|
+
# job id, OS, rustc version, and lockfiles automatically.
|
|
130
|
+
key: ${{ matrix.features }}
|
|
141
131
|
|
|
142
132
|
- name: Pre-pull docker images
|
|
143
133
|
run: |
|
|
@@ -175,17 +165,13 @@ jobs:
|
|
|
175
165
|
- name: Install Rust toolchain
|
|
176
166
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
|
177
167
|
|
|
178
|
-
- name: Cache cargo
|
|
179
|
-
uses:
|
|
168
|
+
- name: Cache cargo build
|
|
169
|
+
uses: Swatinem/rust-cache@v2
|
|
180
170
|
with:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
target/
|
|
186
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
187
|
-
restore-keys: |
|
|
188
|
-
${{ runner.os }}-cargo-
|
|
171
|
+
# Differentiates the matrix `check` job per feature set; evaluates to an
|
|
172
|
+
# empty string (no-op) in non-matrix jobs. rust-cache already keys on the
|
|
173
|
+
# job id, OS, rustc version, and lockfiles automatically.
|
|
174
|
+
key: ${{ matrix.features }}
|
|
189
175
|
|
|
190
176
|
- name: Pre-pull docker images
|
|
191
177
|
run: |
|
|
@@ -223,17 +209,13 @@ jobs:
|
|
|
223
209
|
- name: Install Rust toolchain
|
|
224
210
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
|
225
211
|
|
|
226
|
-
- name: Cache cargo
|
|
227
|
-
uses:
|
|
212
|
+
- name: Cache cargo build
|
|
213
|
+
uses: Swatinem/rust-cache@v2
|
|
228
214
|
with:
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
target/
|
|
234
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
235
|
-
restore-keys: |
|
|
236
|
-
${{ runner.os }}-cargo-
|
|
215
|
+
# Differentiates the matrix `check` job per feature set; evaluates to an
|
|
216
|
+
# empty string (no-op) in non-matrix jobs. rust-cache already keys on the
|
|
217
|
+
# job id, OS, rustc version, and lockfiles automatically.
|
|
218
|
+
key: ${{ matrix.features }}
|
|
237
219
|
|
|
238
220
|
- name: Pre-pull docker images
|
|
239
221
|
run: |
|
|
@@ -264,20 +246,13 @@ jobs:
|
|
|
264
246
|
- name: Install Rust toolchain
|
|
265
247
|
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
|
|
266
248
|
|
|
267
|
-
- name: Cache cargo
|
|
268
|
-
uses:
|
|
249
|
+
- name: Cache cargo build
|
|
250
|
+
uses: Swatinem/rust-cache@v2
|
|
269
251
|
with:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
target/
|
|
275
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
276
|
-
restore-keys: |
|
|
277
|
-
${{ runner.os }}-cargo-
|
|
278
|
-
|
|
279
|
-
- name: Build documentation
|
|
280
|
-
run: cargo doc --all-features --no-deps
|
|
252
|
+
# Differentiates the matrix `check` job per feature set; evaluates to an
|
|
253
|
+
# empty string (no-op) in non-matrix jobs. rust-cache already keys on the
|
|
254
|
+
# job id, OS, rustc version, and lockfiles automatically.
|
|
255
|
+
key: ${{ matrix.features }}
|
|
281
256
|
|
|
282
|
-
- name:
|
|
283
|
-
run: cargo doc --all-features --no-deps
|
|
257
|
+
- name: Build documentation (deny warnings)
|
|
258
|
+
run: RUSTDOCFLAGS="-D warnings" cargo doc --all-features --no-deps
|
|
@@ -195,6 +195,28 @@ jobs:
|
|
|
195
195
|
working-directory: node/mq-bridge-node
|
|
196
196
|
run: ls -R npm
|
|
197
197
|
|
|
198
|
+
# The default per-platform README is ~88 bytes; paired with a 50-67MB
|
|
199
|
+
# binary that low-content signature trips npm's new-package spam filter
|
|
200
|
+
# (E403 "Package name triggered spam detection"). Give each stub a real
|
|
201
|
+
# README plus description/homepage/keywords so it doesn't read as a
|
|
202
|
+
# placeholder. (Server-side heuristic; reduces but cannot guarantee a pass.)
|
|
203
|
+
- name: Enrich platform package metadata
|
|
204
|
+
working-directory: node/mq-bridge-node
|
|
205
|
+
run: |
|
|
206
|
+
set -e
|
|
207
|
+
for dir in npm/*/; do
|
|
208
|
+
name=$(node -p "require('./$dir/package.json').name")
|
|
209
|
+
cat > "$dir/README.md" <<EOF
|
|
210
|
+
# $name
|
|
211
|
+
|
|
212
|
+
Prebuilt native binary for the [\`mq-bridge\`](https://github.com/marcomq/mq-bridge)
|
|
213
|
+
Node.js addon (napi-rs). This is a platform-specific dependency of the
|
|
214
|
+
\`mq-bridge\` package and is selected automatically via optionalDependencies —
|
|
215
|
+
you install \`mq-bridge\`, not this package directly.
|
|
216
|
+
EOF
|
|
217
|
+
node -e "const p='./$dir/package.json',j=require(p);j.description='Prebuilt '+j.name+' binary for the mq-bridge Node.js addon';j.homepage='https://github.com/marcomq/mq-bridge#readme';j.keywords=['mq-bridge','napi','native','prebuilt','addon'];require('fs').writeFileSync(p,JSON.stringify(j,null,2)+'\n')"
|
|
218
|
+
done
|
|
219
|
+
|
|
198
220
|
- name: Publish platform packages and root
|
|
199
221
|
working-directory: node/mq-bridge-node
|
|
200
222
|
env:
|
|
@@ -203,9 +225,27 @@ jobs:
|
|
|
203
225
|
NPM_CONFIG_PROVENANCE: "true"
|
|
204
226
|
run: |
|
|
205
227
|
set -e
|
|
206
|
-
|
|
207
|
-
|
|
228
|
+
publish() {
|
|
229
|
+
local dir="$1" name ver
|
|
230
|
+
name=$(node -p "require('./$dir/package.json').name")
|
|
231
|
+
ver=$(node -p "require('./$dir/package.json').version")
|
|
232
|
+
# Skip if this exact version is already on the registry; a re-run
|
|
233
|
+
# after a partial publish must not fail on already-published dirs.
|
|
234
|
+
if npm view "$name@$ver" version >/dev/null 2>&1; then
|
|
235
|
+
echo "Skipping $name@$ver (already published)"
|
|
236
|
+
return 0
|
|
237
|
+
fi
|
|
238
|
+
echo "Publishing $dir ($name@$ver)"
|
|
208
239
|
npm publish "$dir" --access public
|
|
240
|
+
}
|
|
241
|
+
for dir in npm/*/; do
|
|
242
|
+
publish "$dir"
|
|
209
243
|
done
|
|
210
244
|
echo "Publishing root package"
|
|
211
|
-
|
|
245
|
+
root_name=$(node -p "require('./package.json').name")
|
|
246
|
+
root_ver=$(node -p "require('./package.json').version")
|
|
247
|
+
if npm view "$root_name@$root_ver" version >/dev/null 2>&1; then
|
|
248
|
+
echo "Skipping $root_name@$root_ver (already published)"
|
|
249
|
+
else
|
|
250
|
+
npm publish --access public
|
|
251
|
+
fi
|
|
@@ -161,6 +161,56 @@ input:
|
|
|
161
161
|
# ... kafka config
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
### TLS & Security Hardening
|
|
165
|
+
|
|
166
|
+
Most endpoints accept a `tls` block. The available fields are:
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
tls:
|
|
170
|
+
required: true # enable TLS
|
|
171
|
+
ca_file: "./certs/ca.pem" # CA to verify the server
|
|
172
|
+
cert_file: "./certs/client.pem" # client cert (mTLS)
|
|
173
|
+
key_file: "./certs/client.key" # client private key (mTLS)
|
|
174
|
+
cert_password: "secret" # password for an encrypted key (where supported)
|
|
175
|
+
accept_invalid_certs: false # NEVER set true in production
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Hardening checklist (e.g. for PCI-DSS Req 4.2.1):**
|
|
179
|
+
|
|
180
|
+
1. **Enable TLS on every endpoint carrying sensitive data** (`required: true`) and supply
|
|
181
|
+
a `ca_file`. Use mTLS (`cert_file` + `key_file`) for mutual authentication where the
|
|
182
|
+
broker supports it.
|
|
183
|
+
2. **Never disable certificate validation.** `accept_invalid_certs` defaults to `false`;
|
|
184
|
+
leaving it that way is required — setting it `true` on a sensitive path defeats TLS.
|
|
185
|
+
3. **Choose the crypto provider feature.** Build with `rustls-aws-lc` (FIPS-capable, also
|
|
186
|
+
enables post-quantum key exchange) or `rustls-ring`. The rustls-based endpoints — NATS,
|
|
187
|
+
MQTT, HTTP, gRPC, WebSocket, AMQP — only ever negotiate rustls's safe TLS 1.2/1.3 AEAD
|
|
188
|
+
cipher suites; weak/legacy suites (RC4, 3DES, CBC-SHA1, export) cannot be offered, so
|
|
189
|
+
"strong ciphers only" holds without any explicit cipher list.
|
|
190
|
+
4. **Kafka** (librdkafka/OpenSSL): certificate verification is on by default
|
|
191
|
+
(`enable.ssl.certificate.verification` follows `accept_invalid_certs`). If an auditor
|
|
192
|
+
requires an explicit allowlist, pin it via `producer_options` / `consumer_options`:
|
|
193
|
+
```yaml
|
|
194
|
+
kafka:
|
|
195
|
+
producer_options: [["ssl.cipher.suites", "ECDHE-RSA-AES256-GCM-SHA384"]]
|
|
196
|
+
consumer_options: [["ssl.cipher.suites", "ECDHE-RSA-AES256-GCM-SHA384"]]
|
|
197
|
+
```
|
|
198
|
+
5. **IBM MQ** (native stack): set a strong `cipher_spec` (a TLS 1.2/1.3 CipherSpec) — it is
|
|
199
|
+
required for encrypted connections.
|
|
200
|
+
6. **Keep sensitive payloads out of logs.** Message payloads are emitted at `trace` level;
|
|
201
|
+
run production above `trace` and confirm no cardholder data (PAN) reaches logs or traces.
|
|
202
|
+
7. **Do not commit secrets.** Source passwords and tokens from a secrets manager or env vars
|
|
203
|
+
(`MQB__...`) rather than checked-in config.
|
|
204
|
+
|
|
205
|
+
**Notes and boundaries:**
|
|
206
|
+
|
|
207
|
+
- TLS 1.3 alone is sufficient in 2026 (Mozilla "Modern" profile). The rustls endpoints
|
|
208
|
+
currently negotiate **TLS 1.2 and 1.3** (both are PCI-acceptable). A central
|
|
209
|
+
"TLS 1.3-only" toggle is not yet configurable in the library; enforce a minimum protocol
|
|
210
|
+
version on the broker/server side, which is the side that accepts the connection.
|
|
211
|
+
- Kafka and IBM MQ use native TLS stacks, so a library-wide version policy cannot be
|
|
212
|
+
applied to them — configure their minimum TLS version on the broker.
|
|
213
|
+
|
|
164
214
|
### HTTP Consumer Fast Path
|
|
165
215
|
|
|
166
216
|
Compatible `http -> response` routes may use an inline response fast path for lower latency. This bypasses the normal route consumer/worker/disposition pipeline, but it still keeps the output publisher chain active, including output handlers and allowed output middlewares.
|
|
@@ -183,6 +233,36 @@ input:
|
|
|
183
233
|
|
|
184
234
|
This is useful when you want stable, explicit semantics regardless of future optimizations, or when you want to avoid the inline path's response behavior differences. In particular, the inline path does not automatically echo unchanged request metadata back as HTTP response headers.
|
|
185
235
|
|
|
236
|
+
### Connection Sharing
|
|
237
|
+
|
|
238
|
+
Publishers that target the same server reuse one underlying transport client by
|
|
239
|
+
default, instead of each opening its own. This consolidates TCP connections, background
|
|
240
|
+
threads, and batching, and follows each driver's own guidance (one shared producer /
|
|
241
|
+
client / pool per application). Sharing applies to **Kafka, NATS, MongoDB, SQLx, HTTP,
|
|
242
|
+
and gRPC**; the client is keyed by its connection-level settings (URL, auth, TLS, and
|
|
243
|
+
client-level options), never by topic/subject/collection. A shared client is released
|
|
244
|
+
once the last publisher using it is dropped.
|
|
245
|
+
|
|
246
|
+
Set `shared: false` on a publisher to give it a dedicated connection:
|
|
247
|
+
|
|
248
|
+
```yaml
|
|
249
|
+
orders_out:
|
|
250
|
+
output:
|
|
251
|
+
kafka:
|
|
252
|
+
topic: "orders"
|
|
253
|
+
url: "localhost:9092"
|
|
254
|
+
shared: false # dedicated producer — keeps this latency-sensitive topic off a busy producer's queue
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
* **Kafka**: a single producer serves every topic and is the recommended setup. Use
|
|
258
|
+
`shared: false` to isolate a latency-sensitive topic from a high-throughput one so
|
|
259
|
+
they don't share one internal send queue (head-of-line blocking).
|
|
260
|
+
* **SQLx**: a shared pool means its `max_connections` is a budget shared across every
|
|
261
|
+
route using that database. Use `shared: false` if a route needs its own pool.
|
|
262
|
+
* **gRPC**: a shared channel multiplexes over one HTTP/2 connection; at very high
|
|
263
|
+
concurrency its max-concurrent-streams cap can bottleneck — `shared: false` gives a
|
|
264
|
+
dedicated channel.
|
|
265
|
+
|
|
186
266
|
### Specialized Endpoints
|
|
187
267
|
|
|
188
268
|
#### Switch
|
|
@@ -3082,7 +3082,7 @@ dependencies = [
|
|
|
3082
3082
|
|
|
3083
3083
|
[[package]]
|
|
3084
3084
|
name = "mq-bridge"
|
|
3085
|
-
version = "0.
|
|
3085
|
+
version = "0.3.0"
|
|
3086
3086
|
dependencies = [
|
|
3087
3087
|
"anyhow",
|
|
3088
3088
|
"arc-swap",
|
|
@@ -3149,7 +3149,7 @@ dependencies = [
|
|
|
3149
3149
|
|
|
3150
3150
|
[[package]]
|
|
3151
3151
|
name = "mq-bridge-node"
|
|
3152
|
-
version = "0.
|
|
3152
|
+
version = "0.3.0"
|
|
3153
3153
|
dependencies = [
|
|
3154
3154
|
"anyhow",
|
|
3155
3155
|
"async-trait",
|
|
@@ -3169,7 +3169,7 @@ dependencies = [
|
|
|
3169
3169
|
|
|
3170
3170
|
[[package]]
|
|
3171
3171
|
name = "mq-bridge-py"
|
|
3172
|
-
version = "0.
|
|
3172
|
+
version = "0.3.0"
|
|
3173
3173
|
dependencies = [
|
|
3174
3174
|
"anyhow",
|
|
3175
3175
|
"async-trait",
|
|
@@ -55,7 +55,7 @@ That means `mq-bridge` tries to keep the boring parts boring. Kafka offsets, Rab
|
|
|
55
55
|
|
|
56
56
|
Batching is a big part of that design. Every endpoint is optimized around batch-shaped APIs, even when the backend itself only has a single-message primitive. Batching is disabled by default (`batch_size: 1`) because it is the safest behavior and easiest to reason about. When throughput matters, increasing `batch_size` is usually the first knob to try. For example via `batch_size: 128` in yaml or `.with_batch_size(128)` for routes.
|
|
57
57
|
|
|
58
|
-
The error handling follows the same idea. Batch publishing can report partial success, retryable failures, and non-retryable failures. Route commits are sequenced
|
|
58
|
+
The error handling follows the same idea. Batch publishing can report partial success, retryable failures, and non-retryable failures. Route commits are sequenced for cumulative-ack brokers so later batches cannot acknowledge earlier unresolved messages; transports with independent acknowledgements can commit batches concurrently. In other words: batching is not just a performance trick bolted onto the side; ack/nack behavior and retry/DLQ handling were built to work with it.
|
|
59
59
|
|
|
60
60
|
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.
|
|
61
61
|
|
|
@@ -392,7 +392,7 @@ Important route-level knobs:
|
|
|
392
392
|
|
|
393
393
|
* `batch_size`: maximum messages per route iteration. Defaults to `1`; increase it when throughput matters.
|
|
394
394
|
* `concurrency`: number of route workers. Defaults to `1`; useful for high-latency handlers or endpoints.
|
|
395
|
-
* `commit_concurrency_limit`: maximum
|
|
395
|
+
* `commit_concurrency_limit`: maximum in-flight commit operations, whether they are queued through ordered commit sequencing or run concurrently for independent-ack transports. Defaults to `4096`.
|
|
396
396
|
|
|
397
397
|
Middleware can be attached to inputs or outputs. The most commonly used ones are `retry`, `dlq`, `deduplication`, `limiter`, and `cookie_jar`. Retry/DLQ are especially useful with batching because partial failures can be retried or sent to a DLQ without treating the entire batch as equally broken.
|
|
398
398
|
|
|
@@ -805,6 +805,14 @@
|
|
|
805
805
|
"type": "boolean",
|
|
806
806
|
"default": false
|
|
807
807
|
},
|
|
808
|
+
"shared": {
|
|
809
|
+
"description": "(Publisher only) Share one gRPC channel per connection (default: true); false forces a dedicated channel.",
|
|
810
|
+
"type": [
|
|
811
|
+
"boolean",
|
|
812
|
+
"null"
|
|
813
|
+
],
|
|
814
|
+
"default": true
|
|
815
|
+
},
|
|
808
816
|
"timeout_ms": {
|
|
809
817
|
"description": "Timeout in milliseconds.\n- Client mode: used as the connection timeout and per-request deadline.\n- Server mode: applied as the per-request deadline on the embedded server.",
|
|
810
818
|
"type": [
|
|
@@ -977,6 +985,14 @@
|
|
|
977
985
|
"$ref": "#/$defs/HttpServerProtocol",
|
|
978
986
|
"default": "auto"
|
|
979
987
|
},
|
|
988
|
+
"shared": {
|
|
989
|
+
"description": "(Publisher only) Share one HTTP client per connection (default: true); false forces a dedicated client.",
|
|
990
|
+
"type": [
|
|
991
|
+
"boolean",
|
|
992
|
+
"null"
|
|
993
|
+
],
|
|
994
|
+
"default": true
|
|
995
|
+
},
|
|
980
996
|
"stream_response_to": {
|
|
981
997
|
"description": "(Publisher only) Optional endpoint that receives streamed HTTP response items as correlated messages.\n\nUse a `stream_buffer` endpoint here when callers need to read streamed\nresponse items later through a normal mq-bridge consumer. Each streamed\nitem is published with `correlation_id`, `http_stream_id`,\n`http_stream_index`, `http_stream_format`, and `http_stream_end`\nmetadata. If the request message has no `correlation_id`, the HTTP\npublisher uses `format!(\"{:032x}\", request.message_id)` so callers can\nderive the consumer correlation id before calling `send`.",
|
|
982
998
|
"anyOf": [
|
|
@@ -1215,6 +1231,14 @@
|
|
|
1215
1231
|
]
|
|
1216
1232
|
}
|
|
1217
1233
|
},
|
|
1234
|
+
"shared": {
|
|
1235
|
+
"description": "(Publisher only) Share one producer per connection (default: true); false gives a dedicated producer.",
|
|
1236
|
+
"type": [
|
|
1237
|
+
"boolean",
|
|
1238
|
+
"null"
|
|
1239
|
+
],
|
|
1240
|
+
"default": true
|
|
1241
|
+
},
|
|
1218
1242
|
"tls": {
|
|
1219
1243
|
"description": "TLS configuration.",
|
|
1220
1244
|
"$ref": "#/$defs/TlsConfig",
|
|
@@ -1574,6 +1598,14 @@
|
|
|
1574
1598
|
"format": "uint64",
|
|
1575
1599
|
"minimum": 0
|
|
1576
1600
|
},
|
|
1601
|
+
"shared": {
|
|
1602
|
+
"description": "Share one MongoDB client per connection (default: true); false forces a dedicated client.",
|
|
1603
|
+
"type": [
|
|
1604
|
+
"boolean",
|
|
1605
|
+
"null"
|
|
1606
|
+
],
|
|
1607
|
+
"default": true
|
|
1608
|
+
},
|
|
1577
1609
|
"tls": {
|
|
1578
1610
|
"description": "TLS configuration.",
|
|
1579
1611
|
"$ref": "#/$defs/TlsConfig",
|
|
@@ -1806,6 +1838,14 @@
|
|
|
1806
1838
|
"format": "uint64",
|
|
1807
1839
|
"minimum": 0
|
|
1808
1840
|
},
|
|
1841
|
+
"shared": {
|
|
1842
|
+
"description": "Share one NATS client per connection (default: true); false forces a dedicated connection.",
|
|
1843
|
+
"type": [
|
|
1844
|
+
"boolean",
|
|
1845
|
+
"null"
|
|
1846
|
+
],
|
|
1847
|
+
"default": true
|
|
1848
|
+
},
|
|
1809
1849
|
"stream": {
|
|
1810
1850
|
"description": "(Consumer only). The JetStream stream name. Required for Consumers.",
|
|
1811
1851
|
"type": [
|
|
@@ -2148,6 +2188,14 @@
|
|
|
2148
2188
|
"null"
|
|
2149
2189
|
]
|
|
2150
2190
|
},
|
|
2191
|
+
"shared": {
|
|
2192
|
+
"description": "Share one connection pool per connection (default: true); false forces a dedicated pool.",
|
|
2193
|
+
"type": [
|
|
2194
|
+
"boolean",
|
|
2195
|
+
"null"
|
|
2196
|
+
],
|
|
2197
|
+
"default": true
|
|
2198
|
+
},
|
|
2151
2199
|
"table": {
|
|
2152
2200
|
"description": "The table to interact with.",
|
|
2153
2201
|
"type": "string"
|
|
@@ -110,6 +110,7 @@ class GrpcConfig(TypedDict, total=False):
|
|
|
110
110
|
initial_stream_window_size: Optional[int]
|
|
111
111
|
max_decoding_message_size: Optional[int]
|
|
112
112
|
server_mode: bool
|
|
113
|
+
shared: Optional[bool]
|
|
113
114
|
timeout_ms: Optional[int]
|
|
114
115
|
tls: TlsConfig
|
|
115
116
|
topic: Optional[str]
|
|
@@ -134,6 +135,7 @@ class HttpConfig(TypedDict, total=False):
|
|
|
134
135
|
receive_streamable: bool
|
|
135
136
|
request_timeout_ms: Optional[int]
|
|
136
137
|
server_protocol: HttpServerProtocol
|
|
138
|
+
shared: Optional[bool]
|
|
137
139
|
stream_response_to: Optional[Endpoint]
|
|
138
140
|
tcp_keepalive_ms: Optional[int]
|
|
139
141
|
tls: TlsConfig
|
|
@@ -165,6 +167,7 @@ class KafkaConfig(TypedDict, total=False):
|
|
|
165
167
|
group_id: Optional[str]
|
|
166
168
|
password: Optional[str]
|
|
167
169
|
producer_options: Optional[List[List[Any]]]
|
|
170
|
+
shared: Optional[bool]
|
|
168
171
|
tls: TlsConfig
|
|
169
172
|
topic: Optional[str]
|
|
170
173
|
url: Required[str]
|
|
@@ -221,6 +224,7 @@ class MongoDbConfig(TypedDict, total=False):
|
|
|
221
224
|
reply_polling_ms: Optional[int]
|
|
222
225
|
request_reply: bool
|
|
223
226
|
request_timeout_ms: Optional[int]
|
|
227
|
+
shared: Optional[bool]
|
|
224
228
|
tls: TlsConfig
|
|
225
229
|
ttl_seconds: Optional[int]
|
|
226
230
|
url: Required[str]
|
|
@@ -254,6 +258,7 @@ class NatsConfig(TypedDict, total=False):
|
|
|
254
258
|
prefetch_count: Optional[int]
|
|
255
259
|
request_reply: bool
|
|
256
260
|
request_timeout_ms: Optional[int]
|
|
261
|
+
shared: Optional[bool]
|
|
257
262
|
stream: Optional[str]
|
|
258
263
|
stream_max_bytes: Optional[int]
|
|
259
264
|
stream_max_messages: Optional[int]
|
|
@@ -319,6 +324,7 @@ class SqlxConfig(TypedDict, total=False):
|
|
|
319
324
|
password: Optional[str]
|
|
320
325
|
polling_interval_ms: Optional[int]
|
|
321
326
|
select_query: Optional[str]
|
|
327
|
+
shared: Optional[bool]
|
|
322
328
|
table: Required[str]
|
|
323
329
|
tls: TlsConfig
|
|
324
330
|
url: Required[str]
|
|
@@ -567,7 +567,8 @@ impl Route {
|
|
|
567
567
|
|
|
568
568
|
let wait_name = name.clone();
|
|
569
569
|
let wait_run_state = Arc::clone(&run_state);
|
|
570
|
-
let
|
|
570
|
+
let cleanup_runtime = Arc::clone(&runtime);
|
|
571
|
+
let handle = match thread::Builder::new()
|
|
571
572
|
.name(format!("mqb-route-{name}"))
|
|
572
573
|
.spawn(move || {
|
|
573
574
|
let stop_name = wait_name.clone();
|
|
@@ -576,8 +577,16 @@ impl Route {
|
|
|
576
577
|
core::Route::stop(&stop_name).await;
|
|
577
578
|
});
|
|
578
579
|
finish_run(&wait_run_state, &wait_name);
|
|
579
|
-
})
|
|
580
|
-
|
|
580
|
+
}) {
|
|
581
|
+
Ok(handle) => handle,
|
|
582
|
+
Err(err) => {
|
|
583
|
+
// The route is already deployed and run_state marked running; a failed
|
|
584
|
+
// spawn would otherwise leak the deployed route and its name reservation.
|
|
585
|
+
py.detach(|| cleanup_runtime.block_on(async { core::Route::stop(&name).await }));
|
|
586
|
+
finish_run(&run_state, &name);
|
|
587
|
+
return Err(to_py_runtime_error(err));
|
|
588
|
+
}
|
|
589
|
+
};
|
|
581
590
|
|
|
582
591
|
self.lock_run_state()?.join_handle = Some(handle);
|
|
583
592
|
Ok(())
|
|
@@ -30,11 +30,16 @@ def test_generated_config_types_are_up_to_date() -> None:
|
|
|
30
30
|
generator = _load_generator()
|
|
31
31
|
pyi, py = generator.generate()
|
|
32
32
|
|
|
33
|
+
regen = (
|
|
34
|
+
"stale generated config types. Rebuild the extension and regenerate:\n"
|
|
35
|
+
" cd python/mq-bridge-py && uv run maturin develop "
|
|
36
|
+
"&& uv run --no-sync python scripts/gen_config_types.py"
|
|
37
|
+
)
|
|
33
38
|
assert pyi == (PACKAGE_DIR / "mq_bridge" / "config.pyi").read_text(), (
|
|
34
|
-
"config.pyi is
|
|
39
|
+
f"config.pyi is {regen}"
|
|
35
40
|
)
|
|
36
41
|
assert py == (PACKAGE_DIR / "mq_bridge" / "config.py").read_text(), (
|
|
37
|
-
"config.py is
|
|
42
|
+
f"config.py is {regen}"
|
|
38
43
|
)
|
|
39
44
|
|
|
40
45
|
|
|
@@ -98,7 +98,7 @@ scripts/httparena/frameworks/
|
|
|
98
98
|
## Submitting upstream
|
|
99
99
|
|
|
100
100
|
1. Pin the version: each Rust `Cargo.toml` and the Python `Dockerfile` reference
|
|
101
|
-
this repo at tag `v0.2.
|
|
101
|
+
this repo at tag `v0.2.21` — bump to the release you want to benchmark.
|
|
102
102
|
2. Fork `MDA2AV/HttpArena` and copy each `scripts/httparena/frameworks/<name>/`
|
|
103
103
|
into the fork's `frameworks/<name>/`.
|
|
104
104
|
3. On the PR, validate and benchmark per framework:
|