mq-bridge-py-basic 0.3.1__tar.gz → 0.3.2__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 (203) hide show
  1. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/workflows/benchmark.yml +9 -13
  2. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/workflows/ci.yml +88 -39
  3. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/workflows/ibm-mq.yml +16 -5
  4. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/workflows/publish-python.yml +27 -2
  5. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/Cargo.lock +33 -4
  6. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/Cargo.toml +23 -5
  7. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/PKG-INFO +44 -2
  8. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/README.md +1 -1
  9. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/benches/performance_bench.rs +25 -13
  10. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/mq_bridge/__init__.pyi +29 -1
  11. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/pyproject.toml +1 -1
  12. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/Cargo.toml +18 -6
  13. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/README.md +42 -0
  14. mq_bridge_py_basic-0.3.2/python/mq-bridge-py/examples/dlt_source.py +112 -0
  15. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/pyproject-basic.toml +1 -1
  16. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/src/lib.rs +201 -42
  17. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/tests/test_routes.py +86 -0
  18. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/analysis/debug_ibm_mq_tls.rs +1 -1
  19. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/canonical_message.rs +101 -0
  20. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/amqp.rs +28 -0
  21. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/aws.rs +3 -0
  22. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/file.rs +6 -1
  23. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/grpc.rs +5 -2
  24. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/http.rs +4 -0
  25. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/ibm_mq.rs +203 -11
  26. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/kafka.rs +29 -0
  27. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/mod.rs +5 -5
  28. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/mongodb.rs +25 -0
  29. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/mqtt.rs +332 -20
  30. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/nats.rs +83 -1
  31. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/sled.rs +6 -2
  32. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/zeromq.rs +73 -11
  33. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/models.rs +24 -1
  34. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/test_utils.rs +43 -15
  35. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/armature_integration.rs +39 -33
  36. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mq_init.mqsc +4 -0
  37. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/grpc.rs +4 -4
  38. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/ibm_mq.rs +38 -7
  39. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/ibm_mq_tls.rs +9 -1
  40. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/mariadb.rs +8 -2
  41. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/mod.rs +2 -2
  42. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/mysql.rs +3 -2
  43. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/postgres.rs +8 -2
  44. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration_test.rs +10 -10
  45. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  46. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  47. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  48. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/workflows/node.yml +0 -0
  49. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/workflows/publish-node.yml +0 -0
  50. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/workflows/python.yml +0 -0
  51. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/workflows/release.yml +0 -0
  52. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.github/workflows/test-matrix.yml +0 -0
  53. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/.gitignore +0 -0
  54. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/ARCHITECTURE.md +0 -0
  55. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/CONFIGURATION.md +0 -0
  56. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/CONTRIBUTING.md +0 -0
  57. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/LICENSE +0 -0
  58. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/LICENSE.python +0 -0
  59. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/benches/router_bench.rs +0 -0
  60. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/bindings-common/Cargo.toml +0 -0
  61. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/bindings-common/src/lib.rs +0 -0
  62. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/build.rs +0 -0
  63. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/claude.md +0 -0
  64. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/examples/ipc_worker_queue.yaml +0 -0
  65. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/mq-bridge.schema.json +0 -0
  66. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/mq_bridge/__init__.py +0 -0
  67. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/mq_bridge/config.py +0 -0
  68. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/mq_bridge/config.pyi +0 -0
  69. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/mq_bridge/py.typed +0 -0
  70. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/package-lock.json +0 -0
  71. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/LICENSE.python +0 -0
  72. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/analysis/bench_http_native.py +0 -0
  73. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/example.py +0 -0
  74. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/examples/bench_memory.py +0 -0
  75. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/examples/bench_memory.yaml +0 -0
  76. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/examples/consume_pull.py +0 -0
  77. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/examples/json_route.py +0 -0
  78. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/examples/memory.yaml +0 -0
  79. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/examples/raw_route.py +0 -0
  80. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/scripts/gen_config_types.py +0 -0
  81. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/tests/test_config_types.py +0 -0
  82. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/tests/test_packaging.py +0 -0
  83. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/tests/test_performance_smoke.py +0 -0
  84. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/tests/test_public_api.py +0 -0
  85. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/python/mq-bridge-py/uv.lock +0 -0
  86. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/analysis/HTTP_STREAM_ENDPOINT_PLAN.md +0 -0
  87. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/analysis/benches/metadata_bench.rs +0 -0
  88. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/analysis/http/analyze_xctrace_time_profile.py +0 -0
  89. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/analysis/http/http_flamegraph.py +0 -0
  90. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/analysis/http/mq_bridge_http_profile.rs +0 -0
  91. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/analysis/http/run_http_ladder.sh +0 -0
  92. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/httparena/README.md +0 -0
  93. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/httparena/frameworks/mq-bridge-py/Dockerfile +0 -0
  94. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/httparena/frameworks/mq-bridge-py/meta.json +0 -0
  95. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/httparena/frameworks/mq-bridge-py/server.py +0 -0
  96. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/techempower/Python/mq-bridge-py/README.md +0 -0
  97. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/techempower/Python/mq-bridge-py/benchmark_config.json +0 -0
  98. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/techempower/Python/mq-bridge-py/mq-bridge-py.dockerfile +0 -0
  99. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/techempower/Python/mq-bridge-py/server.py +0 -0
  100. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/techempower/README.md +0 -0
  101. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/techempower/postgres.yml +0 -0
  102. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/techempower/seed.sql +0 -0
  103. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/techempower/verify.sh +0 -0
  104. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/the-benchmarker/README.md +0 -0
  105. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/the-benchmarker/python/mq-bridge-py/config.yaml +0 -0
  106. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/the-benchmarker/python/mq-bridge-py/pyproject.toml +0 -0
  107. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/scripts/the-benchmarker/python/mq-bridge-py/server.py +0 -0
  108. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/command_handler.rs +0 -0
  109. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/connection_registry.rs +0 -0
  110. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/fanout.rs +0 -0
  111. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/grpc.proto +0 -0
  112. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/http_stream.rs +0 -0
  113. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/memory/endpoint.rs +0 -0
  114. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/memory/ipc_unix.rs +0 -0
  115. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/memory/ipc_windows.rs +0 -0
  116. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/memory/memory_transport.rs +0 -0
  117. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/memory/mod.rs +0 -0
  118. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/memory/transport.rs +0 -0
  119. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/null.rs +0 -0
  120. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/reader.rs +0 -0
  121. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/response.rs +0 -0
  122. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/sqlx.rs +0 -0
  123. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/static_endpoint.rs +0 -0
  124. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/stream_buffer.rs +0 -0
  125. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/switch.rs +0 -0
  126. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/endpoints/websocket.rs +0 -0
  127. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/errors.rs +0 -0
  128. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/event_handler.rs +0 -0
  129. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/event_store.rs +0 -0
  130. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/extensions.rs +0 -0
  131. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/lib.rs +0 -0
  132. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/buffer.rs +0 -0
  133. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/cookie_jar.rs +0 -0
  134. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/deduplication.rs +0 -0
  135. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/delay.rs +0 -0
  136. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/dlq.rs +0 -0
  137. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/limiter.rs +0 -0
  138. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/metrics.rs +0 -0
  139. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/mod.rs +0 -0
  140. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/random_panic.rs +0 -0
  141. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/retry.rs +0 -0
  142. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/middleware/weak_join.rs +0 -0
  143. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/outcomes.rs +0 -0
  144. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/publisher.rs +0 -0
  145. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/response.rs +0 -0
  146. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/route.rs +0 -0
  147. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/traits.rs +0 -0
  148. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/src/type_handler.rs +0 -0
  149. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/README.md +0 -0
  150. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/amqp.rs +0 -0
  151. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/aws.rs +0 -0
  152. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/config.toml +0 -0
  153. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/amqp.yml +0 -0
  154. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/aws.yml +0 -0
  155. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/ibm-mq-certs/client.crl +0 -0
  156. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/ibm-mq-certs/client.kdb +0 -0
  157. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/ibm-mq-certs/client.p12 +0 -0
  158. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/ibm-mq-certs/client.rdb +0 -0
  159. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/ibm-mq-certs/client.sth +0 -0
  160. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/ibm_mq.yml +0 -0
  161. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/ibm_mq_tls.yml +0 -0
  162. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/kafka-tls.yml +0 -0
  163. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/kafka.yml +0 -0
  164. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mariadb.yml +0 -0
  165. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mongodb-replica.yml +0 -0
  166. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mongodb-tls.yml +0 -0
  167. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mongodb.yml +0 -0
  168. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mosquitto.conf +0 -0
  169. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mosquitto_performance.conf +0 -0
  170. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mosquitto_persistence.conf +0 -0
  171. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mq_init_tls.mqsc +0 -0
  172. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mqtt.yml +0 -0
  173. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mqtt_performance.yml +0 -0
  174. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/mysql.yml +0 -0
  175. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/nats.yml +0 -0
  176. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/docker-compose/postgres.yml +0 -0
  177. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/file.rs +0 -0
  178. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/grpc_tls.rs +0 -0
  179. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/http.rs +0 -0
  180. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/http_tls.rs +0 -0
  181. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/ipc.rs +0 -0
  182. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/kafka.rs +0 -0
  183. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/kafka_tls.rs +0 -0
  184. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/logic_test.rs +0 -0
  185. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/memory.rs +0 -0
  186. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/mongodb.rs +0 -0
  187. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/mongodb_raw.rs +0 -0
  188. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/mongodb_tls.rs +0 -0
  189. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/mqtt.rs +0 -0
  190. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/nats.rs +0 -0
  191. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/performance_static.rs +0 -0
  192. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/route.rs +0 -0
  193. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/scripts/gen_certs.sh +0 -0
  194. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/sqlite.rs +0 -0
  195. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/tls_helpers.rs +0 -0
  196. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/websocket.rs +0 -0
  197. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/integration/zeromq.rs +0 -0
  198. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/memory_leak_test.rs +0 -0
  199. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/ref_test.rs +0 -0
  200. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/sqlite_test.rs +0 -0
  201. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/tls_example.rs +0 -0
  202. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/unit_tests.md +0 -0
  203. {mq_bridge_py_basic-0.3.1 → mq_bridge_py_basic-0.3.2}/tests/websocket_test.rs +0 -0
