void replica::check_state_completeness() { /* prepare commit durable */ dassert(max_prepared_decree() >= last_committed_decree(), ""); dassert(last_committed_decree() >= last_durable_decree(), ""); auto mind = _stub->_log->max_gced_decree(get_gpid(), _app->init_info().init_offset_in_shared_log); dassert(mind <= last_durable_decree(), ""); _stub->_log->check_valid_start_offset(get_gpid(), _app->init_info().init_offset_in_shared_log); if (_private_log != nullptr) { auto mind = _private_log->max_gced_decree(get_gpid(), _app->init_info().init_offset_in_private_log); dassert(mind <= last_durable_decree(), ""); _private_log->check_valid_start_offset(get_gpid(), _app->init_info().init_offset_in_private_log); } }
bool replica::update_local_configuration(const replica_configuration& config, bool same_ballot/* = false*/) { dassert(config.ballot > get_ballot() || (same_ballot && config.ballot == get_ballot()), ""); dassert (config.gpid == get_gpid(), ""); partition_status old_status = status(); ballot old_ballot = get_ballot(); // skip unncessary configuration change if (old_status == config.status && old_ballot == config.ballot) return true; // skip invalid change switch (old_status) { case PS_ERROR: { ddebug( "%s: status change from %s @ %lld to %s @ %lld is not allowed", name(), enum_to_string(old_status), old_ballot, enum_to_string(config.status), config.ballot ); return false; } break; case PS_INACTIVE: if ((config.status == PS_PRIMARY || config.status == PS_SECONDARY) && !_inactive_is_transient) { ddebug( "%s: status change from %s @ %lld to %s @ %lld is not allowed when inactive state is not transient", name(), enum_to_string(old_status), old_ballot, enum_to_string(config.status), config.ballot ); return false; } break; case PS_POTENTIAL_SECONDARY: if (config.status == PS_ERROR || config.status == PS_INACTIVE) { if (!_potential_secondary_states.cleanup(false)) { dwarn( "%s: status change from %s @ %lld to %s @ %lld is not allowed coz learning remote state is still running", name(), enum_to_string(old_status), old_ballot, enum_to_string(config.status), config.ballot ); return false; } } break; case PS_SECONDARY: if (config.status != PS_SECONDARY && _secondary_states.checkpoint_task != nullptr) { dwarn( "%s: status change from %s @ %lld to %s @ %lld is not allowed coz checkpointing is still running", name(), enum_to_string(old_status), old_ballot, enum_to_string(config.status), config.ballot ); return false; } break; } uint64_t oldTs = _last_config_change_time_ms; _config = config; _last_config_change_time_ms =now_ms(); dassert (max_prepared_decree() >= last_committed_decree(), ""); switch (old_status) { case PS_PRIMARY: cleanup_preparing_mutations(true); switch (config.status) { case PS_PRIMARY: replay_prepare_list(); break; case PS_INACTIVE: _primary_states.cleanup(old_ballot != config.ballot); break; case PS_SECONDARY: case PS_ERROR: _primary_states.cleanup(); break; case PS_POTENTIAL_SECONDARY: dassert (false, "invalid execution path"); break; default: dassert (false, "invalid execution path"); } break; case PS_SECONDARY: cleanup_preparing_mutations(true); switch (config.status) { case PS_PRIMARY: init_group_check(); replay_prepare_list(); break; case PS_SECONDARY: break; case PS_POTENTIAL_SECONDARY: // InActive in config break; case PS_INACTIVE: break; case PS_ERROR: break; default: dassert (false, "invalid execution path"); } break; case PS_POTENTIAL_SECONDARY: switch (config.status) { case PS_PRIMARY: dassert (false, "invalid execution path"); break; case PS_SECONDARY: _prepare_list->truncate(_app->last_committed_decree()); _potential_secondary_states.cleanup(true); check_state_completeness(); break; case PS_POTENTIAL_SECONDARY: break; case PS_INACTIVE: _potential_secondary_states.cleanup(true); break; case PS_ERROR: _prepare_list->reset(_app->last_committed_decree()); _potential_secondary_states.cleanup(true); break; default: dassert (false, "invalid execution path"); } break; case PS_INACTIVE: switch (config.status) { case PS_PRIMARY: _inactive_is_transient = false; init_group_check(); replay_prepare_list(); break; case PS_SECONDARY: _inactive_is_transient = false; break; case PS_POTENTIAL_SECONDARY: _inactive_is_transient = false; break; case PS_INACTIVE: break; case PS_ERROR: _inactive_is_transient = false; break; default: dassert (false, "invalid execution path"); } break; case PS_ERROR: switch (config.status) { case PS_PRIMARY: dassert (false, "invalid execution path"); break; case PS_SECONDARY: dassert (false, "invalid execution path"); break; case PS_POTENTIAL_SECONDARY: dassert(false, "invalid execution path"); break; case PS_INACTIVE: dassert (false, "invalid execution path"); break; case PS_ERROR: break; default: dassert (false, "invalid execution path"); } break; default: dassert (false, "invalid execution path"); } dwarn( "%s: status change %s @ %lld => %s @ %lld, pre(%llu, %llu), app(%llu, %llu), duration=%llu ms", name(), enum_to_string(old_status), old_ballot, enum_to_string(status()), get_ballot(), _prepare_list->max_decree(), _prepare_list->last_committed_decree(), _app->last_committed_decree(), _app->last_durable_decree(), _last_config_change_time_ms - oldTs ); if (status() != old_status) { bool isClosing = (status() == PS_ERROR || (status() == PS_INACTIVE && get_ballot() > old_ballot)); _stub->notify_replica_state_update(config, isClosing); if (isClosing) { ddebug("%s: being close ...", name()); _stub->begin_close_replica(this); return false; } } else { _stub->notify_replica_state_update(config, false); } return true; }