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
//! Shared coordination-capability contract for AS4 topology validation.
use crate;
/// Boxed future returned by [`ConversationOrderGate::acquire_ordered_turn`].
type AcquireOrderedTurnFuture<'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 returned by [`ConversationOrderGate::acquire_ordered_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.
/// 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 acquire_ordered_turn<'a>(
/// &'a self, conversation_id: &'a str, _session: &'a SessionContext,
/// ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Box<dyn ConversationGuardHandle>>> + Send + 'a>> {
/// Box::pin(async move {
/// let guard = self.acquire_redis_lock(conversation_id).await?;
/// Ok(Box::new(guard) as Box<dyn ConversationGuardHandle>)
/// })
/// }
/// 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" }
/// }
/// ```