@@ -2,9 +2,10 @@ name: Benchmark
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [main]
6
- pull_request:
7
- branches: [main]
5
+ branches: [main, dev]
6
+ schedule:
7
+ # Nightly full benchmark run (04:00 UTC)
8
+ - cron: '0 4 * * *'
8
9
  workflow_dispatch:
9
10
 
10
11
  env:
@@ -24,17 +25,12 @@ jobs:
24
25
  - name: Install Rust toolchain
25
26
  uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
26
27
 
27
- - name: Cache cargo registry
28
- uses: actions/cache@v4
28
+ - name: Cache cargo build
29
+ uses: Swatinem/rust-cache@v2
29
30
  with:
30
- path: |
31
- ~/.cargo/bin/
32
- ~/.cargo/registry/cache/
33
- ~/.cargo/git/db/
34
- target/
35
- key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
36
- restore-keys: |
37
- ${{ runner.os }}-cargo-
31
+ # Shared with the release-mode integration jobs so the full-feature
32
+ # dependency compile is reused across jobs/runs instead of rebuilt.
33
+ shared-key: release-full
38
34
 
39
35
  - name: Pre-pull docker images
40
36
  run: |
@@ -107,35 +107,74 @@ jobs:
107
107
  - name: Memory leak soak (route commit-task JoinSet)
