@@ -5070,22 +5070,23 @@ void TestRaft_delete_configuration_change_entries(CuTest *tc)
5070
5070
5071
5071
void * r = raft_new ();
5072
5072
raft_add_node (r , NULL , 1 , 1 );
5073
- raft_add_node (r , NULL , 2 , 0 );
5074
-
5075
5073
raft_set_callbacks (r , & funcs , NULL );
5076
5074
raft_set_current_term (r , 1 );
5077
- raft_set_state (r , RAFT_STATE_LEADER );
5075
+ raft_become_leader (r );
5076
+ raft_apply_all (r );
5077
+
5078
+ raft_add_node (r , NULL , 2 , 0 );
5078
5079
5079
5080
/* If there is no entry, delete should return immediately. */
5080
- CuAssertIntEquals (tc , 0 , raft_delete_entry_from_idx (r , 2 ));
5081
+ CuAssertIntEquals (tc , 0 , raft_delete_entry_from_idx (r , 3 ));
5081
5082
5082
5083
/* Add the non-voting node. */
5083
5084
raft_entry_t * ety = __MAKE_ENTRY (1 , 1 , "3" );
5084
5085
ety -> type = RAFT_LOGTYPE_ADD_NONVOTING_NODE ;
5085
5086
CuAssertIntEquals (tc , 0 , raft_recv_entry (r , ety , NULL ));
5086
5087
5087
5088
/* If there idx is out of bounds, delete should return immediately. */
5088
- CuAssertIntEquals (tc , 0 , raft_delete_entry_from_idx (r , 2 ));
5089
+ CuAssertIntEquals (tc , 0 , raft_delete_entry_from_idx (r , 3 ));
5089
5090
5090
5091
/* Append a removal log entry for the non-voting node we just added. */
5091
5092
ety = __MAKE_ENTRY (1 , 1 , "3" );
@@ -5100,12 +5101,12 @@ void TestRaft_delete_configuration_change_entries(CuTest *tc)
5100
5101
5101
5102
/* Add some random entries. */
5102
5103
__RAFT_APPEND_ENTRIES_SEQ_ID (r , 10 , 1 , 1 , "data" );
5103
- CuAssertIntEquals (tc , 12 , raft_get_log_count (r ));
5104
+ CuAssertIntEquals (tc , 13 , raft_get_log_count (r ));
5104
5105
5105
5106
/* Delete the removal log entry and others after it. */
5106
- CuAssertIntEquals (tc , 0 , raft_delete_entry_from_idx (r , 2 ));
5107
+ CuAssertIntEquals (tc , 0 , raft_delete_entry_from_idx (r , 3 ));
5107
5108
5108
- CuAssertIntEquals (tc , 1 , raft_get_log_count (r ));
5109
+ CuAssertIntEquals (tc , 2 , raft_get_log_count (r ));
5109
5110
CuAssertIntEquals (tc , 1 , raft_node_is_active (raft_get_node (r , 3 )));
5110
5111
CuAssertIntEquals (tc , 0 , raft_node_is_voting (raft_get_node (r , 3 )));
5111
5112
@@ -5152,6 +5153,55 @@ void TestRaft_propagate_persist_metadata_failure(CuTest *tc)
5152
5153
CuAssertIntEquals (tc , e , -1 );
5153
5154
}
5154
5155
5156
+ void TestRaft_reject_config_change_before_log_replay (CuTest * tc )
5157
+ {
5158
+ raft_cbs_t funcs = {
5159
+ .get_node_id = __raft_get_node_id
5160
+ };
5161
+
5162
+ raft_server_t * r = raft_new ();
5163
+ raft_set_callbacks (r , & funcs , NULL );
5164
+ raft_add_non_voting_node (r , NULL , 1 , 1 );
5165
+
5166
+ raft_become_leader (r );
5167
+
5168
+ raft_entry_req_t * ety = __MAKE_ENTRY (1 , 1 , "1" );
5169
+ ety -> type = RAFT_LOGTYPE_ADD_NODE ;
5170
+ CuAssertIntEquals (tc , 0 , raft_recv_entry (r , ety , NULL ));
5171
+ raft_periodic_internal (r , 2000 );
5172
+
5173
+ /* To simulate restart, restore log with the first node's data.*/
5174
+ raft_server_t * r2 = raft_new ();
5175
+ r2 -> log = raft_get_log (r );
5176
+
5177
+ raft_set_callbacks (r2 , & funcs , NULL );
5178
+ raft_add_non_voting_node (r2 , NULL , 1 , 1 );
5179
+
5180
+ raft_restore_metadata (r2 , 1 , 0 );
5181
+ raft_restore_log (r2 );
5182
+ raft_become_leader (r2 );
5183
+
5184
+ ety = __MAKE_ENTRY (2 , 2 , "2" );
5185
+ ety -> type = RAFT_LOGTYPE_ADD_NODE ;
5186
+
5187
+ /* Node has cfg change entry in the log, but it doesn't know the commit idx.
5188
+ * As server does not know whether cfg change entry in the log was applied
5189
+ * in the previous run, it should reply with TRYAGAIN instead of
5190
+ * ONE_VOTING_CHANGE_ONLY which might be confusing if there is no ongoing
5191
+ * cfg change. */
5192
+ CuAssertIntEquals (tc , RAFT_ERR_TRYAGAIN , raft_recv_entry (r2 , ety , NULL ));
5193
+
5194
+ raft_set_commit_idx (r2 , raft_get_current_idx (r2 ));
5195
+ raft_apply_all (r2 );
5196
+
5197
+ /* After log replay is completed (server applied NOOP entry of the term),
5198
+ * multiple config change requests should be rejected with
5199
+ * ONE_VOTING_CHANGE_ONLY. */
5200
+ CuAssertIntEquals (tc , 0 , raft_recv_entry (r2 , ety , NULL ));
5201
+ CuAssertIntEquals (tc , RAFT_ERR_ONE_VOTING_CHANGE_ONLY ,
5202
+ raft_recv_entry (r2 , ety , NULL ));
5203
+ }
5204
+
5155
5205
int main (void )
5156
5206
{
5157
5207
CuString * output = CuStringNew ();
@@ -5309,7 +5359,7 @@ int main(void)
5309
5359
SUITE_ADD_TEST (suite , TestRaft_test_metadata_on_restart );
5310
5360
SUITE_ADD_TEST (suite , TestRaft_rebuild_config_after_restart );
5311
5361
SUITE_ADD_TEST (suite , TestRaft_delete_configuration_change_entries );
5312
- SUITE_ADD_TEST (suite , TestRaft_propagate_persist_metadata_failure );
5362
+ SUITE_ADD_TEST (suite , TestRaft_reject_config_change_before_log_replay );
5313
5363
CuSuiteRun (suite );
5314
5364
CuSuiteDetails (suite , output );
5315
5365
printf ("%s\n" , output -> buffer );
0 commit comments