-
Notifications
You must be signed in to change notification settings - Fork 0
Add ComputeHandler as Middleware #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
dcf3314
changes after review
marcomq 3f5348a
add middleware
marcomq 0f805cb
fixes & add middlewares as features
marcomq fd78502
make bulk_send more robust
marcomq 6056fa5
stabilize errors & fmt
marcomq 108a126
compile fix
marcomq 058328b
add dlq retry
marcomq 82b6465
switch from vec<u8> to bytes and fix some tests
marcomq 2942e59
disable jetstream optionally
marcomq 3691e5d
renam await_ack to skip_ack
marcomq ee84573
fix kafka sasl key
marcomq ea9d194
fmt
marcomq 7175143
switch to prefer bulk
marcomq 9762d1c
fmt
marcomq dbbce6c
clippy
marcomq fb2d88a
fix mqtt error messages
marcomq e98be87
rewrote read direct test for bulk
marcomq f93b669
rename bulk to batch
marcomq d8745c1
add receive_batch for kafka
marcomq 2ed6caf
add tests for separate single and batch read / write performance
marcomq a117227
address some coderabbit issues
marcomq 2bed90e
add batch read for mongodb
marcomq 59bfe8d
try to optimize nats receive_batch (wip)
marcomq 77c7ce5
add comment
marcomq 00619c9
change warning log
marcomq 05629ba
add computehandler as middleware
marcomq 9d31696
Merge remote-tracking branch 'origin/main' into dev
marcomq ca67038
message_id to u128 and make app_name more generic
marcomq 1e9171f
adding proper message_ids
marcomq f0ae729
adding cfg opts and retry and random_panic middelware
marcomq c5e9a76
raneming retry to attempt, bugfixings
marcomq a0dcc62
misc fixes, add performance bench
marcomq 62eedd0
rename crate and repo
marcomq b4fd5d8
fix test
marcomq ec6288d
fix minor issues, add comments
marcomq 553dc50
fix ci
marcomq File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,89 +1,119 @@ | ||
| # hot_queue | ||
| Rust library to access different stream and message queues. | ||
|
|
||
| TODO: I will add the actual endpoint code later. No need to already create it now. | ||
| For now, I will just create the configuration structs, so I can parse yaml and json files | ||
| correctly. | ||
|
|
||
| It is possible to use following seperately: | ||
| - endpoint - have most simple access, can be amqp, mongodb etc... (will be implemted later) | ||
| - route, contains 2 endpoints | ||
|
|
||
| Between endpoint and route, there can be multiple middleware: | ||
| - retry | ||
| - deduplication (optional via feature) | ||
| - dead letter queue | ||
| - metrics | ||
|
|
||
| The message that is shared is a CanonicalMessage, which has a message_id, a byte array as body - | ||
| but it will be implemented later. | ||
|
|
||
| Configuration Schema Overview | ||
| The application uses a hierarchical configuration system. The structure is defined by Rust structs in src/model.rs and interpreted by the serde and config crates. | ||
|
|
||
| The key to understanding the endpoint configuration from, out is the combination of serde attributes you've used: | ||
|
|
||
| YAML Structure | ||
| Here is an example config.yml file that demonstrates the full structure. | ||
|
|
||
| yaml | ||
| kafka_to_nats: # The top-level keys are the route names | ||
| # (Optional) Number of concurrent processing tasks for this route | ||
| concurrency: 10 | ||
|
|
||
| # The input/source endpoint for the route | ||
| input: | ||
| # (Optional) A list of middlewares to apply to each endpoint before or after send | ||
| middlewares: | ||
| deduplication: | ||
| sled_path: "/tmp/hot_queue/dedup_db" | ||
| ttl_seconds: 3600 | ||
| metrics: | ||
| # metrics doesn't have much configuration, its presence enables it. | ||
| kafka: | ||
| # --- Kafka-specific fields --- | ||
| # `topic` is from `KafkaConsumerEndpoint` | ||
| topic: "input-topic" | ||
| # `brokers` and `group_id` are from the flattened `KafkaConfig` | ||
| brokers: "localhost:9092" | ||
| group_id: "my-consumer-group" | ||
| # Other optional KafkaConfig fields like `username`, `password`, `tls`, etc. | ||
| tls: | ||
| required: true | ||
| ca_file: "/path_to_ca" # optional | ||
| cert_file: "/path_to_cert" # optional | ||
| key_file: "/path_to_key" # optional | ||
| cert_password: "password" | ||
| accept_invalid_certs: true | ||
|
|
||
|
|
||
| # The output/sink endpoint for the route | ||
| output: | ||
| nats: | ||
| # --- NATS-specific fields --- | ||
| # `subject` is from `NatsPublisherEndpoint` | ||
| subject: "output-subject" | ||
| # `url` is from the flattened `NatsConfig` | ||
| url: "nats://localhost:4222" | ||
| # Other optional NatsConfig fields like `username`, `password`, `token`, etc. | ||
|
|
||
| Environment Variable Structure | ||
| The config crate maps environment variables to the YAML structure. It uses a prefix (in your case, HQ) and a separator (__). | ||
|
|
||
| Here is how the kafka_to_nats route from the YAML example above would be defined using environment variables: | ||
| sh | ||
| # Route settings | ||
| export HQ__KAFKA_TO_NATS__CONCURRENCY=10 | ||
|
|
||
| # Input endpoint (kafka_to_nats.input) | ||
| export HQ__KAFKA_TO_NATS__INPUT__KAFKA__TOPIC="input-topic" | ||
| export HQ__KAFKA_TO_NATS__INPUT__KAFKA__BROKERS="localhost:9092" | ||
| export HQ__KAFKA_TO_NATS__INPUT__KAFKA__GROUP_ID="my-consumer-group" | ||
|
|
||
| # Output endpoint (kafka_to_nats.output) | ||
| export HQ__KAFKA_TO_NATS__OUTPUT__NATS__SUBJECT="output-subject" | ||
| export HQ__KAFKA_TO_NATS__OUTPUT__NATS__URL="nats://localhost:4222" | ||
|
|
||
| # Dead-Letter Queue endpoint for the input (kafka_to_nats.input.middlewares.dlq) | ||
| export HQ__KAFKA_TO_NATS__INPUT__MIDDLEWARES__DLQ__NATS__SUBJECT="dlq-subject" | ||
| export HQ__KAFKA_TO_NATS__INPUT__MIDDLEWARES__DLQ__NATS__URL="nats://localhost:4222" | ||
| # mq-bridge | ||
| `mq-bridge` is an asynchronous message bridging library for Rust. It connects different messaging systems, data stores, and protocols. It is built on Tokio and supports patterns like retries, dead-letter queues, and message deduplication. | ||
|
|
||
| ## Features | ||
|
|
||
| * **Supported Backends**: Kafka, NATS, AMQP (RabbitMQ), MQTT, MongoDB, HTTP, Files, and in-memory channels. | ||
| * **Configuration**: Routes can be defined via YAML or environment variables. | ||
| * **Middleware**: | ||
| * **Retries**: Exponential backoff for transient failures. | ||
| * **Dead-Letter Queues (DLQ)**: Redirect failed messages. | ||
| * **Deduplication**: Message deduplication using `sled`. | ||
| * **Concurrency**: Configurable concurrency per route using Tokio. | ||
|
|
||
| ## Core Concepts | ||
|
|
||
| * **Route**: A named data pipeline that defines a flow from one `input` to one `output`. | ||
| * **Endpoint**: A source or sink for messages. | ||
| * **Middleware**: Components that intercept and process messages (e.g., for error handling). | ||
|
|
||
| ## Usage | ||
|
|
||
| ### Programmatic Usage | ||
|
|
||
| You can define and run routes directly in Rust code. | ||
|
|
||
| ```rust | ||
| use mq_bridge::models::{Endpoint, EndpointType, MemoryConfig, Route}; | ||
| use std::time::Duration; | ||
| use tokio::time::timeout; | ||
|
|
||
| #[tokio::main] | ||
| async fn main() { | ||
| // Define a route from one in-memory channel to another | ||
| let route = Route { | ||
| input: Endpoint::new(EndpointType::Memory(MemoryConfig { | ||
| topic: "mem-in".to_string(), | ||
| capacity: Some(100), | ||
| })), | ||
| output: Endpoint::new(EndpointType::Memory(MemoryConfig { | ||
| topic: "mem-out".to_string(), | ||
| capacity: Some(100), | ||
| })), | ||
| concurrency: 1, | ||
| }; | ||
|
|
||
| // Get handles to the memory channels for testing | ||
| let in_channel = route.input.channel().unwrap(); | ||
| let out_channel = route.output.channel().unwrap(); | ||
|
|
||
| // Run the route. It will stop when the input channel is closed and empty. | ||
| let (run_result, _) = tokio::join!( | ||
| route.run_until_err("memory-test", None), | ||
| async { | ||
| // Send a message | ||
| in_channel.send_message(mq_bridge::CanonicalMessage::new(b"hello".to_vec(), None)).await.unwrap(); | ||
| // Close the input channel to allow the route to terminate | ||
| in_channel.close(); | ||
| } | ||
| ); | ||
|
|
||
| // Ensure the route ran without errors | ||
| run_result.unwrap(); | ||
|
|
||
| // Verify the message was received | ||
| let received_messages = out_channel.drain_messages(); | ||
| assert_eq!(received_messages.len(), 1); | ||
| assert_eq!(received_messages[0].payload, "hello".as_bytes()); | ||
|
|
||
| println!("Message successfully bridged from 'mem-in' to 'mem-out'!"); | ||
| } | ||
| ``` | ||
|
|
||
| ## Configuration Details | ||
|
|
||
| ### Environment Variables | ||
| All YAML configuration can be overridden with environment variables. The mapping follows this pattern: | ||
| `MQB__{ROUTE_NAME}__{PATH_TO_SETTING}` | ||
|
|
||
| For example, to set the Kafka topic for the `kafka_to_nats` route: | ||
| ```sh | ||
| export MQB__KAFKA_TO_NATS__INPUT__KAFKA__TOPIC="my-other-topic" | ||
| ``` | ||
|
|
||
| ### Middleware Configuration | ||
| Middleware is defined as a list under an endpoint. | ||
|
|
||
| ```yaml | ||
| input: | ||
| middlewares: | ||
| - retry: | ||
| max_attempts: 5 | ||
| initial_interval_ms: 200 | ||
| - dlq: | ||
| endpoint: | ||
| nats: | ||
| subject: "my-dlq-subject" | ||
| url: "nats://localhost:4222" | ||
| - deduplication: | ||
| sled_path: "/var/data/mq-bridge/dedup_db" | ||
| ttl_seconds: 3600 # 1 hour | ||
| kafka: | ||
| # ... kafka config | ||
| ``` | ||
|
|
||
| ## Running Tests | ||
| The project includes a comprehensive suite of integration and performance tests that require Docker. | ||
|
|
||
| To run the performance benchmarks for all supported backends: | ||
| ```sh | ||
| cargo test --test integration_test --release -- --ignored --nocapture --test-threads=1 | ||
| ``` | ||
|
|
||
| To run the criterion benchmarks: | ||
| ```sh | ||
| cargo bench --features "full" | ||
| ``` | ||
|
|
||
| ## License | ||
| `mq-bridge` is licensed under the MIT License. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix list indentation to match Markdown standards.
The nested list items use 4-space indentation, but the markdown linter expects 2-space indentation for consistency.
🔎 Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
9-9: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
10-10: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
11-11: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
🤖 Prompt for AI Agents