108
108
  run: cargo test --test memory_leak_test --features=full -- --ignored --nocapture
109
109
 
110
- # Integration tests (requires Docker) split into parallel jobs
110
+ # Compile the integration test binaries ONCE (release, full,test-utils) and
111
+ # publish them as a nextest archive. The three run jobs below download this
112
+ # archive instead of each recompiling the same binaries.
113
+ integration-build:
114
+ name: Integration — Build
115
+ runs-on: ubuntu-latest
116
+ steps:
117
+ - uses: actions/checkout@v4
118
+
119
+ - name: Install Rust toolchain
120
+ uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
121
+
122
+ - name: Install cargo-nextest
123
+ uses: taiki-e/install-action@v2
124
+ with:
125
+ tool: nextest
126
+
127
+ - name: Cache cargo build
128
+ uses: Swatinem/rust-cache@v2
129
+ with:
130
+ # Shared with the benchmark job so the full-feature dependency compile
131
+ # is reused across jobs/runs.
132
+ shared-key: release-full
133
+
134
+ - name: Build nextest archive
135
+ run: |
136
+ cargo nextest archive \
137
+ --release --features full,test-utils \
138
+ --test integration_test --test armature_integration \
139
+ --archive-file nextest-archive.tar.zst
140
+
141
+ - name: Upload nextest archive
142
+ uses: actions/upload-artifact@v4
143
+ with:
144
+ name: integration-nextest-archive
145
+ path: nextest-archive.tar.zst
146
+ retention-days: 1
147
+
148
+ # Integration tests (requires Docker) split into parallel jobs. Each runs the
149
+ # prebuilt nextest archive from integration-build — no recompilation.
111
150
  integration-others:
112
151
  name: Integration — Others
113
152
  runs-on: ubuntu-latest
153
+ needs: integration-build
114
154
  services:
115
155
  docker:
116
156
  image: docker:dind
117
157
  options: --privileged
118
158
  steps:
119
159
  - uses: actions/checkout@v4
120
-
121
- - name: Install Rust toolchain
122
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
123
-
124
- - name: Cache cargo build
125
- uses: Swatinem/rust-cache@v2
160
+
161
+ - name: Install cargo-nextest
162
+ uses: taiki-e/install-action@v2
126
163
  with:
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 }}
131
-
164
+ tool: nextest
165
+
166
+ - name: Download nextest archive
167
+ uses: actions/download-artifact@v4
168
+ with:
169
+ name: integration-nextest-archive
170
+
132
171
  - name: Pre-pull docker images
133
172
  run: |
134
173
  find tests/integration/docker-compose -name "*.yml" | xargs -P 5 -I {} docker compose -f {} pull -q
135
- - name: Install JDK (for keytool)
174
+ - name: Install JRE (for keytool)
136
175
  run: |
137
176
  sudo apt-get update
138
- sudo apt-get install -y default-jdk
177
+ sudo apt-get install -y default-jre-headless
139
178
 
140
179
  - name: Generate TLS certs for integration services
141
180
  run: |
@@ -146,15 +185,20 @@ jobs:
146
185
 
147
186
  - name: Run chaos/stability tests
148
187
  run: |
149
- cargo test --test integration_test --release --features full,test-utils -- --ignored --nocapture --test-threads=1 --exact test_all_chaos
188
+ cargo nextest run --archive-file nextest-archive.tar.zst \
189
+ --run-ignored all --no-capture \
190
+ -E 'binary(integration_test) & test(=test_all_chaos)'
150
191
 
151
192
  - name: Run remaining integration tests (excluding chaos & performance)
152
193
  run: |
153
- cargo test --test integration_test --release --features full,test-utils -- --ignored --nocapture --test-threads=1 --skip test_all_chaos --skip test_all_performance_pipeline --skip test_all_performance_direct
194
+ cargo nextest run --archive-file nextest-archive.tar.zst \
195
+ --run-ignored all --no-capture \
196
+ -E 'binary(integration_test) & not test(=test_all_chaos) & not test(=test_all_performance_pipeline) & not test(=test_all_performance_direct)'
154
197
 
155
198
  integration-performance:
156
199
  name: Integration — Performance
157
200
  runs-on: ubuntu-latest
201
+ needs: integration-build
158
202
  services:
159
203
  docker:
160
204
  image: docker:dind
@@ -162,24 +206,23 @@ jobs:
162
206
  steps:
