mq-bridge-py-basic 0.3.4__tar.gz → 0.3.5__tar.gz

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