mq-bridge-py-basic 0.3.8__tar.gz → 0.3.10__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 (273) hide show
  1. mq_bridge_py_basic-0.3.10/.github/dependabot.yml +11 -0
  2. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/workflows/ci.yml +27 -0
  3. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.gitignore +4 -0
  4. mq_bridge_py_basic-0.3.10/CHANGELOG.md +76 -0
  5. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/Cargo.lock +30 -10
  6. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/Cargo.toml +16 -7
  7. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/PKG-INFO +3 -3
  8. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/README.md +80 -6
  9. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/REFERENCE.md +131 -42
  10. mq_bridge_py_basic-0.3.10/SECURITY.md +104 -0
  11. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/benches/ETL_BENCHMARKS.md +5 -5
  12. mq_bridge_py_basic-0.3.10/deny.toml +97 -0
  13. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/mq-bridge.schema.json +70 -8
  14. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/mq_bridge/__init__.pyi +8 -0
  15. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/mq_bridge/config.pyi +39 -0
  16. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/Cargo.toml +17 -2
  17. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/README.md +2 -2
  18. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/examples/dlt_source.py +4 -8
  19. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/src/lib.rs +76 -9
  20. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/tests/test_public_api.py +10 -7
  21. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/httparena/README.md +1 -1
  22. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/httparena/frameworks/mq-bridge-py/server.py +1 -1
  23. mq_bridge_py_basic-0.3.10/src/canonical_message.rs +854 -0
  24. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/checkpoint.rs +24 -5
  25. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/command_handler.rs +9 -1
  26. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/amqp.rs +20 -5
  27. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/clickhouse.rs +4 -3
  28. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/file/mod.rs +679 -167
  29. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/file/tests.rs +543 -3
  30. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/http/publisher.rs +23 -1
  31. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/http/stream.rs +9 -5
  32. mq_bridge_py_basic-0.3.10/src/endpoints/kafka.rs +1927 -0
  33. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/memory/framed.rs +81 -13
  34. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/mod.rs +232 -29
  35. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/mongodb/consumer.rs +14 -9
  36. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/mongodb/readers.rs +105 -16
  37. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/mongodb/tests.rs +67 -0
  38. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/nats.rs +77 -19
  39. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/object_store.rs +264 -47
  40. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/postgres/mod.rs +125 -21
  41. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/postgres/replication.rs +83 -23
  42. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/redis_streams.rs +130 -19
  43. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/sqlx/dedup.rs +23 -3
  44. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/sqlx/mod.rs +413 -157
  45. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/sqlx/tests.rs +302 -0
  46. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/structural/reader.rs +17 -2
  47. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/structural/stream_buffer.rs +34 -13
  48. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/zeromq/omq.rs +12 -3
  49. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/zeromq/zmq.rs +25 -0
  50. mq_bridge_py_basic-0.3.10/src/errors.rs +172 -0
  51. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/lib.rs +4 -1
  52. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/cookie_jar.rs +73 -5
  53. mq_bridge_py_basic-0.3.10/src/middleware/deduplication.rs +1128 -0
  54. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/dlq.rs +141 -2
  55. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/encryption.rs +133 -24
  56. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/limiter.rs +31 -1
  57. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/mod.rs +43 -3
  58. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/retry.rs +90 -5
  59. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/transform/compiled.rs +133 -15
  60. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/transform/mod.rs +7 -0
  61. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/transform/path.rs +26 -0
  62. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/transform/schema.rs +18 -6
  63. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/transform/tests.rs +333 -7
  64. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/models.rs +78 -5
  65. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/route.rs +763 -7
  66. mq_bridge_py_basic-0.3.10/src/support/base64_engine.rs +101 -0
  67. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/support/compression.rs +1 -1
  68. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/support/connection_registry.rs +3 -3
  69. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/support/crypto.rs +3 -5
  70. mq_bridge_py_basic-0.3.10/src/support/crypto_envelope.rs +17 -0
  71. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/support/interpolation.rs +260 -17
  72. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/support/mod.rs +3 -0
  73. mq_bridge_py_basic-0.3.10/src/support/source_ranges.rs +448 -0
  74. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/test_utils.rs +30 -1
  75. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/traits.rs +6 -0
  76. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/armature_integration.rs +16 -0
  77. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/mariadb.rs +16 -0
  78. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/mod.rs +32 -0
  79. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/mongodb.rs +40 -1
  80. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/mqtt.rs +9 -3
  81. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/mysql.rs +137 -0
  82. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/nats.rs +48 -0
  83. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/object_store.rs +102 -0
  84. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/postgres_cdc.rs +114 -0
  85. mq_bridge_py_basic-0.3.10/tests/integration/redis_streams.rs +434 -0
  86. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/route.rs +25 -23
  87. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration_test.rs +95 -0
  88. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/reference_docs_test.rs +134 -177
  89. mq_bridge_py_basic-0.3.8/src/canonical_message.rs +0 -471
  90. mq_bridge_py_basic-0.3.8/src/endpoints/kafka.rs +0 -986
  91. mq_bridge_py_basic-0.3.8/src/errors.rs +0 -96
  92. mq_bridge_py_basic-0.3.8/src/middleware/deduplication.rs +0 -582
  93. mq_bridge_py_basic-0.3.8/tests/integration/redis_streams.rs +0 -227
  94. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  95. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  96. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  97. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/workflows/benchmark.yml +0 -0
  98. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/workflows/ibm-mq.yml +0 -0
  99. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/workflows/node.yml +0 -0
  100. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/workflows/publish-node.yml +0 -0
  101. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/workflows/publish-python.yml +0 -0
  102. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/workflows/python.yml +0 -0
  103. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/workflows/release.yml +0 -0
  104. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/.github/workflows/test-matrix.yml +0 -0
  105. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/ARCHITECTURE.md +0 -0
  106. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/CLAUDE.md +0 -0
  107. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/CONFIGURATION.md +0 -0
  108. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/CONTRIBUTING.md +0 -0
  109. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/LICENSE +0 -0
  110. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/LICENSE.python +0 -0
  111. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/benches/performance_bench.rs +0 -0
  112. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/benches/router_bench.rs +0 -0
  113. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/benches/zeromq_backends.rs +0 -0
  114. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/bindings-common/Cargo.toml +0 -0
  115. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/bindings-common/src/lib.rs +0 -0
  116. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/bindings-common/src/logging.rs +0 -0
  117. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/build.rs +0 -0
  118. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/examples/ipc_worker_queue.yaml +0 -0
  119. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/mq_bridge/__init__.py +0 -0
  120. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/mq_bridge/config.py +0 -0
  121. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/mq_bridge/py.typed +0 -0
  122. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/package-lock.json +0 -0
  123. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/pyproject.toml +0 -0
  124. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/LICENSE.python +0 -0
  125. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/analysis/bench_http_native.py +0 -0
  126. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/example.py +0 -0
  127. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/examples/bench_memory.py +0 -0
  128. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/examples/bench_memory.yaml +0 -0
  129. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/examples/consume_pull.py +0 -0
  130. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/examples/json_route.py +0 -0
  131. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/examples/memory.yaml +0 -0
  132. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/examples/raw_route.py +0 -0
  133. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/pyproject-basic.toml +0 -0
  134. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/pyproject-reduced.toml +0 -0
  135. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/scripts/gen_config_types.py +0 -0
  136. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/tests/test_config_types.py +0 -0
  137. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/tests/test_logging.py +0 -0
  138. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/tests/test_packaging.py +0 -0
  139. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/tests/test_performance_smoke.py +0 -0
  140. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/tests/test_routes.py +0 -0
  141. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/python/mq-bridge-py/uv.lock +0 -0
  142. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/analysis/benches/metadata_bench.rs +0 -0
  143. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/analysis/debug_ibm_mq_tls.rs +0 -0
  144. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/analysis/http/analyze_xctrace_time_profile.py +0 -0
  145. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/analysis/http/http_flamegraph.py +0 -0
  146. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/analysis/http/mq_bridge_http_profile.rs +0 -0
  147. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/analysis/http/run_http_ladder.sh +0 -0
  148. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/ci/format_bench_output.sh +0 -0
  149. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/httparena/frameworks/mq-bridge-py/Dockerfile +0 -0
  150. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/httparena/frameworks/mq-bridge-py/meta.json +0 -0
  151. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/techempower/Python/mq-bridge-py/README.md +0 -0
  152. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/techempower/Python/mq-bridge-py/benchmark_config.json +0 -0
  153. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/techempower/Python/mq-bridge-py/mq-bridge-py.dockerfile +0 -0
  154. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/techempower/Python/mq-bridge-py/server.py +0 -0
  155. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/techempower/README.md +0 -0
  156. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/techempower/postgres.yml +0 -0
  157. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/techempower/seed.sql +0 -0
  158. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/techempower/verify.sh +0 -0
  159. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/the-benchmarker/README.md +0 -0
  160. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/the-benchmarker/python/mq-bridge-py/config.yaml +0 -0
  161. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/the-benchmarker/python/mq-bridge-py/pyproject.toml +0 -0
  162. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/scripts/the-benchmarker/python/mq-bridge-py/server.py +0 -0
  163. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/aws.rs +0 -0
  164. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/grpc.proto +0 -0
  165. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/grpc.rs +0 -0
  166. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/http/mod.rs +0 -0
  167. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/http/tests.rs +0 -0
  168. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/ibm_mq.rs +0 -0
  169. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/memory/endpoint.rs +0 -0
  170. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/memory/ipc_unix.rs +0 -0
  171. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/memory/ipc_windows.rs +0 -0
  172. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/memory/memory_transport.rs +0 -0
  173. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/memory/mod.rs +0 -0
  174. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/memory/transport.rs +0 -0
  175. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/mongodb/dedup.rs +0 -0
  176. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/mongodb/mod.rs +0 -0
  177. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/mongodb/publisher.rs +0 -0
  178. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/mqtt.rs +0 -0
  179. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/poll.rs +0 -0
  180. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/postgres/VENDORED.md +0 -0
  181. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/postgres/pgoutput/decoder.rs +0 -0
  182. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/postgres/pgoutput/messages.rs +0 -0
  183. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/postgres/pgoutput/mod.rs +0 -0
  184. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/postgres/pgoutput/registry.rs +0 -0
  185. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/postgres/pgoutput/values.rs +0 -0
  186. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/postgres/state.rs +0 -0
  187. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/sled.rs +0 -0
  188. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/structural/fanout.rs +0 -0
  189. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/structural/mod.rs +0 -0
  190. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/structural/null.rs +0 -0
  191. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/structural/request.rs +0 -0
  192. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/structural/response.rs +0 -0
  193. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/structural/static_endpoint.rs +0 -0
  194. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/structural/switch.rs +0 -0
  195. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/websocket.rs +0 -0
  196. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/zeromq/codec.rs +0 -0
  197. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/endpoints/zeromq/mod.rs +0 -0
  198. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/event_handler.rs +0 -0
  199. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/event_store.rs +0 -0
  200. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/extensions.rs +0 -0
  201. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/buffer.rs +0 -0
  202. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/compression.rs +0 -0
  203. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/delay.rs +0 -0
  204. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/metrics.rs +0 -0
  205. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/random_panic.rs +0 -0
  206. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/transform/coerce.rs +0 -0
  207. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/transform/error.rs +0 -0
  208. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/middleware/weak_join.rs +0 -0
  209. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/outcomes.rs +0 -0
  210. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/publisher.rs +0 -0
  211. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/response.rs +0 -0
  212. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/src/type_handler.rs +0 -0
  213. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/README.md +0 -0
  214. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/amqp.rs +0 -0
  215. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/aws.rs +0 -0
  216. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/clickhouse.rs +0 -0
  217. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/config.toml +0 -0
  218. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/amqp.yml +0 -0
  219. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/aws.yml +0 -0
  220. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/clickhouse.yml +0 -0
  221. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/ibm-mq-certs/client.crl +0 -0
  222. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/ibm-mq-certs/client.kdb +0 -0
  223. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/ibm-mq-certs/client.p12 +0 -0
  224. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/ibm-mq-certs/client.rdb +0 -0
  225. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/ibm-mq-certs/client.sth +0 -0
  226. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/ibm_mq.yml +0 -0
  227. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/ibm_mq_tls.yml +0 -0
  228. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/kafka-tls.yml +0 -0
  229. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/kafka.yml +0 -0
  230. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mariadb.yml +0 -0
  231. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mongodb-replica.yml +0 -0
  232. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mongodb-tls.yml +0 -0
  233. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mongodb.yml +0 -0
  234. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mosquitto.conf +0 -0
  235. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mosquitto_performance.conf +0 -0
  236. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mosquitto_persistence.conf +0 -0
  237. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mq_init.mqsc +0 -0
  238. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mq_init_tls.mqsc +0 -0
  239. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mqtt.yml +0 -0
  240. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mqtt_performance.yml +0 -0
  241. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/mysql.yml +0 -0
  242. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/nats.yml +0 -0
  243. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/object_store.yml +0 -0
  244. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/postgres.yml +0 -0
  245. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/postgres_cdc.yml +0 -0
  246. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/docker-compose/redis.yml +0 -0
  247. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/file.rs +0 -0
  248. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/grpc.rs +0 -0
  249. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/grpc_tls.rs +0 -0
  250. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/http.rs +0 -0
  251. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/http_tls.rs +0 -0
  252. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/ibm_mq.rs +0 -0
  253. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/ibm_mq_tls.rs +0 -0
  254. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/ipc.rs +0 -0
  255. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/kafka.rs +0 -0
  256. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/kafka_tls.rs +0 -0
  257. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/logic_test.rs +0 -0
  258. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/memory.rs +0 -0
  259. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/mongodb_raw.rs +0 -0
  260. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/mongodb_tls.rs +0 -0
  261. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/performance_static.rs +0 -0
  262. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/postgres.rs +0 -0
  263. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/scripts/gen_certs.sh +0 -0
  264. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/sqlite.rs +0 -0
  265. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/tls_helpers.rs +0 -0
  266. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/websocket.rs +0 -0
  267. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/integration/zeromq.rs +0 -0
  268. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/memory_leak_test.rs +0 -0
  269. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/ref_test.rs +0 -0
  270. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/sqlite_test.rs +0 -0
  271. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/tls_example.rs +0 -0
  272. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/unit_tests.md +0 -0
  273. {mq_bridge_py_basic-0.3.8 → mq_bridge_py_basic-0.3.10}/tests/websocket_test.rs +0 -0
