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
//! Pluggable storage backends for dedup and reconciliation.
//!
//! This module provides trait-based abstractions for idempotency key tracking
//! and reconciliation request queuing, enabling production deployments to use
//! distributed backends (Redis, PostgreSQL, etc.) while maintaining simple
//! in-memory implementations for development and testing.
//!
use crateReconciliationRequest;
use Future;
use Pin;
use Poll;
/// `dyn`-safe boxed async future for storage trait methods.
///
/// This is the return type of [`DedupStorage::first_seen`]. Implementations
/// box their async body with `Box::pin(async move { ... })`.
pub type BoxFuture<'a, T> = ;
/// Drive a `BoxFuture` to completion synchronously.
///
/// This is provided for the **sync receive path** (`receive_push_with_dedup_sync`),
/// which runs inside `tokio::task::spawn_blocking` and cannot `.await` a future.
/// All in-memory [`DedupStorage`] implementations return a `Poll::Ready` future
/// immediately and this function resolves them in O(1).
///
/// # Panics
/// Panics with a clear message if the future returns `Poll::Pending`, which
/// indicates an async backend being called from the sync path. In that case,
/// switch to `receive_push_with_dedup_async` which properly `.await`s the dedup call.
pub
/// Trait for distributed dedup state storage.
///
/// The single required method [`first_seen`](Self::first_seen) is **async** via
/// a `BoxFuture` return so that production backends backed by Redis, PostgreSQL,
/// DynamoDB, or SlateDB can implement it natively without
/// `block_in_place` / `Handle::current().block_on(…)` boilerplate.
///
/// In-memory implementations simply wrap synchronous logic in `Box::pin(async move { … })` —
/// the future resolves immediately on the first `.await`.
///
/// Implementations must provide strict idempotency guarantees: each idempotency key
/// is seen exactly once, and lock poison/infrastructure failures must fail-closed.
///
/// # Example implementation (in-memory, sync-wrapped)
/// ```rust,ignore
/// impl DedupStorage for MyMemoryStore {
/// fn is_durable(&self) -> bool { false }
/// fn first_seen<'a>(&'a self, key: &'a str) -> BoxFuture<'a, asx_rs::core::Result<bool>> {
/// Box::pin(async move { self.inner_first_seen_sync(key) })
/// }
/// }
/// ```
///
/// # Example implementation (async backend)
/// ```rust,ignore
/// impl DedupStorage for RedisDedup {
/// fn is_durable(&self) -> bool { true }
/// fn first_seen<'a>(&'a self, key: &'a str) -> BoxFuture<'a, asx_rs::core::Result<bool>> {
/// Box::pin(async move {
/// self.redis.set_nx(key).await.map_err(|e| storage_err(e))
/// })
/// }
/// }
/// ```
/// Trait for distributed reconciliation request queuing.
/// Implementations must preserve order and prevent duplicate reconciliation attempts.
///
/// **Stability notice — forward declaration only.**
/// This trait is exported as public API, but no public function in `asx-rs 0.1`
/// accepts a `dyn ReconciliationStorage` parameter. It is provided so that
/// downstream crates can implement it against a future stable integration surface
/// without a dependency version bump. The trait shape (method signatures, return
/// types, error variants) is subject to breaking change while the crate is at
/// `0.x`. Pin to an exact `asx-rs` patch version if you implement this trait.
pub use ;