@@ -1225,18 +1225,22 @@ handle_follower(#append_entries_rpc{term = Term,
1225
1225
{NextState , State ,
1226
1226
[cast_reply (Id , LeaderId , Reply ) | Effects ]};
1227
1227
false ->
1228
- % % We need to ensure we make progress in case
1229
- % % the last applied index is lower than the last
1230
- % % valid index
1228
+ % % We need to ensure we make progress in case the
1229
+ % % leader is having to resend already received
1230
+ % % entries in order to validate, e.g. after a
1231
+ % % term_mismatch, hence we reply with success but
1232
+ % % only up to the last index we already had
1231
1233
LastValidatedIdx = max (LastApplied , LastValidIdx ),
1232
1234
? DEBUG (" ~ts : append_entries_rpc with last index ~b "
1233
1235
" including ~b entries did not validate local log. "
1234
- " Requesting resend from index ~b " ,
1235
- [LogId , PLIdx , length (Entries0 ),
1236
- LastValidatedIdx + 1 ]),
1237
- {Reply , State } =
1238
- mismatch_append_entries_reply (Term , LastValidatedIdx ,
1239
- State0 #{log => Log2 }),
1236
+ " Local last index ~b " ,
1237
+ [LogId , PLIdx , length (Entries0 ), LocalLastIdx ]),
1238
+ {LVTerm , State } = fetch_term (LastValidatedIdx , State0 ),
1239
+ Reply = # append_entries_reply {term = CurTerm ,
1240
+ success = true ,
1241
+ next_index = LastValidatedIdx + 1 ,
1242
+ last_index = LastValidatedIdx ,
1243
+ last_term = LVTerm },
1240
1244
{follower , State ,
1241
1245
[cast_reply (Id , LeaderId , Reply )]}
1242
1246
end ;
@@ -1246,14 +1250,12 @@ handle_follower(#append_entries_rpc{term = Term,
1246
1250
LeaderCommit ),
1247
1251
% % assert we're not writing below the last applied index
1248
1252
? assertNot (FstIdx < LastApplied ),
1249
- State2 = lists :foldl (fun pre_append_log_follower /2 ,
1250
- State1 , Entries ),
1253
+ State = lists :foldl (fun pre_append_log_follower /2 ,
1254
+ State1 , Entries ),
1251
1255
case ra_log :write (Entries , Log1 ) of
1252
1256
{ok , Log2 } ->
1253
- {NextState , State , Effects } =
1254
- evaluate_commit_index_follower (State2 #{log => Log2 },
1255
- Effects0 ),
1256
- {NextState , State , Effects };
1257
+ evaluate_commit_index_follower (State #{log => Log2 },
1258
+ Effects0 );
1257
1259
{error , wal_down } ->
1258
1260
% % at this point we know the wal process exited
1259
1261
% % but we dont know exactly which in flight messages
@@ -1265,9 +1267,9 @@ handle_follower(#append_entries_rpc{term = Term,
1265
1267
% % it wrote for each UID into an ETS table and query
1266
1268
% % this.
1267
1269
{await_condition ,
1268
- State2 #{log => Log1 ,
1269
- condition =>
1270
- #{predicate_fun => fun wal_down_condition /2 }},
1270
+ State #{log => Log1 ,
1271
+ condition =>
1272
+ #{predicate_fun => fun wal_down_condition /2 }},
1271
1273
Effects0 };
1272
1274
{error , _ } = Err ->
1273
1275
exit (Err )
@@ -1342,20 +1344,23 @@ handle_follower(#heartbeat_rpc{leader_id = LeaderId,
1342
1344
cfg := # cfg {id = Id }} = State ) ->
1343
1345
Reply = heartbeat_reply (CurTerm , QueryIndex ),
1344
1346
{follower , State , [cast_reply (Id , LeaderId , Reply )]};
1345
- handle_follower ({ra_log_event , {written , _ , _ } = Evt },
1346
- State0 = #{log := Log0 ,
1347
- cfg := # cfg {id = Id },
1348
- leader_id := LeaderId ,
1349
- current_term := Term })
1350
- when LeaderId =/= undefined ->
1347
+ handle_follower ({ra_log_event , Evt }, #{log := Log0 ,
1348
+ cfg := # cfg {id = Id },
1349
+ leader_id := LeaderId ,
1350
+ current_term := Term } = State0 ) ->
1351
+ % forward events to ra_log
1352
+ % if the last written changes then send an append entries reply
1353
+ LW = ra_log :last_written (Log0 ),
1351
1354
{Log , Effects } = ra_log :handle_event (Evt , Log0 ),
1352
1355
State = State0 #{log => Log },
1353
- Reply = append_entries_reply (Term , true , State ),
1354
- {follower , State , [cast_reply (Id , LeaderId , Reply ) | Effects ]};
1355
- handle_follower ({ra_log_event , Evt }, State = #{log := Log0 }) ->
1356
- % simply forward all other events to ra_log
1357
- {Log , Effects } = ra_log :handle_event (Evt , Log0 ),
1358
- {follower , State #{log => Log }, Effects };
1356
+ case LW =/= ra_log :last_written (Log ) of
1357
+ true when LeaderId =/= undefined ->
1358
+ % % last written has changed so we need to send an AER reply
1359
+ Reply = append_entries_reply (Term , true , State ),
1360
+ {follower , State , [cast_reply (Id , LeaderId , Reply ) | Effects ]};
1361
+ _ ->
1362
+ {follower , State , Effects }
1363
+ end ;
1359
1364
handle_follower (# pre_vote_rpc {},
1360
1365
#{cfg := # cfg {log_id = LogId },
1361
1366
membership := Membership } = State ) when Membership =/= voter ->
@@ -2180,7 +2185,10 @@ log_read(From0, To, Cache, Log0) ->
2180
2185
{From , Entries0 } = log_fold_cache (From0 , To , Cache , []),
2181
2186
ra_log :fold (From , To , fun (E , A ) -> [E | A ] end , Entries0 , Log0 ).
2182
2187
2183
- log_fold_cache (From , To , [{From , _ , _ } = Entry | Rem ], Acc ) ->
2188
+ % % this cache is a bit so and so as it will only really work when each follower
2189
+ % % begins with the same from index
2190
+ log_fold_cache (From , To , [{From , _ , _ } = Entry | Rem ], Acc )
2191
+ when From =< To ->
2184
2192
log_fold_cache (From + 1 , To , Rem , [Entry | Acc ]);
2185
2193
log_fold_cache (From , _To , _Cache , Acc ) ->
2186
2194
{From , Acc }.
0 commit comments