163
207
  - uses: actions/checkout@v4
164
208
 
165
- - name: Install Rust toolchain
166
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
209
+ - name: Install cargo-nextest
210
+ uses: taiki-e/install-action@v2
211
+ with:
212
+ tool: nextest
167
213
 
168
- - name: Cache cargo build
169
- uses: Swatinem/rust-cache@v2
214
+ - name: Download nextest archive
215
+ uses: actions/download-artifact@v4
170
216
  with:
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 }}
217
+ name: integration-nextest-archive
175
218
 
176
219
  - name: Pre-pull docker images
177
220
  run: |
178
221
  find tests/integration/docker-compose -name "*.yml" | xargs -P 5 -I {} docker compose -f {} pull -q
179
- - name: Install JDK (for keytool)
222
+ - name: Install JRE (for keytool)
180
223
  run: |
181
224
  sudo apt-get update
182
- sudo apt-get install -y default-jdk
225
+ sudo apt-get install -y default-jre-headless
183
226
 
184
227
  - name: Generate TLS certs for integration services
185
228
  run: |
@@ -190,15 +233,20 @@ jobs:
190
233
 
191
234
  - name: Run pipeline performance tests
192
235
  run: |
193
- cargo test --test integration_test --release --features full,test-utils -- --ignored --nocapture --test-threads=1 --exact test_all_performance_pipeline
236
+ cargo nextest run --archive-file nextest-archive.tar.zst \
237
+ --run-ignored all --no-capture \
238
+ -E 'binary(integration_test) & test(=test_all_performance_pipeline)'
194
239
 
195
240
  - name: Run other performance tests
196
241
  run: |
197
- cargo test --test integration_test --release --features full,test-utils -- --ignored --nocapture --test-threads=1 --exact test_all_performance_direct
242
+ cargo nextest run --archive-file nextest-archive.tar.zst \
243
+ --run-ignored all --no-capture \
244
+ -E 'binary(integration_test) & test(=test_all_performance_direct)'
198
245
 
199
246
  integration-armature:
200
247
  name: Integration — Armature
201
248
  runs-on: ubuntu-latest
249
+ needs: integration-build
202
250
  services:
203
251
  docker:
204
252
  image: docker:dind
@@ -206,24 +254,23 @@ jobs:
206
254
  steps:
207
255
  - uses: actions/checkout@v4
208
256
 
209
- - name: Install Rust toolchain
210
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
257
+ - name: Install cargo-nextest
258
+ uses: taiki-e/install-action@v2
259
+ with:
260
+ tool: nextest
211
261
 
212
- - name: Cache cargo build
213
- uses: Swatinem/rust-cache@v2
262
+ - name: Download nextest archive
263
+ uses: actions/download-artifact@v4
214
264
  with:
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 }}
265
+ name: integration-nextest-archive
219
266
 
220
267
  - name: Pre-pull docker images
221
268
  run: |
222
269
  find tests/integration/docker-compose -name "*.yml" | xargs -P 5 -I {} docker compose -f {} pull -q
223
- - name: Install JDK (for keytool)
270
+ - name: Install JRE (for keytool)
224
271
  run: |
225
272
  sudo apt-get update
226
- sudo apt-get install -y default-jdk
273
+ sudo apt-get install -y default-jre-headless
227
274
 
228
275
  - name: Generate TLS certs for integration services
229
276
  run: |
@@ -234,7 +281,9 @@ jobs:
234
281
 
235
282
  - name: Run armature integration tests
236
283
  run: |
237
- cargo test --test armature_integration --release --features full,test-utils -- --ignored --nocapture --test-threads=1
284
+ cargo nextest run --archive-file nextest-archive.tar.zst \
285
+ --run-ignored all --no-capture \
286
+ -E 'binary(armature_integration)'
238
287
 
239
288
  # Documentation
240
289
  docs:
@@ -82,10 +82,16 @@ jobs:
82
82
  ls -la /opt/mqm/lib64/ || echo "MQ lib64 directory not found"
83
83
  echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
84
84
 
85
- - name: Build with IBM MQ feature
85
+ # `ibm-mq` (dlopen) is the shipping path and needs no SDK at build; the
86
+ # redist install above only provides headers for the static compile-check.
87
+ - name: Build with IBM MQ feature (dlopen)
86
88
  if: steps.mqclient.outputs.available == 'true'
87
89
  run: cargo build --features ibm-mq
88
90
 
91
+ - name: Build with IBM MQ feature (static link)
92
+ if: steps.mqclient.outputs.available == 'true'
93
+ run: cargo build --features ibm-mq-static
94
+
89
95
  - name: Run clippy on IBM MQ code
90
96
  if: steps.mqclient.outputs.available == 'true'
91
97
  run: cargo clippy --features ibm-mq -- -D warnings
@@ -156,13 +162,18 @@ jobs:
156
162
 
157
163
  - name: Run IBM MQ integration tests
158
164
  if: steps.mqclient.outputs.available == 'true'
165
+ env:
166
+ MQB_TEST_BACKEND: ibm-mq
159
167
  run: |
168
+ # The test fns are named test_all_* (filtered to ibm-mq via
169
+ # MQB_TEST_BACKEND). The previous test_ibm_mq_* filters matched no
170
+ # test, so this job silently ran zero tests.
160
171
  cargo test --test integration_test --features ibm-mq,test-utils,rustls-ring -- \
