Replies: 3 comments 12 replies
|
Thanks for the detailed write-up — this aligns very well with how async testing is structured today in Microcks. From reviewing the current async test flow (AsyncAPITestRunner → async-minion → AsyncAPITestManager → MessageConsumptionTask), a request/reply implementation could likely start with a minimal, additive scope: Suggested first step (MVP): Detect request/reply semantics from AsyncAPI v3 (operation.reply) For such operations, have the minion: Consume a request message Extract a correlation ID using the AsyncAPI runtime expression Produce a single reply message on the resolved reply channel Reuse existing schema validation on the reply Design notes: Keeping correlation header-based initially feels like the safest, protocol-agnostic choice Reusing existing timeout semantics avoids introducing new lifecycle rules Treating request/reply handlers as opt-in (and excluding them from scheduled production) keeps backward compatibility I agree this is best approached incrementally — starting with static reply channels and a simple correlation strategy should already unlock many real-world use cases, and richer patterns can follow once this foundation is validated. Happy to help review or test as the design evolves. |
|
Thanks, @adamhicks, for initializing this and for all the great materials. Here are my comments and initial thoughts below in the order of reading:
Regarding the Open Questions:
Agree. Let's face it: we shouldn't have hundreds of request/reply handlers running in the minion.
Agree. Moreover, the asynchronous mocking doesn't use dispatchers at all as of today. We could introduce just one (as explained above in my comment on
We should be able to handle this and take care of how it translates on the concrete binding (typically, it would mean different consumer groups on Kafka).
Yes, we'll need to introduce some new elements in the UI.
It probably depends on the bindings - IIRC some bindings are defined at the channel levels, some others are only defined at the operation or server levels. We must, in any case, start with a green path that uses the same binding for both request and reply. Other questions I have:
|
|
Hey @adamhicks I scratched my head yesterday about the domain model evolution we can push to have something consistent with the existing part and the one we also have to manage with #1039. I came up with the following design that starts to make sense IMHO. Here's the classes diagram I can imagine at the moment, to support all our use cases: classDiagram
class ReplyInfo {
+String address
+String addressLocation
}
class Exchange
<<abstract>> Exchange
Service *-- Operation : composition
Operation *-- Exchange : composition
class Operation {
+ReplyInfo replyInfo
}
Exchange <|-- RequestResponsePair
Exchange <|-- UnidirectionalEvent
Exchange <|-- RequestReplyEvents
RequestResponsePair <|-- RequestResponsePairWithCB
class RequestResponsePair {
+Request request
+Response response
}
class UnidirectionalEvent {
+EventMessage eventMessage
}
class RequestReplyEvents {
+EventMessage request
+EventMessage reply
}
class RequestResponsePairWithCB {
+[RequestResponsePair] callbacks
}
class Message
<<abstract>> Message
Message <|-- Request
Message <|-- Response
Message <|-- EventMessage
class Message {
+String callbackId
}
class Request {
+String responseId
}
class Response {
+String dispatchCriteria
}
class EventMessage {
+String dispatchCriteria
+String replyId
}
You'll find the elements from your proposition we discussed: For OpenAPI callbacks, I imagine introducing the new Note In the case of callbacks, we also need some kind of address location (typically something like I am still a bit stuck on the callback-specific question of how to specify/know the delay between the original request processing and the callback submission. This can be a low random number (2-5 secs) in case of a single callback events but we need explicit information if there is more than 1 callback - in order to keep the temporal logic of events ( For OpenAPI webhooks, I realize that there was, in fact, an additional What do you think? Does it make sense? |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
AsyncAPI Request/Reply Feature - Design Discussion
I'd like to propose and discuss adding support for AsyncAPI 3.0 Request/Reply patterns to Microcks. This feature would enable mocking of bi-directional async message flows where a service listens for requests on one channel and sends correlated replies on another channel.
The Request/Reply pattern is a common messaging pattern in event-driven architectures and is now officially supported in AsyncAPI 3.0 through the
replyobject. This discussion outlines the analysis, design options, and implementation approach for bringing this capability to Microcks.We want to use this feature at Dojo and I'm keen to extend Microcks rather than build an internal tool. If you and/or your team could also benefit from this feature then please use this discussion as an opportunity to share what you would like to see. Feel free to comment on any of my thoughts and assumptions.
Background & Use Cases
What is Request/Reply in AsyncAPI?
Request/Reply is an asynchronous communication pattern where:
Key characteristics:
Example
Order Status Queries
A service that responds to requests about an order with the status of that order.
Specification
Reply Channels
AsyncAPI 3.0 supports two patterns for specifying reply channels:
1. Static Reply Channel:
2. Dynamic Reply Channel:
Correlation ID
Correlation IDs are a way for requestors to link responses to requests that they have made.
AsyncAPI 3.0 supports correlation IDs in both headers and payload. The
correlationIdobject uses runtime expressions to specify where the correlation ID is located.Correlation ID:
Specification with examples
Implementation Plan
Requirements
To support Request/Reply, we need to:
To make Request/Reply more usable, we should:
Domain Model
We'll need some extra models to hold reply info in, these will be populated during AsyncAPI spec parsing.
ReplyInfo: Contains either reply channel or a dynamic definition of where to find itOperation: Needs to hold request/reply infoRequestReplyEvent: As an alternative to UnidirectionalEventCorrelationID: Contains where to find the correlation ID in the EventMessageAsync Minion
These models will be communicated to the Async Minion which needs to schedule broker specific handlers. We need:
A singleton manager for request/reply handlers (similar to ProducerScheduler) which can
Handler classes for each type of broker
Integration Points
AsyncMockDefinitionUpdater:
isRequestReply() == trueRequestReplyHandlerinstead of adding to scheduled productionProducerManager:
Open Questions
should we use Quartz triggers to schedule responses rather than having a thread for each operation?
My proposal would create a new thread for every Request/Reply operation, if this efficient (enough)?
Can we take advantage of Quartz, given that we're already using it? (I don't know Quartz)
should we extend the reply spec (i.e. add x-microcks-operation) to add dispatcher rules?
I feel like we shouldn't try and do this yet, some of the features could be useful, such as serving different messages based on request payload but some of the options won't make sense for events. e.g.
URI_[...],PROXYandPROXY_FALLBACKwhat if the user specifies multiple operations that consume from the same channel?
Should we just let the independent handlers race? Should we throw an error and only let one work?
UI?
I don't fully know where event specs are visible on microcks, will we need to make some changes to surface the reply info?
Should we support request on one protocol, reply on another?
I don't think this is a popular use case, but I could be proven wrong.
Looking forward to your thoughts! 🚀
All reactions