@@ -236,6 +236,13 @@ pub struct RaftCore<T: Storage> {
236
236
/// Enable this if greater cluster stability is preferred over faster elections.
237
237
pub pre_vote : bool ,
238
238
239
+ /// Enable follower replication.
240
+ ///
241
+ /// This enables data replication from a follower to other servers in the same available zone.
242
+ ///
243
+ /// Enable this for reducing across-AZ traffic of cloud deployment.
244
+ pub follower_repl : bool ,
245
+
239
246
skip_bcast_commit : bool ,
240
247
batch_append : bool ,
241
248
@@ -337,6 +344,7 @@ impl<T: Storage> Raft<T> {
337
344
promotable : false ,
338
345
check_quorum : c. check_quorum ,
339
346
pre_vote : c. pre_vote ,
347
+ follower_repl : c. follower_repl ,
340
348
read_only : ReadOnly :: new ( c. read_only_option ) ,
341
349
heartbeat_timeout : c. heartbeat_tick ,
342
350
election_timeout : c. election_tick ,
@@ -1372,6 +1380,7 @@ impl<T: Storage> Raft<T> {
1372
1380
if m. get_msg_type ( ) == MessageType :: MsgAppend
1373
1381
|| m. get_msg_type ( ) == MessageType :: MsgHeartbeat
1374
1382
|| m. get_msg_type ( ) == MessageType :: MsgSnapshot
1383
+ || m. get_msg_type ( ) == MessageType :: MsgGroupBroadcast && self . follower_repl
1375
1384
{
1376
1385
self . become_follower ( m. term , m. from ) ;
1377
1386
} else {
@@ -1381,7 +1390,8 @@ impl<T: Storage> Raft<T> {
1381
1390
} else if m. term < self . term {
1382
1391
if ( self . check_quorum || self . pre_vote )
1383
1392
&& ( m. get_msg_type ( ) == MessageType :: MsgHeartbeat
1384
- || m. get_msg_type ( ) == MessageType :: MsgAppend )
1393
+ || m. get_msg_type ( ) == MessageType :: MsgAppend
1394
+ || m. get_msg_type ( ) == MessageType :: MsgGroupBroadcast && self . follower_repl )
1385
1395
{
1386
1396
// We have received messages from a leader at a lower term. It is possible
1387
1397
// that these messages were simply delayed in the network, but this could
@@ -2314,6 +2324,11 @@ impl<T: Storage> Raft<T> {
2314
2324
self . leader_id = m. from ;
2315
2325
self . handle_append_entries ( & m) ;
2316
2326
}
2327
+ MessageType :: MsgGroupBroadcast => {
2328
+ self . election_elapsed = 0 ;
2329
+ self . leader_id = m. from ;
2330
+ self . handle_group_broadcast ( & m) ;
2331
+ }
2317
2332
MessageType :: MsgHeartbeat => {
2318
2333
self . election_elapsed = 0 ;
2319
2334
self . leader_id = m. from ;
@@ -2425,13 +2440,14 @@ impl<T: Storage> Raft<T> {
2425
2440
Err ( Error :: RequestSnapshotDropped )
2426
2441
}
2427
2442
2428
- // TODO: revoke pub when there is a better way to test .
2429
- /// For a given message, append the entries to the log.
2430
- pub fn handle_append_entries ( & mut self , m : & Message ) {
2443
+ /// Try to append entries, and return the append result .
2444
+ /// Return true only if the entries in the message has been appended in the log successfully .
2445
+ pub fn try_append_entries ( & mut self , m : & Message ) -> bool {
2431
2446
if self . pending_request_snapshot != INVALID_INDEX {
2432
2447
self . send_request_snapshot ( ) ;
2433
- return ;
2448
+ return false ;
2434
2449
}
2450
+
2435
2451
if m. index < self . raft_log . committed {
2436
2452
debug ! (
2437
2453
self . logger,
@@ -2443,13 +2459,14 @@ impl<T: Storage> Raft<T> {
2443
2459
to_send. index = self . raft_log . committed ;
2444
2460
to_send. commit = self . raft_log . committed ;
2445
2461
self . r . send ( to_send, & mut self . msgs ) ;
2446
- return ;
2462
+ return false ;
2447
2463
}
2448
2464
2449
2465
let mut to_send = Message :: default ( ) ;
2450
2466
to_send. to = m. from ;
2451
2467
to_send. set_msg_type ( MessageType :: MsgAppendResponse ) ;
2452
2468
2469
+ let mut success = true ;
2453
2470
if let Some ( ( _, last_idx) ) = self
2454
2471
. raft_log
2455
2472
. maybe_append ( m. index , m. log_term , m. commit , & m. entries )
@@ -2458,7 +2475,7 @@ impl<T: Storage> Raft<T> {
2458
2475
} else {
2459
2476
debug ! (
2460
2477
self . logger,
2461
- "rejected msgApp [logterm: {msg_log_term}, index: {msg_index}] \
2478
+ "Reject append [logterm: {msg_log_term}, index: {msg_index}] \
2462
2479
from {from}",
2463
2480
msg_log_term = m. log_term,
2464
2481
msg_index = m. index,
@@ -2483,9 +2500,70 @@ impl<T: Storage> Raft<T> {
2483
2500
to_send. reject = true ;
2484
2501
to_send. reject_hint = hint_index;
2485
2502
to_send. log_term = hint_term. unwrap ( ) ;
2503
+ success = false ;
2486
2504
}
2487
2505
to_send. set_commit ( self . raft_log . committed ) ;
2488
2506
self . r . send ( to_send, & mut self . msgs ) ;
2507
+ success
2508
+ }
2509
+
2510
+ // TODO: revoke pub when there is a better way to test.
2511
+ /// For a given message, append the entries to the log.
2512
+ pub fn handle_append_entries ( & mut self , m : & Message ) {
2513
+ self . try_append_entries ( m) ;
2514
+ }
2515
+
2516
+ /// For a broadcast, append entries to onw log and forward MsgAppend to other dest.
2517
+ pub fn handle_group_broadcast ( & mut self , m : & Message ) {
2518
+ if self . try_append_entries ( m) {
2519
+ // If the agent fails to append entries from the leader,
2520
+ // the agent cannot forward MsgAppend.
2521
+ let agent_id = m. get_to ( ) ;
2522
+ for forward in m. get_forwards ( ) {
2523
+ // Dest should be in the cluster.
2524
+ if self . prs ( ) . get ( forward. get_to ( ) ) . is_some ( ) {
2525
+ // Fetch log entries from the forward.index to the last index of log.
2526
+ let ents = self . raft_log . entries (
2527
+ forward. get_index ( ) + 1 ,
2528
+ self . max_msg_size ,
2529
+ GetEntriesContext ( GetEntriesFor :: SendAppend {
2530
+ to : forward. get_to ( ) ,
2531
+ term : m. term ,
2532
+ aggressively : false ,
2533
+ } ) ,
2534
+ ) ;
2535
+ if self
2536
+ . raft_log
2537
+ . match_term ( forward. get_index ( ) , forward. get_log_term ( ) )
2538
+ {
2539
+ let mut m_append = Message :: default ( ) ;
2540
+ m_append. to = forward. get_to ( ) ;
2541
+ m_append. from = m. get_from ( ) ;
2542
+ m_append. set_msg_type ( MessageType :: MsgAppend ) ;
2543
+ m_append. index = forward. get_index ( ) ;
2544
+ m_append. log_term = forward. get_log_term ( ) ;
2545
+ m_append. set_entries ( ents. unwrap ( ) . into ( ) ) ;
2546
+ m_append. commit = m. get_commit ( ) ;
2547
+ m_append. commit_term = m. get_commit_term ( ) ;
2548
+ debug ! (
2549
+ self . logger,
2550
+ "Peer {} forward MsgAppend from {} to {}" ,
2551
+ agent_id,
2552
+ m_append. from,
2553
+ m_append. to
2554
+ ) ;
2555
+ self . r . send ( m_append, & mut self . msgs )
2556
+ } else {
2557
+ warn ! (
2558
+ self . logger,
2559
+ "The agent's log does not match with index {} log term {} in forward message" ,
2560
+ forward. get_index( ) ,
2561
+ forward. get_log_term( )
2562
+ ) ;
2563
+ }
2564
+ }
2565
+ }
2566
+ }
2489
2567
}
2490
2568
2491
2569
// TODO: revoke pub when there is a better way to test.
0 commit comments