161
172
  --ignored --nocapture --test-threads=1 \
162
- test_ibm_mq_subscriber_logic \
163
- test_ibm_mq_performance_pipeline \
164
- test_ibm_mq_chaos \
165
- test_ibm_mq_status
173
+ test_all_subscriber_logic \
174
+ test_all_performance_pipeline \
175
+ test_all_chaos \
176
+ test_all_status
166
177
 
167
178
  - name: Run IBM MQ TLS tests
168
179
  if: steps.mqclient.outputs.available == 'true'
@@ -39,6 +39,25 @@ jobs:
39
39
  os: linux
40
40
  target: aarch64
41
41
  args: --release --out dist --find-interpreter --no-default-features
42
+ # musl/Alpine wheels for the basic package. basic uses the aws-lc-sys
43
+ # crypto provider (amqp + aws pull it in, same as full); aws-lc-sys
44
+ # compiles its C natively under musl given cmake + gcc, which
45
+ # before-script-linux installs in the musllinux container. The full
46
+ # wheel has no musl build (rdkafka makes it a separate effort).
47
+ - package: mq-bridge-py-basic
48
+ pyproject: pyproject-basic.toml
49
+ runner: ubuntu-latest
50
+ os: linux
51
+ target: x86_64
52
+ manylinux: musllinux_1_2
53
+ args: --release --out dist --find-interpreter --no-default-features
54
+ - package: mq-bridge-py-basic
55
+ pyproject: pyproject-basic.toml
56
+ runner: ubuntu-24.04-arm
57
+ os: linux
58
+ target: aarch64
59
+ manylinux: musllinux_1_2
60
+ args: --release --out dist --find-interpreter --no-default-features
42
61
  - package: mq-bridge-py-basic
43
62
  pyproject: pyproject-basic.toml
44
63
  runner: macos-15-intel
@@ -126,12 +145,18 @@ jobs:
126
145
  target: ${{ matrix.target }}
127
146
  args: ${{ matrix.args }}
128
147
  sccache: "true"
129
- manylinux: auto
148
+ manylinux: ${{ matrix.manylinux || 'auto' }}
149
+ # amqp/aws (and full's kafka) pull aws-lc-sys, which compiles its C with
150
+ # cmake. manylinux containers already ship it; the musllinux (Alpine)
151
+ # container does not, so install it there. Guarded so it's a no-op
152
+ # wherever cmake is already present. Only runs for linux builds.
153
+ before-script-linux: |
154
+ command -v cmake >/dev/null 2>&1 || apk add --no-cache cmake gcc g++ make musl-dev perl
130
155
 
131
156
  - name: Upload wheels
132
157
  uses: actions/upload-artifact@v4
133
158
  with:
134
- name: wheels-${{ matrix.package }}-${{ matrix.os }}-${{ matrix.target }}
159
+ name: wheels-${{ matrix.package }}-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.manylinux || 'auto' }}
135
160
  path: python/mq-bridge-py/dist
136
161
 
137
162
  build-sdists:
@@ -1576,6 +1576,29 @@ dependencies = [
1576
1576
  "syn",
1577
1577
  ]
1578
1578
 
1579
+ [[package]]
1580
+ name = "dlopen2"
1581
+ version = "0.8.2"
1582
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1583
+ checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4"
1584
+ dependencies = [
1585
+ "dlopen2_derive",
1586
+ "libc",
1587
+ "once_cell",
1588
+ "winapi",
1589
+ ]
1590
+
1591
+ [[package]]
1592
+ name = "dlopen2_derive"
1593
+ version = "0.4.3"
1594
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1595
+ checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f"
1596
+ dependencies = [
1597
+ "proc-macro2",
1598
+ "quote",
1599
+ "syn",
1600
+ ]
1601
+
1579
1602
  [[package]]
1580
1603
  name = "dlv-list"
1581
1604
  version = "0.5.2"
@@ -2775,6 +2798,10 @@ name = "libmqm-sys"
2775
2798
  version = "0.10.2"
2776
2799
  source = "registry+https://github.com/rust-lang/crates.io-index"
2777
2800
  checksum = "d888946090ed1e149d6978906143db293b9232c9a8cca0b369a1dc4668d9082a"
2801
+ dependencies = [
2802
+ "dlopen2",
2803
+ "libc",
2804
+ ]
2778
2805
 
2779
2806
  [[package]]
2780
2807
  name = "libsqlite3-sys"
@@ -3082,7 +3109,7 @@ dependencies = [
3082
3109
 
3083
3110
  [[package]]
3084
3111
  name = "mq-bridge"
3085
- version = "0.3.1"
3112
+ version = "0.3.2"
3086
3113
  dependencies = [
3087
3114
  "anyhow",
3088
3115
  "arc-swap",
@@ -3107,6 +3134,7 @@ dependencies = [
3107
3134
  "hyper-rustls 0.27.9",
3108
3135
  "hyper-util",
3109
3136
  "lapin",
3137
+ "libmqm-sys",
3110
3138
  "metrics",
3111
3139
  "metrics-util",
3112
3140
  "mongodb",
@@ -3149,7 +3177,7 @@ dependencies = [
3149
3177
 
3150
3178
  [[package]]
3151
3179
  name = "mq-bridge-bindings-common"
3152
- version = "0.3.1"
3180
+ version = "0.3.2"
3153
3181
  dependencies = [
3154
3182
  "anyhow",
3155
3183
  "fast-uuid-v7",
@@ -3161,7 +3189,7 @@ dependencies = [
3161
3189
 
3162
3190
  [[package]]
3163
3191
  name = "mq-bridge-node"
3164
- version = "0.3.1"
3192
+ version = "0.3.2"
3165
3193
  dependencies = [
3166
3194
  "anyhow",
3167
3195
  "async-trait",
@@ -3181,7 +3209,7 @@ dependencies = [
3181
3209
 
3182
3210
  [[package]]
3183
3211
  name = "mq-bridge-py"
3184
- version = "0.3.1"
3212
+ version = "0.3.2"
3185
3213
  dependencies = [
3186
3214
  "anyhow",
3187
3215
  "async-trait",
@@ -3206,6 +3234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3206
3234
  checksum = "298bea58d1259df694242dc973b07ce156660c897a7b3a421e324b1f37905cdc"
3207
3235
  dependencies = [
3208
3236
  "derive_more",
3237
+ "dlopen2",
3209
3238
  "libmqm-constants",
3210
3239
  "libmqm-default",
3211
3240
  "libmqm-sys",
@@ -16,7 +16,7 @@ members = ["bindings-common", "python/mq-bridge-py"]
16
16
  resolver = "2"
17
17
 
18
18
  [workspace.package]
19
- version = "0.3.1"
19
+ version = "0.3.2"
20
20
 
21
21
  [workspace.dependencies]
22
22
  anyhow = "1.0"
@@ -106,8 +106,13 @@ async-nats = { version = "0.49", optional = true }
106
106
  # or `tokio-rustls?/aws_lc_rs` from the crypto features below.
107
107
  rumqttc = { version = "0.25", default-features = false, features = ["use-rustls-no-provider"], optional = true }
108
108
 
109
- # IBM MQ
110
- mqi = { version = "0.3", optional = true }
109
+ # IBM MQ. default-features off so the version/provider features are chosen per
110
+ # feature below: `ibm-mq` loads the client at runtime via dlopen (no SDK at
111
+ # build), `ibm-mq-static` links the IBM C client at build (needs the SDK).
112
+ mqi = { version = "0.3", optional = true, default-features = false }
113
+ # Direct dep only for the dlopen path, so we can name MqmContainer to load the
114
+ # client at runtime. Features are unified from mqi/dlopen2.
115
+ libmqm-sys = { version = "0.10", optional = true, default-features = false }
111
116
 
112
117
  # HTTP
113
118
  arc-swap = { version = "1", optional = true }
@@ -154,7 +159,11 @@ protoc-bin-vendored = { version = "3.0", optional = true }
154
159
  [features]
155
160
  default = []
156
161
  middleware = ["metrics", "dedup"]
157
- full = ["middleware", "mongodb", "kafka", "amqp", "nats", "mqtt", "http", "aws", "zeromq", "sled", "grpc", "sqlx", "websocket", "rustls-aws-lc"]
162
+ # ibm-mq (dlopen) is safe to include: it needs no IBM SDK at build, and at
163
+ # runtime the client is loaded lazily only when an ibm-mq endpoint is opened
164
+ # (clear error if the client isn't installed). Use `ibm-mq-static` instead for
165
+ # static linking.
166
+ full = ["middleware", "mongodb", "kafka", "amqp", "nats", "mqtt", "http", "aws", "zeromq", "sled", "grpc", "sqlx", "websocket", "rustls-aws-lc", "ibm-mq"]
158
167
 
159
168
  # Functionality features
160
169
  dedup = ["sled"]
@@ -183,7 +192,16 @@ mqtt = ["rumqttc", "rustls", "tokio-rustls", "rustls-pemfile", "url"]
183
192
  http = ["dep:arc-swap", "hyper", "hyper-util", "hyper-rustls", "http-body-util", "dep:http-body", "rustls", "tokio-rustls", "rustls-pemfile", "webpki-roots", "h2", "dep:flate2", "dep:base64"]
184
193
  websocket = ["dep:tokio-websockets"]
185
194
  aws = ["aws-config", "aws-sdk-sqs", "aws-sdk-sns", "dep:base64"]
186
- ibm-mq = ["mqi"]
195
+ # Runtime-loaded IBM MQ client via dlopen: builds with no SDK present; the IBM
196
+ # redistributable client is only needed at runtime, and only if an ibm-mq
197
+ # endpoint is actually used.
198
+ # mqi/mqai is required because mqi's always-compiled (doc-hidden) test module
199
+ # references MqaiLibrary on the dlopen2 path; it adds only pregen bindings, no SDK.
200
+ ibm-mq = ["dep:mqi", "mqi/dlopen2", "mqi/mqai", "mqi/mqc_9_2_0_0", "dep:libmqm-sys", "libmqm-sys/dlopen2"]
201
+ # Static-link IBM MQ client (needs the IBM SDK at build time). mqc_9_2_0_0 matches
202
+ # the crate's previous default so behavior is unchanged. Enabling this alongside
203
+ # `ibm-mq` (dlopen) forces static linking — the SDK is then required at build.
204
+ ibm-mq-static = ["dep:mqi", "mqi/link", "mqi/mqc_9_2_0_0"]
187
205
  zeromq = ["dep:zeromq"]
188
206
  # Note: The grpc feature requires protobuf-src which can significantly increase build times.
189
207
  grpc = ["tonic", "prost", "tokio-stream", "tonic-prost", "tonic-prost-build", "protoc-bin-vendored", "rustls"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mq-bridge-py-basic
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: License :: OSI Approved :: MIT License
6
6
  Classifier: Programming Language :: Python :: 3
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.13
12
12
  Classifier: Programming Language :: Rust
13
13
  Requires-Dist: typing-extensions>=4.0
14
14
  License-File: LICENSE.python
15
- Summary: Python bindings for mq-bridge (basic: all-platform messaging set)
15
+ Summary: Python bindings for mq-bridge (basic: all-platform set — HTTP, NATS, MQTT, WebSocket, ZeroMQ, MongoDB, AMQP, AWS SQS/SNS)
16
16
  Keywords: messaging,tokio,pyo3,maturin,mq-bridge
17
17
  License: MIT
18
18
  Requires-Python: >=3.9
@@ -161,6 +161,48 @@ brokers never set it.
161
161
  > Commit after each batch you have durably handled (as in the loops above). If a
162
162
  > batch fails downstream, simply *don't* commit it — it will be redelivered.
163
163
 
164
+ ### Per-batch tokens: `poll_batch` / `ack` / `nack`
165
+
166
+ When you need to ack or release **specific** batches (rather than everything since
167
+ the last commit), use the token form. `poll_batch(max, timeout_ms)` returns
168
+ `(messages, token)`; `ack(token)` commits just that batch, and `nack(token)`
169
+ releases it for redelivery (`nack()` with no argument nacks every outstanding
170
+ batch). This is the shape a [`dlt`](https://dlthub.com) resource wants — poll →
171
+ yield records → load package commits → `ack(token)` — and is demonstrated in
172
+ [`examples/dlt_source.py`](examples/dlt_source.py) with the wiring brief in
173
+ [`examples/OMNILOAD_INTEGRATION.md`](examples/OMNILOAD_INTEGRATION.md).
174
+
175
+ ```python
176
+ messages, token = consumer.poll_batch(max=500, timeout_ms=1000) # ([], None) on timeout
177
+ if token is not None: # nothing returned on an idle timeout
178
+ # ... persist the batch downstream ...
179
+ consumer.ack(token) # or consumer.nack(token) to redeliver
180
+ ```
181
+
182
+ Tokens stay outstanding until acked/nacked; `commit()` still acks every
183
+ outstanding batch at once, so don't mix the two styles on one consumer. On
184
+ cumulative-ack transports (Kafka), acking a later batch would implicitly ack the
185
+ earlier ones, so `ack(token)` must follow receive order — acking out of order
186
+ raises; ack the oldest outstanding batch first, or use `commit()`. Transports
187
+ that ack each batch individually (NATS JetStream, AMQP, MQTT) accept any order.
188
+
189
+ > **At-least-once + idempotent merge.** Redelivery (after a nack, a missed
190
+ > `commit()`, or an expired broker ack deadline) means a record can arrive twice.
191
+ > A downstream loader must dedup on a stable key — `message.id` is globally unique
192
+ > per source position (Kafka `partition:offset`, NATS `stream_sequence`, AMQP
193
+ > delivery tag) and makes a natural primary key. Source cursor fields are also
194
+ > available in `message.metadata` (`mqb.src.kafka_topic`/`mqb.src.kafka_offset`, `mqb.src.nats_subject`/`mqb.src.nats_stream_sequence`,
195
+ > `mqb.src.amqp_routing_key`/`mqb.src.amqp_delivery_tag`) when you opt in by setting
196
+ > the `MQB_SOURCE_METADATA=1` environment variable (off by default).
197
+ >
198
+ > **Ack deadlines vs slow loads.** JetStream `AckWait` (default 30s), AMQP
199
+ > prefetch/consumer-timeout and MQTT inflight windows each bound how long a batch
200
+ > may stay un-acked. Keep `batch_size × per-record handling cost` under the
201
+ > smallest deadline (or raise it in the endpoint config); past it the broker
202
+ > redelivers — correctness is preserved by idempotent merge, but reload work is
203
+ > wasted. **Kafka has no per-message nack:** `nack` there leaves the offset
204
+ > unadvanced, so redelivery happens on the next run/rebalance, not immediately.
205
+
164
206
  `consumer.status()` returns a snapshot dict (`healthy`, `target`, `pending`,
165
207
  `capacity`, `error`, `details`). `pending` is the broker backlog/lag where the
166
208
  transport reports it — Kafka offset lag, AMQP queue depth, NATS JetStream
@@ -57,7 +57,7 @@ For implementation details and quick start examples for each usage type, see the
57
57
  ## Features
58
58
 
59
59
  * **Supported Backends**: Kafka, NATS, AMQP (RabbitMQ), MQTT, MongoDB, SQL Databases (PostgreSQL, MySQL, SQLite via sqlx), HTTP, WebSocket, ZeroMQ, Files, AWS (SQS/SNS), IBM MQ, and in-memory channels.
60
- > **Note**: IBM MQ is not included in the `full` feature set. It requires the `ibm-mq` feature and the IBM MQ Client library. See [mqi crate](https://crates.io/crates/mqi/) for installation details.
60
+ > **Note**: IBM MQ is included in the `full` feature set via the `ibm-mq` feature, which loads the IBM MQ client library at runtime via dlopen — **no IBM SDK is needed to build**. The IBM MQ redistributable client only has to be present at runtime, and only if you actually use an IBM MQ endpoint (it is loaded lazily on first connect). If the client is missing, the affected route fails fast with a non-retryable error instead of reconnecting forever. The loader finds the client via the platform default name, `MQ_INSTALLATION_PATH` (e.g. `/opt/mqm`), or an explicit `MQB_IBM_MQ_LIB` path. To link the client statically at build time instead, use the `ibm-mq-static` feature (requires the IBM MQ SDK). See the [mqi crate](https://crates.io/crates/mqi/) for details.
61
61
  * **Configuration**: Routes can be defined via YAML, JSON or environment variables.
62
62
  * **Programmable Logic**: Inject custom Rust handlers to transform or filter messages in-flight.
63
63
  * **Batching**: Every endpoint uses the same `send_batch` / `receive_batch` shape. Routes default to single-message batches, but can switch to larger batches with `batch_size`.
@@ -357,7 +357,7 @@ pub mod zeromq_helper {
357
357
  }
358
358
  }
359
359
 
360
- #[cfg(feature = "ibm-mq")]
360
+ #[cfg(any(feature = "ibm-mq-static", feature = "ibm-mq"))]
361
361
  pub mod ibm_mq_helper {
362
362
  use mq_bridge::endpoints::ibm_mq::{IbmMqConsumer, IbmMqPublisher};
363
363
  use mq_bridge::models::IbmMqConfig;
@@ -1027,18 +1027,30 @@ fn performance_benchmarks(c: &mut Criterion) {
1027
1027
  PERF_TEST_CONCURRENCY,
1028
1028
  std::time::Duration::from_millis(10)
1029
1029
  );
1030
- bench_backend!(
1031
- "ibm-mq",
1032
- "ibm-mq",
1033
- "tests/integration/docker-compose/ibm_mq.yml",
1034
- ibm_mq_helper,
1035
- group,
1036
- &rt,
1037
- &BENCH_RESULTS,
1038
- PERF_TEST_MESSAGE_COUNT,
1039
- PERF_TEST_CONCURRENCY,
1040
- std::time::Duration::from_millis(100)
1041
- );
1030
+ #[cfg(any(feature = "ibm-mq-static", feature = "ibm-mq"))]
1031
+ {
1032
+ // Skip when the IBM MQ client library isn't loadable (dlopen build with no
1033
+ // client installed); otherwise the helper would panic on first connect.
1034
+ if mq_bridge::endpoints::ibm_mq::ibm_mq_client_available() {
1035
+ bench_backend!(
1036
+ "",
1037
+ "ibm-mq",
1038
+ "tests/integration/docker-compose/ibm_mq.yml",
1039
+ ibm_mq_helper,
1040
+ group,
1041
+ &rt,
1042
+ &BENCH_RESULTS,
1043
+ PERF_TEST_MESSAGE_COUNT,
1044
+ PERF_TEST_CONCURRENCY,
1045
+ std::time::Duration::from_millis(100)
1046
+ );
1047
+ } else {
1048
+ eprintln!(
1049
+ "Skipping IBM MQ benchmark: client library not found. \
1050
+ Install the IBM MQ redistributable client or set MQB_IBM_MQ_LIB."
1051
+ );
1052
+ }
1053
+ }
1042
1054
  bench_backend!(
1043
1055
  "memory",
1044
1056
  memory_helper,
@@ -1,5 +1,5 @@
1
1
  from types import TracebackType
2
- from typing import Any, Callable, Dict, List, Mapping, Optional, Type, Union
2
+ from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, Union
3
3
 
4
4
  __version__: str
5
5
 
@@ -127,6 +127,34 @@ class Consumer:
127
127
  you must call it (see ``commit``)."""
128
128
  ...
129
129
 
130
+ def poll_batch(
131
+ self,
132
+ max: int = ...,
133
+ timeout_ms: Optional[int] = ...,
134
+ ) -> Tuple[List["Message"], Optional[int]]:
135
+ """Like ``poll()``, but also return the batch's token so it can be acked
136
+ or nacked individually with ``ack(token)`` / ``nack(token)`` — the shape a
137
+ ``dlt`` resource wants (``poll → yield → commit load package →
138
+ ack(token)``). Returns ``(messages, token)``, or ``([], None)`` on timeout
139
+ or end-of-stream. Tokens stay outstanding until acked/nacked; ``commit()``
140
+ still acks every outstanding batch at once, so don't mix the two styles on
141
+ one consumer."""
142
+ ...
143
+
144
+ def ack(self, token: int) -> None:
145
+ """Ack a single batch by the token from ``poll_batch()``, advancing the
146
+ consumer offset for just that batch. Raises if the token is unknown
147
+ (already acked/nacked, or never polled)."""
148
+ ...
149
+
150
+ def nack(self, token: Optional[int] = ...) -> None:
151
+ """Negatively acknowledge so the broker can redeliver. With a ``token``,
152
+ nacks just that batch; without one, nacks every outstanding batch (oldest
153
+ first). On Kafka there is no per-message nack — this leaves the offset
154
+ unadvanced, so redelivery happens on the next run/rebalance, not at
155
+ once."""
156
+ ...
157
+
130
158
  def commit(self) -> None:
131
159
  """Ack every batch returned by ``poll()`` since the last ``commit()``,
132
160
  advancing the consumer offset.