@@ -0,0 +1,11 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "cargo" # See documentation for possible values
9
+ directory: "/" # Location of package manifests
10
+ schedule:
11
+ interval: "weekly"
@@ -305,3 +305,30 @@ jobs:
305
305
 
306
306
  - name: Build documentation (deny warnings)
307
307
  run: RUSTDOCFLAGS="-D warnings" cargo doc --all-features --no-deps
308
+
309
+ # Supply chain: licenses, banned crates, source registries, RustSec advisories
310
+ supply-chain:
311
+ name: Supply Chain
312
+ runs-on: ubuntu-latest
313
+ permissions:
314
+ contents: read
315
+ steps:
316
+ - uses: actions/checkout@v4
317
+ with:
318
+ persist-credentials: false
319
+
320
+ # Hard gate: an unapproved license, a banned crate or a non-crates.io
321
+ # source is always a real, actionable problem in this repo.
322
+ - name: Licenses / bans / sources
323
+ uses: EmbarkStudios/cargo-deny-action@v2
324
+ with:
325
+ command: check bans licenses sources
326
+
327
+ # Informational: every current advisory is in a transitive dependency we
328
+ # cannot upgrade ourselves, and RustSec updates daily. SECURITY.md records
329
+ # the per-feature reachability analysis; deny.toml carries the reasoning.
330
+ - name: Advisories (informational)
331
+ continue-on-error: true
332
+ uses: EmbarkStudios/cargo-deny-action@v2
333
+ with:
334
+ command: check advisories
@@ -48,3 +48,7 @@ tests/**/*.pkcs12
48
48
  .pytest_cache/
49
49
  python/mq-bridge-py/.venv/
50
50
  python/mq-bridge-py/dist/
51
+
52
+ # Local build setup (shared cargo target dir with mq-bridge-app), not for git
53
+ .cargo/
54
+ .vscode/
@@ -0,0 +1,76 @@
1
+ # Changelog
2
+
3
+ All notable changes to `mq-bridge`. Newest first.
4
+
5
+ ## 0.3.10
6
+
7
+
8
+ ### Changed
9
+
10
+ - DLQ and output middleware now run around the publish step, not inside the handler, so a
11
+ handler failure is no longer retried by the output chain. The tradeoff: a handler failure
12
+ now skips the output middleware entirely, so `dlq` on the output endpoint cannot capture
13
+ it — only publish failures reach the DLQ. Put a `dlq` on the input endpoint to catch
14
+ handler failures.
15
+ - Errors carry their full cause chain instead of only the outermost context.
16
+ - `deduplication` on an **output** endpoint is now a startup error instead of a warning and a
17
+ silent no-op. Move it to the route's input endpoint.
18
+ - `limiter` paces a single `send_batch` by its own message count, so one large batch is no
19
+ longer a free burst. Sustained throughput is unchanged.
20
+ - A `message_id` that is neither a UUID nor a `u128` is hashed to a stable id rather than
21
+ making the whole JSON envelope unparseable.
22
+
23
+ ### Performance
24
+
25
+ - **Kafka consumer**: a long-lived prefetch task reads librdkafka continuously into a bounded
26
+ channel, instead of rebuilding the stream inside every `receive_batch`. librdkafka only keeps
27
+ requesting records while its queue is drained, so every pause the pipeline took — a transform,
28
+ a slow sink — was also a pause in fetching, and the fetch rate collapsed to well under what the
29
+ broker could serve. Batch offsets are also recorded once per partition rather than once per
30
+ message, which was O(n²) in the batch and allocated two `CString`s per record.
31
+ Kafka → transform → file: **192,854 → 824,983 rows/s** on the 1M-row, four-partition
32
+ benchmark, from 0.35x to 1.51x Arroyo on identical output. A 16,384-message passthrough
33
+ batch went from 11.5s to 2.2s, and from 10.4s to 0.9s of CPU.
34
+ - **Postgres / sqlx**: `test_before_acquire(false)` on the pool, zero-copy row encoding via
35
+ a prebuilt `JsonRowSchema`, and prebuilt first/next page queries on the cursor path.
36
+ - **Command handler**: `send_batch` no longer loops per-message `send()` (≈8x on batched routes).
37
+ - **File**: single-pass byte-array decode, faster CSV writes, compression sniffing.
38
+ - **Deduplication**: the two-phase reserve/commit no longer writes to the store twice per
39
+ message. Reservations are held in memory — sled takes an exclusive file lock on its directory,
40
+ so a claim only ever has to be visible to this process — leaving one write per message, on
41
+ commit.
42
+
43
+ ### Fixed
44
+
45
+ - `insert_query` batch inserts dropped anything but a bare token from the `VALUES` tuple, so
46
+ `decode(${payload:x}, 'base64')` and casts were silently discarded — a binary (`bytea`)
47
+ column could not be written. The batch path now keeps the user's SQL and falls back to
48
+ iterative inserts when the tuple contains an expression.
49
+ - A payload string containing an embedded NUL byte was dropped by the driver and stored as
50
+ SQL `NULL` while the route reported success. It is now rejected as non-retryable.
51
+ - Database errors were rendered twice, because `sqlx` errors already display their own
52
+ source. Cause chains now skip a link an earlier one already contains.
53
+ - `cookie_jar`'s `inject_metadata` resolved only a bare stored name, so the namespaced
54
+ `cookie.<name>` / `value.<name>` spelling that `export_metadata_prefix` reports back
55
+ injected nothing. Both spellings now work.
56
+ - MQTT publishes are confirmed by PUBACK/PUBCOMP and guarded against session resets, which
57
+ took chaos-test message loss to zero.
58
+ - Postgres CDC advances its replication slot durably on shutdown instead of leaving the
59
+ feedback unflushed.
60
+ - `deduplication` could silently drop a message after a crash. The reservation was written to
61
+ the store *before* the message was processed, so a redelivery arriving within the 5s pending
62
+ TTL was classified as a duplicate and acked — without ever having been written to the sink.
63
+ Reservations are now in-memory and die with the process, so the redelivery is reprocessed.
64
+ - A Kafka record with no value — a tombstone, which is ordinary traffic on a compacted topic —
65
+ failed the whole batch. Its offset was never committed, so the route reconnected onto the same
66
+ record forever: one tombstone wedged the consumer permanently, and a compacted topic could not
67
+ be consumed at all. Tombstones now arrive as an empty payload flagged `mqb.kafka.tombstone`.
68
+ - `--drain` on a Kafka source could report success having copied only part of a topic, or none
69
+ of it. An idle wait was taken to mean "source exhausted", but an idle channel means nothing on
70
+ its own: before the first fetch lands, or in an ordinary gap between fetches, it looks exactly
71
+ the same as the end of the data. The shorter the idle timeout the more went missing — at 1ms a
72
+ 1,000,000-row topic landed 0 rows, and the copy still exited successfully. A drain now
73
+ completes only once every assigned partition has reached the offset it held when the drain
74
+ began, and lands all 1,000,000 rows at every idle timeout including 0.
75
+ - Drain no longer hangs on an empty source, and reconnect attempts are bounded.
76
+ - CSV reader handles quoted newlines; file endpoints fail fast on an unopenable path.
@@ -2976,6 +2976,15 @@ dependencies = [
2976
2976
  "windows-link",
2977
2977
  ]
2978
2978
 
2979
+ [[package]]
2980
+ name = "libmimalloc-sys"
2981
+ version = "0.1.49"
2982
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2983
+ checksum = "6a45a52f43e1c16f667ccfe4dd8c85b7f7c204fd5e3bf46c5b0db9a5c3c0b8e9"
2984
+ dependencies = [
2985
+ "cc",
2986
+ ]
2987
+
2979
2988
  [[package]]
2980
2989
  name = "libmqm-constants"
2981
2990
  version = "0.8.4"
@@ -3200,6 +3209,15 @@ dependencies = [
3200
3209
  "sketches-ddsketch",
3201
3210
  ]
3202
3211
 
3212
+ [[package]]
3213
+ name = "mimalloc"
3214
+ version = "0.1.52"
3215
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3216
+ checksum = "2d4139bb28d14ad1facf21d5eb8825051b326e172d216b39f6d31df53cc97862"
3217
+ dependencies = [
3218
+ "libmimalloc-sys",
3219
+ ]
3220
+
3203
3221
  [[package]]
3204
3222
  name = "mime"
3205
3223
  version = "0.3.17"
@@ -3330,7 +3348,7 @@ dependencies = [
3330
3348
 
3331
3349
  [[package]]
3332
3350
  name = "mq-bridge"
3333
- version = "0.3.8"
3351
+ version = "0.3.10"
3334
3352
  dependencies = [
3335
3353
  "aes-gcm",
3336
3354
  "anyhow",
@@ -3411,7 +3429,7 @@ dependencies = [
3411
3429
 
3412
3430
  [[package]]
3413
3431
  name = "mq-bridge-bindings-common"
3414
- version = "0.3.8"
3432
+ version = "0.3.10"
3415
3433
  dependencies = [
3416
3434
  "anyhow",
3417
3435
  "fast-uuid-v7",
@@ -3425,12 +3443,13 @@ dependencies = [
3425
3443
 
3426
3444
  [[package]]
3427
3445
  name = "mq-bridge-node"
3428
- version = "0.3.8"
3446
+ version = "0.3.10"
3429
3447
  dependencies = [
3430
3448
  "anyhow",
3431
3449
  "async-trait",
3432
3450
  "bytes",
3433
3451
  "fast-uuid-v7",
3452
+ "mimalloc",
3434
3453
  "mq-bridge",
3435
3454
  "mq-bridge-bindings-common",
3436
3455
  "napi",
@@ -3446,12 +3465,13 @@ dependencies = [
3446
3465
 
3447
3466
  [[package]]
3448
3467
  name = "mq-bridge-py"
3449
- version = "0.3.8"
3468
+ version = "0.3.10"
3450
3469
  dependencies = [
3451
3470
  "anyhow",
3452
3471
  "async-trait",
3453
3472
  "bytes",
3454
3473
  "fast-uuid-v7",
3474
+ "mimalloc",
3455
3475
  "mq-bridge",
3456
3476
  "mq-bridge-bindings-common",
3457
3477
  "pyo3",
@@ -3725,9 +3745,9 @@ dependencies = [
3725
3745
 
3726
3746
  [[package]]
3727
3747
  name = "omq-proto"
3728
- version = "0.24.1"
3748
+ version = "0.25.0"
3729
3749
  source = "registry+https://github.com/rust-lang/crates.io-index"
3730
- checksum = "c9335ef21d570294464c491837af5477caddb05cf3c7223af1c4341a127e6035"
3750
+ checksum = "856d18f6b58f39153fcf0b574efe3f04362ff44d3a46829eb984c1f9ef2d121c"
3731
3751
  dependencies = [
3732
3752
  "bytes",
3733
3753
  "patricia_tree",
@@ -3739,9 +3759,9 @@ dependencies = [
3739
3759
 
3740
3760
  [[package]]
3741
3761
  name = "omq-tokio"
3742
- version = "0.20.1"
3762
+ version = "0.20.3"
3743
3763
  source = "registry+https://github.com/rust-lang/crates.io-index"
3744
- checksum = "4a9d9c6caa88d29df5b9dc9638ea9f0f33b087ecc0f149b70e61149f97bcdb80"
3764
+ checksum = "98aaa33702e7d723de268dd9fe725f93d469afef3f1bedb14cfa7f579f61a7b7"
3745
3765
  dependencies = [
3746
3766
  "arc-swap",
3747
3767
  "bytes",
@@ -7191,9 +7211,9 @@ dependencies = [
7191
7211
 
7192
7212
  [[package]]
7193
7213
  name = "yring"
7194
- version = "0.3.10"
7214
+ version = "0.3.11"
7195
7215
  source = "registry+https://github.com/rust-lang/crates.io-index"
7196
- checksum = "39146d2147cfb9f71b9f071f93f7b5dbcfb28eee528dafcaf5f500f4917cb69f"
7216
+ checksum = "f8545becda6379f37e13fbdf5d9de464849bdd1565a995368648f11fe9d96d13"
7197
7217
  dependencies = [
7198
7218
  "loom",
7199
7219
  ]
@@ -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.8"
19
+ version = "0.3.10"
20
20
 
21
21
  [workspace.dependencies]
22
22
  anyhow = "1.0"
@@ -29,6 +29,12 @@ serde_yaml_ng = "0.10"
29
29
  tokio = "1.34" # `Receiver::recv_many` (HTTP source batch drain) needs 1.34+.
30
30
  tracing = "0.1"
31
31
 
32
+ [profile.dev.package."*"]
33
+ debug = false
34
+
35
+ [profile.dev.build-override]
36
+ debug = false
37
+
32
38
  [profile.release]
33
39
  lto = "thin"
34
40
  strip = "symbols"
@@ -138,7 +144,8 @@ flate2 = { version = "1.1", optional = true }
138
144
  lz4_flex = { version = "0.14", optional = true, default-features = false, features = ["frame"] }
139
145
  # zstd (bundled libzstd via zstd-sys); Decoder handles concatenated frames like gzip/lz4.
140
146
  zstd = { version = "0.13", optional = true }
141
- base64 = { version = "0.23", optional = true }
147
+ # Non-optional: `CanonicalMessage`'s JSON form encodes binary payloads as base64.
148
+ base64 = { version = "0.23" }
142
149
 
143
150
  # AEAD ciphers for the `encryption` feature (payload middleware + at-rest file/object).
144
151
  chacha20poly1305 = { version = "0.11", optional = true }
@@ -210,7 +217,7 @@ float-roundtrip = ["serde_json/float_roundtrip"]
210
217
  # runtime the client is loaded lazily only when an ibm-mq endpoint is opened
211
218
  # (clear error if the client isn't installed). Use `ibm-mq-static` instead for
212
219
  # static linking.
213
- 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"]
220
+ full = ["middleware", "mongodb", "kafka", "amqp", "nats", "mqtt", "http", "aws", "zeromq", "zeromq-omq", "redis-streams", "sled", "grpc", "sqlx", "clickhouse", "websocket", "postgres-cdc", "object-store", "rustls-aws-lc", "ibm-mq"]
214
221
 
215
222
  # Endpoints that build across most OS targets (glibc/musl/Alpine, macOS,
216
223
  # Windows, aarch64/armv7) without a special toolchain — no librdkafka, protoc,
@@ -229,7 +236,7 @@ dedup = ["sled"]
229
236
  compression = ["dep:flate2", "dep:lz4_flex", "dep:zstd"]
230
237
  # AEAD payload encryption: the `encryption` middleware and the at-rest
231
238
  # `encryption` field on the file/object_store endpoints.
232
- encryption = ["dep:chacha20poly1305", "dep:aes-gcm", "dep:base64"]
239
+ encryption = ["dep:chacha20poly1305", "dep:aes-gcm"]
233
240
  metrics = ["dep:metrics"]
234
241
  test-utils = ["dep:tracing-appender", "dep:tracing-subscriber", "dep:serde_yaml_ng"]
235
242
  # Opt-in produce/consume-only isolation benchmarks (throwaway diagnostics, off by default)
@@ -254,9 +261,9 @@ amqp = ["lapin", "rustls", "rustls-pemfile", "url"]
254
261
  nats = ["async-nats", "rustls", "rustls-pemfile"]
255
262
  mongodb = ["dep:mongodb", "url"]
256
263
  mqtt = ["rumqttc", "rustls", "tokio-rustls", "rustls-pemfile", "url"]
257
- 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:lz4_flex", "dep:zstd", "dep:base64"]
264
+ 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:lz4_flex", "dep:zstd"]
258
265
  websocket = ["dep:tokio-websockets"]
259
- aws = ["aws-config", "aws-sdk-sqs", "aws-sdk-sns", "dep:base64"]
266
+ aws = ["aws-config", "aws-sdk-sqs", "aws-sdk-sns"]
260
267
  # Runtime-loaded IBM MQ client via dlopen: builds with no SDK present; the IBM
261
268
  # redistributable client is only needed at runtime, and only if an ibm-mq
262
269
  # endpoint is actually used.
@@ -283,10 +290,12 @@ object-store = ["dep:object_store", "url"]
283
290
  # Postgres logical-replication CDC (pgoutput). Reuses the sqlx dependency for the
284
291
  # slot-lifecycle control plane; the streaming wire protocol is pgwire-replication.
285
292
  # Needs Rust 1.88 (pgwire-replication MSRV), like the amqp feature gate.
286
- postgres-cdc = ["dep:pgwire-replication", "dep:byteorder", "sqlx", "dep:base64", "url", "dep:percent-encoding"]
293
+ postgres-cdc = ["dep:pgwire-replication", "dep:byteorder", "sqlx", "url", "dep:percent-encoding"]
287
294
 
288
295
  [dev-dependencies]
289
296
  config = { version = "0.15" }
297
+ # `start_paused` — lets a test cross a 30s health window without waiting 30s.
298
+ tokio = { workspace = true, features = ["test-util"] }
290
299
  serde_yaml_ng = "0.10"
291
300
  tempfile = "3.27"
292
301
  chrono = "0.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mq-bridge-py-basic
3
- Version: 0.3.8
3
+ Version: 0.3.10
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: License :: OSI Approved :: MIT License
6
6
  Classifier: Programming Language :: Python :: 3
@@ -232,8 +232,8 @@ that ack each batch individually (NATS JetStream, AMQP, MQTT) accept any order.
232
232
  > per source position (Kafka `partition:offset`, NATS `stream_sequence`, AMQP
233
233
  > delivery tag) and makes a natural primary key. Source cursor fields are also
234
234
  > available in `message.metadata` (`mqb.src.kafka_topic`/`mqb.src.kafka_offset`, `mqb.src.nats_subject`/`mqb.src.nats_stream_sequence`,
235
- > `mqb.src.amqp_routing_key`/`mqb.src.amqp_delivery_tag`) when you opt in by setting
236
- > the `MQB_SOURCE_METADATA=1` environment variable (off by default).
235
+ > `mqb.src.amqp_routing_key`/`mqb.src.amqp_delivery_tag`) when its Kafka, NATS, or
236
+ > AMQP source config sets `source_metadata: true` (off by default).
237
237
  >
238
238
  > **Ack deadlines vs slow loads.** JetStream `AckWait` (default 30s), AMQP
239
239
  > prefetch/consumer-timeout and MQTT inflight windows each bound how long a batch
@@ -6,6 +6,8 @@
6
6
  ![Linux](https://img.shields.io/badge/Linux-supported-green?logo=linux)
7
7
  ![Windows](https://img.shields.io/badge/Windows-supported-green?logo=windows)
8
8
  ![macOS](https://img.shields.io/badge/macOS-supported-green?logo=apple)
9
+ [![Supply chain](https://img.shields.io/badge/supply%20chain-cargo--deny-blue)](deny.toml)
10
+ [![Security](https://img.shields.io/badge/security-advisory%20analysis-brightgreen)](SECURITY.md)
9
11
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
10
12
 
11
13
 
@@ -21,7 +23,7 @@ It is not only a forwarder. A route can transform, filter, fan out, retry, rate-
21
23
 
22
24
  ## Move data from A to B — inside your own service
23
25
 
24
- If you need to move data reliably between systems and you write code (Rust, Python, or Node), `mq-bridge` is a strong default. It is a **library you embed**, not a daemon or control plane you operate.
26
+ If you need to move data or events reliably between systems and you write code (Rust, Python, or Node), `mq-bridge` is a strong default. It is a **library you embed**, not a daemon or control plane you operate.
25
27
 
26
28
  **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
29
 
@@ -33,7 +35,9 @@ If you need to move data reliably between systems and you write code (Rust, Pyth
33
35
  * **TLS everywhere, one config shape**: a single `TlsConfig` block (CA bundle, client cert/key for mTLS, insecure-skip) is reused across transports.
34
36
  * **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
37
 
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).
38
+ > **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) — on a CSV→JSONL file conversion (1,000,000 mixed-type rows, ~116 MiB) sustained **1,133,786 rows/s** at ~22 MiB, about **~58x faster** and **~20x 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).
39
+
40
+ > **Kafka → file.** In a 1,000,000-row relay with the default file format and no transform, the engine was up to **80% faster than Sea Streamer**. The [`mq-bridge-app` benchmark](https://github.com/marcomq/mq-bridge-app/tree/dev/benches/etl) contains the reproducible helper and native file-format caveats.
37
41
 
38
42
 
39
43
  ## Language Bindings
@@ -306,9 +310,14 @@ server-side (default one-hour window) — `insert_deduplication_token` lets you
306
310
  mq-bridge does not set one, so rely on `ReplacingMergeTree` for logical dedup and treat block-level
307
311
  dedup only as retry-safety.
308
312
 
309
- **Postgres CDC — deterministic id + `postgres.key`.** The `postgres_cdc` source already resumes from a
310
- durable LSN checkpoint (no re-delivery on clean restart). For the crash window (row written, then a
311
- crash before the checkpoint flush) it is at-least-once, so make the sink idempotent. Each change event
313
+ **Postgres CDC — deterministic id + `postgres.key`.** The `postgres_cdc` source resumes from the slot's
314
+ durable `confirmed_flush_lsn`. In-band standby feedback is asynchronous and is *not* flushed when the
315
+ stream stops, so the last acks are made durable by the consumer's `Drop`, which stops the stream and
316
+ advances the slot synchronously. Re-delivery is therefore avoided only on a restart that actually runs
317
+ that teardown — a host process that exits without dropping the route (or one on a current-thread Tokio
318
+ runtime, where the blocking advance is skipped) replays everything since the last asynchronous feedback
319
+ tick. Set `checkpoint_store` to a `file://` path for a second, per-ack durable position that survives
320
+ teardown regardless. Treat the source as at-least-once and make the sink idempotent. Each change event
312
321
  carries the full row (so the primary key is in the payload), `postgres.lsn` (a monotonic version),
313
322
  `postgres.operation`/`schema`/`table`, and — when the table has a primary key / replica identity —
314
323
  `postgres.key` (the key value). The event's `message_id` is a stable hash of `schema.table + key +
@@ -359,11 +368,56 @@ orders_upsert_branch:
359
368
  duplicate to detect. Left unwrapped by `request`, the tagged message is returned as the route's
360
369
  response as usual.
361
370
 
371
+ **Files & object storage — `idempotency`.** A filesystem has no unique constraint, so the `file` and
372
+ `object_store` sinks get replay safety a different way: **deterministic names plus covered-range
373
+ recovery**. Set `idempotency: true` and the sink stops writing UUID-named objects. Instead it groups
374
+ each batch into runs of consecutive source positions and writes one immutable part per run, named
375
+ for the range it covers:
376
+
377
+ ```yaml
378
+ kafka_to_s3:
379
+ input:
380
+ kafka: { topic: "orders", url: "localhost:9092", source_metadata: true }
381
+ output:
382
+ object_store:
383
+ url: "s3://my-bucket/orders"
384
+ idempotency: true # → s3://my-bucket/orders/part-orders-0-0-1023.jsonl
385
+ ```
386
+
387
+ On startup the sink lists what is already there, parses the ranges out of the part names, and drops
388
+ any incoming record whose position falls inside one. Filtering is **per record, not per batch**, so
389
+ batch boundaries are free to differ across restarts — that is what makes it work without a
390
+ checkpoint protocol. Local files stage to a temp name, `fsync`, then `rename` (atomic within a
391
+ filesystem); object stores have no atomic rename, so a single PUT under the final name *is* the
392
+ commit.
393
+
394
+ This needs a replayable source position, which today means **Kafka** (topic/partition/offset) or
395
+ **`postgres_cdc`** (commit LSN plus in-transaction ordinal). A route with an idempotent output turns
396
+ `source_metadata` on for its input automatically; set it explicitly only when you want the
397
+ `mqb.src.*` keys for something else. Any other input is rejected when the route starts. NATS, AMQP
398
+ and MongoDB CDC also accept `source_metadata` and emit provenance keys, but a subject or routing key
399
+ is not a replayable offset, so they cannot drive an idempotent sink.
400
+
401
+ For the `file` sink, `idempotency: true` changes what `path` means: it is the directory that receives
402
+ the part files, not the file that is appended to. The sink creates it on startup, so pointing it at an
403
+ existing regular file fails there with "Failed to create idempotent file sink directory".
404
+
405
+ `compression` and `encryption` work as usual.
406
+
407
+ Current limits, all of which the sink rejects or logs rather than silently mishandling:
408
+
409
+ * No `csv` (each part would need its own header row).
410
+ * `date_partition` is ignored — parts are written flat under the prefix, since the name already
411
+ carries the range. Logged at startup.
412
+ * **One part file per contiguous run per batch.** There is no size-based rolling yet, so a large
413
+ backfill produces many small files (~1000 per 1M rows at `batch_size: 1024`). For `postgres_cdc`
414
+ this is per-transaction, so a high-commit-rate stream produces one small file per commit. Rolling
415
+ would require buffering across batches the route has already acked, which is not safe today.
362
416
 
363
417
  ### Cloud Object Storage (S3 / GCS / Azure)
364
418
  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://`).
365
419
 
366
- * **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.
420
+ * **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. This naming applies only while `idempotency` is off — with `idempotency: true` the parts are instead named for the source range they cover and written flat under the prefix, and `date_partition` is ignored (see "Files & object storage — `idempotency`" above).
367
421
  * **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.
368
422
 
369
423
  ```yaml
@@ -391,6 +445,26 @@ replay_from_s3:
391
445
  > always write the `{message_id, payload, metadata}` wrapper, so the message id survives the round
392
446
  > trip. Use `format: raw` to write payloads verbatim (bare documents, no wrapper). Applies to both
393
447
  > `file` and `object_store`.
448
+ >
449
+ > A **source** on those formats expects that same wrapper, `message_id` included. A line that is
450
+ > valid JSON but not the wrapper — a hand-written fixture with only `payload` and `metadata`, say —
451
+ > is not decomposed: the whole line becomes the payload and the line's own `metadata` is discarded.
452
+ > The reader logs a warning naming the wrapper when this happens. For plain JSON lines, use
453
+ > `format: raw`.
454
+ >
455
+ > **Payload encoding in the wrapper.** A UTF-8 payload is written as a plain JSON string under
456
+ > `payload`. A binary one (compressed, encrypted, Protobuf, …) is base64-encoded under a separate
457
+ > `payload_base64` field; the two are mutually exclusive, as in the
458
+ > [CloudEvents JSON format](https://github.com/cloudevents/spec/blob/main/cloudevents/formats/json-format.md).
459
+ >
460
+ > ```json
461
+ > {"message_id":"019f9b12-d786-7ebe-a7ec-a1aa71bc47ae","payload":"{\"order_id\":7}"}
462
+ > {"message_id":"019f9b12-d78a-7c01-b0f4-2f0f4d6a1c33","payload_base64":"KLUv/SBOAQAA"}
463
+ > ```
464
+ >
465
+ > Sources still read the older byte-array form (`"payload":[123,34,…]`), so existing files keep
466
+ > working. Only the reverse is a break: a **binary** record written by this version is not readable
467
+ > by an older mq-bridge. Text records are compatible in both directions.
394
468
 
395
469
  ### Response Endpoint
396
470
  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.