1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! Shared coordination-capability contract for AS4 topology validation.
use crate;
/// Boxed future yielding a held turn.
type TurnFuture<'a> = Pin;
/// Boxed future returned by [`ConversationOrderGate::reserve_ordered_turn`].
type ReserveTurnFuture<'a> = Pin;
/// Capability surface for AS4 ordered-delivery and pull-queue coordination backends.
///
/// Strict-production startup validation uses this trait so clustered deployments
/// must pass concrete coordination handles instead of raw booleans.
// ---------------------------------------------------------------------------
// ConversationOrderGate — distributed / pluggable gate abstraction
// ---------------------------------------------------------------------------
/// RAII guard for a conversation's active turn.
///
/// The guard holds the ordered turn for a single AS4 conversation. All
/// subsequent waiters for the same conversation are suspended until this guard
/// is released.
///
/// Implementations must also release the turn on `drop` so that panics or task
/// cancellation never leave a conversation permanently blocked.
/// A reserved place in a conversation's **arrival** order.
///
/// This is the half of the gate that makes ordering mean anything. The
/// reservation is taken as soon as a message arrives — before it is parsed,
/// verified or decrypted — so the queue reflects the order messages showed up
/// in. [`wait_for_turn`](Self::wait_for_turn) is then awaited *after* that work,
/// and yields only once every earlier arrival has finished.
///
/// Reserving after the work instead orders by processing time: a small message
/// that arrived second overtakes a large one that arrived first (D54).
///
/// Dropping a reservation without waiting releases the place, so a failed
/// receive does not block the conversation.
/// Conversation-level ordering gate for AS4 ordered-delivery MEPs.
///
/// The `As4ConversationOrderGate` is an **in-process** implementation. For
/// multi-replica deployments, supply a custom implementation backed by:
/// - A Redis `SET NX PX` lock (redlock-style)
/// - A database advisory lock (`pg_try_advisory_lock`)
/// - A ZooKeeper ephemeral node
///
/// ## ⚠ Sticky routing requirement
///
/// Even with a distributed `ConversationOrderGate`, replicas that receive
/// messages out of order cannot guarantee the *application-visible* delivery
/// sequence unless all messages for a given `ConversationId` are routed to the
/// same replica **or** the coordination primitive enforces strict global ordering.
/// A Redis-based gate provides mutual exclusion but NOT sequencing across replicas
/// unless combined with a sequence counter. Document your deployment topology's
/// ordering guarantees clearly.
///
/// ## Example — plugging in a custom gate
///
/// ```rust,ignore
/// struct RedisOrderGate { client: redis::Client }
///
/// impl ConversationOrderGate for RedisOrderGate {
/// fn reserve_ordered_turn<'a>(
/// &'a self, conversation_id: &'a str, _session: &'a SessionContext,
/// ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Box<dyn ConversationTurnHandle>>> + Send + 'a>> {
/// Box::pin(async move {
/// // Take a sequence number now; wait for it in `wait_for_turn`.
/// let ticket = self.next_ticket(conversation_id).await?;
/// Ok(Box::new(ticket) as Box<dyn ConversationTurnHandle>)
/// })
/// }
/// fn record_message_ordering<'a>(
/// &'a self, _: &'a str, _: &'a str, _: Option<&'a str>,
/// ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<()>> + Send + 'a>> {
/// Box::pin(async move { Ok(()) })
/// }
/// }
///
/// impl As4TopologyCoordination for RedisOrderGate {
/// fn cluster_safe(&self) -> bool { true }
/// fn topology_component(&self) -> &'static str { "redis-conversation-gate" }
/// }
/// ```