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.
Files changed (199) hide show
  1. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/ci.yml +54 -79
  2. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/publish-node.yml +43 -3
  3. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/CONFIGURATION.md +80 -0
  4. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/Cargo.lock +3 -3
  5. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/Cargo.toml +1 -1
  6. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/PKG-INFO +1 -1
  7. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/README.md +2 -2
  8. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq-bridge.schema.json +48 -0
  9. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/config.pyi +6 -0
  10. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/src/lib.rs +12 -3
  11. {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
  12. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/httparena/README.md +1 -1
  13. mq_bridge_py_basic-0.3.0/src/connection_registry.rs +280 -0
  14. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/amqp.rs +21 -17
  15. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/aws.rs +5 -0
  16. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/file.rs +3 -0
  17. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/grpc.rs +33 -2
  18. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/http.rs +66 -31
  19. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/ibm_mq.rs +3 -0
  20. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/kafka.rs +57 -13
  21. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/endpoint.rs +176 -48
  22. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/mongodb.rs +33 -1
  23. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/mqtt.rs +8 -0
  24. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/nats.rs +39 -5
  25. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/sled.rs +5 -0
  26. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/sqlx.rs +41 -1
  27. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/static_endpoint.rs +4 -0
  28. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/stream_buffer.rs +3 -0
  29. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/websocket.rs +13 -31
  30. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/zeromq.rs +5 -0
  31. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/event_store.rs +3 -0
  32. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/lib.rs +1 -0
  33. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/buffer.rs +3 -0
  34. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/cookie_jar.rs +3 -0
  35. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/deduplication.rs +3 -0
  36. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/delay.rs +3 -0
  37. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/limiter.rs +3 -0
  38. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/metrics.rs +3 -0
  39. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/random_panic.rs +3 -0
  40. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/weak_join.rs +3 -0
  41. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/models.rs +30 -0
  42. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/route.rs +213 -13
  43. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/test_utils.rs +10 -1
  44. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/traits.rs +176 -43
  45. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/http_tls.rs +1 -1
  46. mq_bridge_py_basic-0.3.0/tests/memory_leak_test.rs +134 -0
  47. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  48. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  49. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  50. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/benchmark.yml +0 -0
  51. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/ibm-mq.yml +0 -0
  52. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/node.yml +0 -0
  53. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/publish-python.yml +0 -0
  54. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/python.yml +0 -0
  55. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/release.yml +0 -0
  56. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.github/workflows/test-matrix.yml +0 -0
  57. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/.gitignore +0 -0
  58. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/ARCHITECTURE.md +0 -0
  59. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/CONTRIBUTING.md +0 -0
  60. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/LICENSE +0 -0
  61. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/LICENSE.python +0 -0
  62. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/benches/performance_bench.rs +0 -0
  63. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/benches/router_bench.rs +0 -0
  64. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/build.rs +0 -0
  65. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/claude.md +0 -0
  66. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/examples/ipc_worker_queue.yaml +0 -0
  67. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/__init__.py +0 -0
  68. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/__init__.pyi +0 -0
  69. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/config.py +0 -0
  70. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/mq_bridge/py.typed +0 -0
  71. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/package-lock.json +0 -0
  72. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/pyproject.toml +0 -0
  73. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/Cargo.toml +0 -0
  74. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/LICENSE.python +0 -0
  75. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/README.md +0 -0
  76. {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
  77. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/example.py +0 -0
  78. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/bench_memory.py +0 -0
  79. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/bench_memory.yaml +0 -0
  80. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/json_route.py +0 -0
  81. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/memory.yaml +0 -0
  82. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/examples/raw_route.py +0 -0
  83. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/pyproject-basic.toml +0 -0
  84. {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
  85. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/tests/test_packaging.py +0 -0
  86. {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
  87. {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
  88. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/tests/test_routes.py +0 -0
  89. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/python/mq-bridge-py/uv.lock +0 -0
  90. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/HTTP_STREAM_ENDPOINT_PLAN.md +0 -0
  91. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/benches/metadata_bench.rs +0 -0
  92. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/debug_ibm_mq_tls.rs +0 -0
  93. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/http/analyze_xctrace_time_profile.py +0 -0
  94. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/http/http_flamegraph.py +0 -0
  95. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/http/mq_bridge_http_profile.rs +0 -0
  96. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/analysis/http/run_http_ladder.sh +0 -0
  97. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/httparena/frameworks/mq-bridge-py/Dockerfile +0 -0
  98. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/httparena/frameworks/mq-bridge-py/meta.json +0 -0
  99. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/httparena/frameworks/mq-bridge-py/server.py +0 -0
  100. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/Python/mq-bridge-py/README.md +0 -0
  101. {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
  102. {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
  103. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/Python/mq-bridge-py/server.py +0 -0
  104. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/README.md +0 -0
  105. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/postgres.yml +0 -0
  106. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/seed.sql +0 -0
  107. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/techempower/verify.sh +0 -0
  108. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/scripts/the-benchmarker/README.md +0 -0
  109. {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
  110. {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
  111. {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
  112. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/canonical_message.rs +0 -0
  113. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/command_handler.rs +0 -0
  114. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/fanout.rs +0 -0
  115. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/grpc.proto +0 -0
  116. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/http_stream.rs +0 -0
  117. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/ipc_unix.rs +0 -0
  118. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/ipc_windows.rs +0 -0
  119. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/memory_transport.rs +0 -0
  120. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/mod.rs +0 -0
  121. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/memory/transport.rs +0 -0
  122. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/mod.rs +0 -0
  123. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/null.rs +0 -0
  124. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/reader.rs +0 -0
  125. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/response.rs +0 -0
  126. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/endpoints/switch.rs +0 -0
  127. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/errors.rs +0 -0
  128. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/event_handler.rs +0 -0
  129. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/extensions.rs +0 -0
  130. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/dlq.rs +0 -0
  131. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/mod.rs +0 -0
  132. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/middleware/retry.rs +0 -0
  133. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/outcomes.rs +0 -0
  134. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/publisher.rs +0 -0
  135. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/response.rs +0 -0
  136. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/src/type_handler.rs +0 -0
  137. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/README.md +0 -0
  138. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/armature_integration.rs +0 -0
  139. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/amqp.rs +0 -0
  140. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/aws.rs +0 -0
  141. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/config.toml +0 -0
  142. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/amqp.yml +0 -0
  143. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/aws.yml +0 -0
  144. {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
  145. {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
  146. {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
  147. {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
  148. {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
  149. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/ibm_mq.yml +0 -0
  150. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/ibm_mq_tls.yml +0 -0
  151. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/kafka-tls.yml +0 -0
  152. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/kafka.yml +0 -0
  153. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mariadb.yml +0 -0
  154. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mongodb-replica.yml +0 -0
  155. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mongodb-tls.yml +0 -0
  156. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mongodb.yml +0 -0
  157. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mosquitto.conf +0 -0
  158. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mosquitto_performance.conf +0 -0
  159. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mosquitto_persistence.conf +0 -0
  160. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mq_init.mqsc +0 -0
  161. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mq_init_tls.mqsc +0 -0
  162. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mqtt.yml +0 -0
  163. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mqtt_performance.yml +0 -0
  164. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/mysql.yml +0 -0
  165. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/nats.yml +0 -0
  166. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/docker-compose/postgres.yml +0 -0
  167. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/file.rs +0 -0
  168. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/grpc.rs +0 -0
  169. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/grpc_tls.rs +0 -0
  170. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/http.rs +0 -0
  171. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/ibm_mq.rs +0 -0
  172. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/ibm_mq_tls.rs +0 -0
  173. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/ipc.rs +0 -0
  174. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/kafka.rs +0 -0
  175. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/kafka_tls.rs +0 -0
  176. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/logic_test.rs +0 -0
  177. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mariadb.rs +0 -0
  178. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/memory.rs +0 -0
  179. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mod.rs +0 -0
  180. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mongodb.rs +0 -0
  181. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mongodb_raw.rs +0 -0
  182. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mongodb_tls.rs +0 -0
  183. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mqtt.rs +0 -0
  184. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/mysql.rs +0 -0
  185. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/nats.rs +0 -0
  186. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/performance_static.rs +0 -0
  187. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/postgres.rs +0 -0
  188. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/route.rs +0 -0
  189. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/scripts/gen_certs.sh +0 -0
  190. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/sqlite.rs +0 -0
  191. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/tls_helpers.rs +0 -0
  192. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/websocket.rs +0 -0
  193. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration/zeromq.rs +0 -0
  194. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/integration_test.rs +0 -0
  195. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/ref_test.rs +0 -0
  196. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/sqlite_test.rs +0 -0
  197. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/tls_example.rs +0 -0
  198. {mq_bridge_py_basic-0.2.21 → mq_bridge_py_basic-0.3.0}/tests/unit_tests.md +0 -0
  199. {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 registry
27
- uses: actions/cache@v4
26
+ - name: Cache cargo build
27
+ uses: Swatinem/rust-cache@v2
28
28
  with:
29
- path: |
30
- ~/.cargo/bin/
31
- ~/.cargo/registry/cache/
32
- ~/.cargo/git/db/
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 registry
69
- uses: actions/cache@v4
64
+ - name: Cache cargo build
65
+ uses: Swatinem/rust-cache@v2
70
66
  with:
71
- path: |
72
- ~/.cargo/bin/
73
- ~/.cargo/registry/cache/
74
- ~/.cargo/git/db/
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: Build
72
+ - name: Check
81
73
  run: |
82
74
  if [ -z "${{ matrix.features }}" ]; then
83
- cargo build --release
75
+ cargo check --all-targets
84
76
  else
85
- cargo build --release --features "${{ matrix.features }}"
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 registry
99
- uses: actions/cache@v4
90
+ - name: Cache cargo build
91
+ uses: Swatinem/rust-cache@v2
100
92
  with:
101
- path: |
102
- ~/.cargo/bin/
103
- ~/.cargo/registry/cache/
104
- ~/.cargo/git/db/
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 registry
131
- uses: actions/cache@v4
124
+ - name: Cache cargo build
125
+ uses: Swatinem/rust-cache@v2
132
126
  with:
133
- path: |
134
- ~/.cargo/bin/
135
- ~/.cargo/registry/cache/
136
- ~/.cargo/git/db/
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 registry
179
- uses: actions/cache@v4
168
+ - name: Cache cargo build
169
+ uses: Swatinem/rust-cache@v2
180
170
  with:
181
- path: |
182
- ~/.cargo/bin/
183
- ~/.cargo/registry/cache/
184
- ~/.cargo/git/db/
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 registry
227
- uses: actions/cache@v4
212
+ - name: Cache cargo build
213
+ uses: Swatinem/rust-cache@v2
228
214
  with:
229
- path: |
230
- ~/.cargo/bin/
231
- ~/.cargo/registry/cache/
232
- ~/.cargo/git/db/
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 registry
268
- uses: actions/cache@v4
249
+ - name: Cache cargo build
250
+ uses: Swatinem/rust-cache@v2
269
251
  with:
270
- path: |
271
- ~/.cargo/bin/
272
- ~/.cargo/registry/cache/
273
- ~/.cargo/git/db/
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: Check for broken links
283
- run: cargo doc --all-features --no-deps 2>&1 | grep -i "warning\|error" && exit 1 || exit 0
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
- for dir in npm/*/; do
207
- echo "Publishing $dir"
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
- npm publish --access public
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.2.21"
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.2.21"
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.2.21"
3172
+ version = "0.3.0"
3173
3173
  dependencies = [
3174
3174
  "anyhow",
3175
3175
  "async-trait",
@@ -16,7 +16,7 @@ members = ["python/mq-bridge-py"]
16
16
  resolver = "2"
17
17
 
18
18
  [workspace.package]
19
- version = "0.2.21"
19
+ version = "0.3.0"
20
20
 
21
21
  [workspace.dependencies]
22
22
  anyhow = "1.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mq-bridge-py-basic
3
- Version: 0.2.21
3
+ Version: 0.3.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: License :: OSI Approved :: MIT License
6
6
  Classifier: Programming Language :: Python :: 3
@@ -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 so cumulative-ack brokers do not accidentally acknowledge later messages before earlier batches are resolved. 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.
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 queued in-flight commit operations used by ordered commit sequencing. Defaults to `4096`.
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 handle = thread::Builder::new()
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
- .map_err(to_py_runtime_error)?;
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 stale; regenerate with scripts/gen_config_types.py"
39
+ f"config.pyi is {regen}"
35
40
  )
36
41
  assert py == (PACKAGE_DIR / "mq_bridge" / "config.py").read_text(), (
37
- "config.py is stale; regenerate with scripts/gen_config_types.py"
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.18` — bump to the release you want to benchmark.
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: