@@ -90,6 +90,9 @@ pub struct EventObserver {
90
90
pub endpoint : String ,
91
91
/// Timeout for sending events to this observer
92
92
pub timeout : Duration ,
93
+ /// If true, the stacks-node will not retry if event delivery fails for any reason.
94
+ /// WARNING: This should not be set on observers that require successful delivery of all events.
95
+ pub disable_retries : bool ,
93
96
}
94
97
95
98
struct ReceiptPayloadInfo < ' a > {
@@ -439,7 +442,7 @@ impl EventObserver {
439
442
440
443
for ( id, url, payload, timeout_ms) in pending_payloads {
441
444
let timeout = Duration :: from_millis ( timeout_ms) ;
442
- Self :: send_payload_directly ( & payload, & url, timeout) ;
445
+ Self :: send_payload_directly ( & payload, & url, timeout, false ) ;
443
446
444
447
#[ cfg( test) ]
445
448
if TEST_EVENT_OBSERVER_SKIP_RETRY . get ( ) {
@@ -456,7 +459,12 @@ impl EventObserver {
456
459
}
457
460
}
458
461
459
- fn send_payload_directly ( payload : & serde_json:: Value , full_url : & str , timeout : Duration ) {
462
+ fn send_payload_directly (
463
+ payload : & serde_json:: Value ,
464
+ full_url : & str ,
465
+ timeout : Duration ,
466
+ disable_retries : bool ,
467
+ ) {
460
468
debug ! (
461
469
"Event dispatcher: Sending payload" ; "url" => %full_url, "payload" => ?payload
462
470
) ;
@@ -506,6 +514,11 @@ impl EventObserver {
506
514
}
507
515
}
508
516
517
+ if disable_retries {
518
+ warn ! ( "Observer is configured in disable_retries mode: skipping retry of payload" ) ;
519
+ return ;
520
+ }
521
+
509
522
#[ cfg( test) ]
510
523
if TEST_EVENT_OBSERVER_SKIP_RETRY . get ( ) {
511
524
warn ! ( "Fault injection: skipping retry of payload" ) ;
@@ -522,7 +535,12 @@ impl EventObserver {
522
535
}
523
536
}
524
537
525
- fn new ( working_dir : Option < PathBuf > , endpoint : String , timeout : Duration ) -> Self {
538
+ fn new (
539
+ working_dir : Option < PathBuf > ,
540
+ endpoint : String ,
541
+ timeout : Duration ,
542
+ disable_retries : bool ,
543
+ ) -> Self {
526
544
let db_path = if let Some ( mut db_path) = working_dir {
527
545
db_path. push ( "event_observers.sqlite" ) ;
528
546
@@ -541,6 +559,7 @@ impl EventObserver {
541
559
db_path,
542
560
endpoint,
543
561
timeout,
562
+ disable_retries,
544
563
}
545
564
}
546
565
@@ -555,7 +574,10 @@ impl EventObserver {
555
574
} ;
556
575
let full_url = format ! ( "http://{url_str}" ) ;
557
576
558
- if let Some ( db_path) = & self . db_path {
577
+ // if the observer is in "disable_retries" mode quickly send the payload without checking for the db
578
+ if self . disable_retries {
579
+ Self :: send_payload_directly ( payload, & full_url, self . timeout , true ) ;
580
+ } else if let Some ( db_path) = & self . db_path {
559
581
let conn =
560
582
Connection :: open ( db_path) . expect ( "Failed to open database for event observer" ) ;
561
583
@@ -566,7 +588,7 @@ impl EventObserver {
566
588
Self :: process_pending_payloads ( & conn) ;
567
589
} else {
568
590
// No database, just send the payload
569
- Self :: send_payload_directly ( payload, & full_url, self . timeout ) ;
591
+ Self :: send_payload_directly ( payload, & full_url, self . timeout , false ) ;
570
592
}
571
593
}
572
594
@@ -1666,8 +1688,13 @@ impl EventDispatcher {
1666
1688
Some ( working_dir) ,
1667
1689
conf. endpoint . clone ( ) ,
1668
1690
Duration :: from_millis ( conf. timeout_ms ) ,
1691
+ conf. disable_retries ,
1669
1692
) ;
1670
1693
1694
+ if conf. disable_retries {
1695
+ warn ! ( "Observer {} is configured in \" disable_retries\" mode: events are not guaranteed to be delivered" , conf. endpoint) ;
1696
+ }
1697
+
1671
1698
let observer_index = self . registered_observers . len ( ) as u16 ;
1672
1699
1673
1700
for event_key_type in conf. events_keys . iter ( ) {
@@ -1770,7 +1797,8 @@ mod test {
1770
1797
1771
1798
#[ test]
1772
1799
fn build_block_processed_event ( ) {
1773
- let observer = EventObserver :: new ( None , "nowhere" . to_string ( ) , Duration :: from_secs ( 3 ) ) ;
1800
+ let observer =
1801
+ EventObserver :: new ( None , "nowhere" . to_string ( ) , Duration :: from_secs ( 3 ) , false ) ;
1774
1802
1775
1803
let filtered_events = vec ! [ ] ;
1776
1804
let block = StacksBlock :: genesis_block ( ) ;
@@ -1830,7 +1858,8 @@ mod test {
1830
1858
1831
1859
#[ test]
1832
1860
fn test_block_processed_event_nakamoto ( ) {
1833
- let observer = EventObserver :: new ( None , "nowhere" . to_string ( ) , Duration :: from_secs ( 3 ) ) ;
1861
+ let observer =
1862
+ EventObserver :: new ( None , "nowhere" . to_string ( ) , Duration :: from_secs ( 3 ) , false ) ;
1834
1863
1835
1864
let filtered_events = vec ! [ ] ;
1836
1865
let mut block_header = NakamotoBlockHeader :: empty ( ) ;
@@ -2087,7 +2116,8 @@ mod test {
2087
2116
let endpoint = "http://example.com" . to_string ( ) ;
2088
2117
let timeout = Duration :: from_secs ( 5 ) ;
2089
2118
2090
- let observer = EventObserver :: new ( Some ( working_dir. clone ( ) ) , endpoint. clone ( ) , timeout) ;
2119
+ let observer =
2120
+ EventObserver :: new ( Some ( working_dir. clone ( ) ) , endpoint. clone ( ) , timeout, false ) ;
2091
2121
2092
2122
// Verify fields
2093
2123
assert_eq ! ( observer. endpoint, endpoint) ;
@@ -2104,7 +2134,7 @@ mod test {
2104
2134
let endpoint = "http://example.com" . to_string ( ) ;
2105
2135
let timeout = Duration :: from_secs ( 5 ) ;
2106
2136
2107
- let observer = EventObserver :: new ( None , endpoint. clone ( ) , timeout) ;
2137
+ let observer = EventObserver :: new ( None , endpoint. clone ( ) , timeout, false ) ;
2108
2138
2109
2139
// Verify fields
2110
2140
assert_eq ! ( observer. endpoint, endpoint) ;
@@ -2133,7 +2163,7 @@ mod test {
2133
2163
let endpoint = server. url ( ) . strip_prefix ( "http://" ) . unwrap ( ) . to_string ( ) ;
2134
2164
let timeout = Duration :: from_secs ( 5 ) ;
2135
2165
2136
- let observer = EventObserver :: new ( Some ( working_dir) , endpoint, timeout) ;
2166
+ let observer = EventObserver :: new ( Some ( working_dir) , endpoint, timeout, false ) ;
2137
2167
2138
2168
TEST_EVENT_OBSERVER_SKIP_RETRY . set ( false ) ;
2139
2169
@@ -2170,7 +2200,7 @@ mod test {
2170
2200
2171
2201
let endpoint = server. url ( ) . strip_prefix ( "http://" ) . unwrap ( ) . to_string ( ) ;
2172
2202
2173
- let observer = EventObserver :: new ( None , endpoint, timeout) ;
2203
+ let observer = EventObserver :: new ( None , endpoint, timeout, false ) ;
2174
2204
2175
2205
// Call send_payload
2176
2206
observer. send_payload ( & payload, "/test" ) ;
@@ -2201,8 +2231,12 @@ mod test {
2201
2231
tx. send ( ( ) ) . unwrap ( ) ;
2202
2232
} ) ;
2203
2233
2204
- let observer =
2205
- EventObserver :: new ( None , format ! ( "127.0.0.1:{port}" ) , Duration :: from_secs ( 3 ) ) ;
2234
+ let observer = EventObserver :: new (
2235
+ None ,
2236
+ format ! ( "127.0.0.1:{port}" ) ,
2237
+ Duration :: from_secs ( 3 ) ,
2238
+ false ,
2239
+ ) ;
2206
2240
2207
2241
let payload = json ! ( { "key" : "value" } ) ;
2208
2242
@@ -2250,8 +2284,12 @@ mod test {
2250
2284
}
2251
2285
} ) ;
2252
2286
2253
- let observer =
2254
- EventObserver :: new ( None , format ! ( "127.0.0.1:{port}" ) , Duration :: from_secs ( 3 ) ) ;
2287
+ let observer = EventObserver :: new (
2288
+ None ,
2289
+ format ! ( "127.0.0.1:{port}" ) ,
2290
+ Duration :: from_secs ( 3 ) ,
2291
+ false ,
2292
+ ) ;
2255
2293
2256
2294
let payload = json ! ( { "key" : "value" } ) ;
2257
2295
@@ -2298,7 +2336,7 @@ mod test {
2298
2336
}
2299
2337
} ) ;
2300
2338
2301
- let observer = EventObserver :: new ( None , format ! ( "127.0.0.1:{port}" ) , timeout) ;
2339
+ let observer = EventObserver :: new ( None , format ! ( "127.0.0.1:{port}" ) , timeout, false ) ;
2302
2340
2303
2341
let payload = json ! ( { "key" : "value" } ) ;
2304
2342
@@ -2391,7 +2429,12 @@ mod test {
2391
2429
}
2392
2430
} ) ;
2393
2431
2394
- let observer = EventObserver :: new ( Some ( working_dir) , format ! ( "127.0.0.1:{port}" ) , timeout) ;
2432
+ let observer = EventObserver :: new (
2433
+ Some ( working_dir) ,
2434
+ format ! ( "127.0.0.1:{port}" ) ,
2435
+ timeout,
2436
+ false ,
2437
+ ) ;
2395
2438
2396
2439
let payload = json ! ( { "key" : "value" } ) ;
2397
2440
let payload2 = json ! ( { "key" : "value2" } ) ;
@@ -2417,4 +2460,70 @@ mod test {
2417
2460
rx. recv_timeout ( Duration :: from_secs ( 5 ) )
2418
2461
. expect ( "Server did not receive request in time" ) ;
2419
2462
}
2463
+
2464
+ #[ test]
2465
+ fn test_event_dispatcher_disable_retries ( ) {
2466
+ let timeout = Duration :: from_secs ( 5 ) ;
2467
+ let payload = json ! ( { "key" : "value" } ) ;
2468
+
2469
+ // Create a mock server returning error 500
2470
+ let mut server = mockito:: Server :: new ( ) ;
2471
+ let _m = server. mock ( "POST" , "/test" ) . with_status ( 500 ) . create ( ) ;
2472
+
2473
+ let endpoint = server. url ( ) . strip_prefix ( "http://" ) . unwrap ( ) . to_string ( ) ;
2474
+
2475
+ let observer = EventObserver :: new ( None , endpoint, timeout, true ) ;
2476
+
2477
+ // in non "disable_retries" mode this will run forever
2478
+ observer. send_payload ( & payload, "/test" ) ;
2479
+
2480
+ // Verify that the payload was sent
2481
+ _m. assert ( ) ;
2482
+ }
2483
+
2484
+ #[ test]
2485
+ fn test_event_dispatcher_disable_retries_invalid_url ( ) {
2486
+ let timeout = Duration :: from_secs ( 5 ) ;
2487
+ let payload = json ! ( { "key" : "value" } ) ;
2488
+
2489
+ let endpoint = String :: from ( "255.255.255.255" ) ;
2490
+
2491
+ let observer = EventObserver :: new ( None , endpoint, timeout, true ) ;
2492
+
2493
+ // in non "disable_retries" mode this will run forever
2494
+ observer. send_payload ( & payload, "/test" ) ;
2495
+ }
2496
+
2497
+ #[ test]
2498
+ #[ ignore]
2499
+ /// This test generates a new block and ensures the "disable_retries" events_observer will not block.
2500
+ fn block_event_with_disable_retries_observer ( ) {
2501
+ let dir = tempdir ( ) . unwrap ( ) ;
2502
+ let working_dir = dir. path ( ) . to_path_buf ( ) ;
2503
+
2504
+ let mut event_dispatcher = EventDispatcher :: new ( ) ;
2505
+ let config = EventObserverConfig {
2506
+ endpoint : String :: from ( "255.255.255.255" ) ,
2507
+ events_keys : vec ! [ EventKeyType :: MinedBlocks ] ,
2508
+ timeout_ms : 1000 ,
2509
+ disable_retries : true ,
2510
+ } ;
2511
+ event_dispatcher. register_observer ( & config, working_dir) ;
2512
+
2513
+ let nakamoto_block = NakamotoBlock {
2514
+ header : NakamotoBlockHeader :: empty ( ) ,
2515
+ txs : vec ! [ ] ,
2516
+ } ;
2517
+
2518
+ // this will block forever in non "disable_retries" mode
2519
+ event_dispatcher. process_mined_nakamoto_block_event (
2520
+ 0 ,
2521
+ & nakamoto_block,
2522
+ 0 ,
2523
+ & ExecutionCost :: max_value ( ) ,
2524
+ vec ! [ ] ,
2525
+ ) ;
2526
+
2527
+ assert_eq ! ( event_dispatcher. registered_observers. len( ) , 1 ) ;
2528
+ }
2420
2529
}
0 commit comments