@@ -105,6 +105,17 @@ pub struct DeadLetter {
105105 pub retry_count : u32 ,
106106}
107107
108+ /// Parameters for [`DeadLetterQueue::enqueue`].
109+ pub struct EnqueueDeadLetterArgs < ' a > {
110+ pub peer_id : u64 ,
111+ pub user_id : u64 ,
112+ pub tenant_id : u64 ,
113+ pub delta : Vec < u8 > ,
114+ pub constraint : & ' a Constraint ,
115+ pub reason : String ,
116+ pub hint : CompensationHint ,
117+ }
118+
108119/// Bounded dead-letter queue.
109120///
110121/// Stores rejected deltas for later inspection and retry. The queue is bounded
@@ -126,17 +137,17 @@ impl DeadLetterQueue {
126137 }
127138
128139 /// Enqueue a rejected delta with full auth context.
129- # [ allow ( clippy :: too_many_arguments ) ]
130- pub fn enqueue (
131- & mut self ,
132- peer_id : u64 ,
133- user_id : u64 ,
134- tenant_id : u64 ,
135- delta : Vec < u8 > ,
136- constraint : & Constraint ,
137- reason : String ,
138- hint : CompensationHint ,
139- ) -> Result < u64 > {
140+ pub fn enqueue ( & mut self , args : EnqueueDeadLetterArgs < ' _ > ) -> Result < u64 > {
141+ let EnqueueDeadLetterArgs {
142+ peer_id ,
143+ user_id ,
144+ tenant_id ,
145+ delta ,
146+ constraint ,
147+ reason ,
148+ hint ,
149+ } = args ;
150+
140151 if self . entries . len ( ) >= self . capacity {
141152 return Err ( CrdtError :: DlqFull {
142153 capacity : self . capacity ,
@@ -253,19 +264,19 @@ mod tests {
253264 let c = test_constraint ( ) ;
254265
255266 let id = dlq
256- . enqueue (
257- 42 ,
258- 0 ,
259- 0 ,
260- b"delta-bytes" . to_vec ( ) ,
261- & c,
262- "email already exists" . into ( ) ,
263- CompensationHint :: RetryWithDifferentValue {
267+ . enqueue ( EnqueueDeadLetterArgs {
268+ peer_id : 42 ,
269+ user_id : 0 ,
270+ tenant_id : 0 ,
271+ delta : b"delta-bytes" . to_vec ( ) ,
272+ constraint : & c,
273+ reason : "email already exists" . into ( ) ,
274+ hint : CompensationHint :: RetryWithDifferentValue {
264275 field : "email" . into ( ) ,
265276 conflicting_value : "alice@example.com" . into ( ) ,
266277 suggestion : "alice+1@example.com" . into ( ) ,
267278 } ,
268- )
279+ } )
269280 . unwrap ( ) ;
270281
271282 assert_eq ! ( dlq. len( ) , 1 ) ;
@@ -285,12 +296,36 @@ mod tests {
285296 reason : "test" . into ( ) ,
286297 } ;
287298
288- dlq. enqueue ( 1 , 0 , 0 , vec ! [ ] , & c, "r1" . into ( ) , hint. clone ( ) )
289- . unwrap ( ) ;
290- dlq. enqueue ( 2 , 0 , 0 , vec ! [ ] , & c, "r2" . into ( ) , hint. clone ( ) )
291- . unwrap ( ) ;
292-
293- let err = dlq. enqueue ( 3 , 0 , 0 , vec ! [ ] , & c, "r3" . into ( ) , hint) ;
299+ dlq. enqueue ( EnqueueDeadLetterArgs {
300+ peer_id : 1 ,
301+ user_id : 0 ,
302+ tenant_id : 0 ,
303+ delta : vec ! [ ] ,
304+ constraint : & c,
305+ reason : "r1" . into ( ) ,
306+ hint : hint. clone ( ) ,
307+ } )
308+ . unwrap ( ) ;
309+ dlq. enqueue ( EnqueueDeadLetterArgs {
310+ peer_id : 2 ,
311+ user_id : 0 ,
312+ tenant_id : 0 ,
313+ delta : vec ! [ ] ,
314+ constraint : & c,
315+ reason : "r2" . into ( ) ,
316+ hint : hint. clone ( ) ,
317+ } )
318+ . unwrap ( ) ;
319+
320+ let err = dlq. enqueue ( EnqueueDeadLetterArgs {
321+ peer_id : 3 ,
322+ user_id : 0 ,
323+ tenant_id : 0 ,
324+ delta : vec ! [ ] ,
325+ constraint : & c,
326+ reason : "r3" . into ( ) ,
327+ hint,
328+ } ) ;
294329 assert ! ( matches!( err, Err ( CrdtError :: DlqFull { .. } ) ) ) ;
295330 }
296331
@@ -314,14 +349,46 @@ mod tests {
314349 // Two entries for tenant 1 / users, one for tenant 1 / orders,
315350 // one for tenant 2 / users — only the first two should be
316351 // dropped by `purge_collection(1, "users")`.
317- dlq. enqueue ( 10 , 0 , 1 , vec ! [ ] , & users_c, "a" . into ( ) , hint. clone ( ) )
318- . unwrap ( ) ;
319- dlq. enqueue ( 11 , 0 , 1 , vec ! [ ] , & users_c, "b" . into ( ) , hint. clone ( ) )
320- . unwrap ( ) ;
321- dlq. enqueue ( 12 , 0 , 1 , vec ! [ ] , & orders_c, "c" . into ( ) , hint. clone ( ) )
322- . unwrap ( ) ;
323- dlq. enqueue ( 13 , 0 , 2 , vec ! [ ] , & users_c, "d" . into ( ) , hint. clone ( ) )
324- . unwrap ( ) ;
352+ dlq. enqueue ( EnqueueDeadLetterArgs {
353+ peer_id : 10 ,
354+ user_id : 0 ,
355+ tenant_id : 1 ,
356+ delta : vec ! [ ] ,
357+ constraint : & users_c,
358+ reason : "a" . into ( ) ,
359+ hint : hint. clone ( ) ,
360+ } )
361+ . unwrap ( ) ;
362+ dlq. enqueue ( EnqueueDeadLetterArgs {
363+ peer_id : 11 ,
364+ user_id : 0 ,
365+ tenant_id : 1 ,
366+ delta : vec ! [ ] ,
367+ constraint : & users_c,
368+ reason : "b" . into ( ) ,
369+ hint : hint. clone ( ) ,
370+ } )
371+ . unwrap ( ) ;
372+ dlq. enqueue ( EnqueueDeadLetterArgs {
373+ peer_id : 12 ,
374+ user_id : 0 ,
375+ tenant_id : 1 ,
376+ delta : vec ! [ ] ,
377+ constraint : & orders_c,
378+ reason : "c" . into ( ) ,
379+ hint : hint. clone ( ) ,
380+ } )
381+ . unwrap ( ) ;
382+ dlq. enqueue ( EnqueueDeadLetterArgs {
383+ peer_id : 13 ,
384+ user_id : 0 ,
385+ tenant_id : 2 ,
386+ delta : vec ! [ ] ,
387+ constraint : & users_c,
388+ reason : "d" . into ( ) ,
389+ hint : hint. clone ( ) ,
390+ } )
391+ . unwrap ( ) ;
325392
326393 let removed = dlq. purge_collection ( 1 , "users" ) ;
327394 assert_eq ! ( removed, 2 ) ;
@@ -352,11 +419,36 @@ mod tests {
352419 reason : "test" . into ( ) ,
353420 } ;
354421
355- dlq. enqueue ( 1 , 0 , 0 , vec ! [ ] , & c, "a" . into ( ) , hint. clone ( ) )
356- . unwrap ( ) ;
357- dlq. enqueue ( 2 , 0 , 0 , vec ! [ ] , & c, "b" . into ( ) , hint. clone ( ) )
358- . unwrap ( ) ;
359- dlq. enqueue ( 1 , 0 , 0 , vec ! [ ] , & c, "c" . into ( ) , hint) . unwrap ( ) ;
422+ dlq. enqueue ( EnqueueDeadLetterArgs {
423+ peer_id : 1 ,
424+ user_id : 0 ,
425+ tenant_id : 0 ,
426+ delta : vec ! [ ] ,
427+ constraint : & c,
428+ reason : "a" . into ( ) ,
429+ hint : hint. clone ( ) ,
430+ } )
431+ . unwrap ( ) ;
432+ dlq. enqueue ( EnqueueDeadLetterArgs {
433+ peer_id : 2 ,
434+ user_id : 0 ,
435+ tenant_id : 0 ,
436+ delta : vec ! [ ] ,
437+ constraint : & c,
438+ reason : "b" . into ( ) ,
439+ hint : hint. clone ( ) ,
440+ } )
441+ . unwrap ( ) ;
442+ dlq. enqueue ( EnqueueDeadLetterArgs {
443+ peer_id : 1 ,
444+ user_id : 0 ,
445+ tenant_id : 0 ,
446+ delta : vec ! [ ] ,
447+ constraint : & c,
448+ reason : "c" . into ( ) ,
449+ hint,
450+ } )
451+ . unwrap ( ) ;
360452
361453 let peer1 = dlq. drain_peer ( 1 ) ;
362454 assert_eq ! ( peer1. len( ) , 2 ) ;
@@ -372,9 +464,27 @@ mod tests {
372464 } ;
373465
374466 let id1 = dlq
375- . enqueue ( 1 , 0 , 0 , vec ! [ ] , & c, "a" . into ( ) , hint. clone ( ) )
467+ . enqueue ( EnqueueDeadLetterArgs {
468+ peer_id : 1 ,
469+ user_id : 0 ,
470+ tenant_id : 0 ,
471+ delta : vec ! [ ] ,
472+ constraint : & c,
473+ reason : "a" . into ( ) ,
474+ hint : hint. clone ( ) ,
475+ } )
476+ . unwrap ( ) ;
477+ let _id2 = dlq
478+ . enqueue ( EnqueueDeadLetterArgs {
479+ peer_id : 1 ,
480+ user_id : 0 ,
481+ tenant_id : 0 ,
482+ delta : vec ! [ ] ,
483+ constraint : & c,
484+ reason : "b" . into ( ) ,
485+ hint,
486+ } )
376487 . unwrap ( ) ;
377- let _id2 = dlq. enqueue ( 1 , 0 , 0 , vec ! [ ] , & c, "b" . into ( ) , hint) . unwrap ( ) ;
378488
379489 let removed = dlq. remove ( id1) . unwrap ( ) ;
380490 assert_eq ! ( removed. reason, "a" ) ;
0 commit comments