-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RCORE-2191 Add support for multi-process subscription state change notifications #7862
Conversation
9da38f2
to
876436b
Compare
if (auto status = sess->receive_query_error_message(raw_error_code, message, query_version); !status.is_ok()) { | ||
close_due_to_protocol_error(std::move(status)); | ||
if (Session* sess = find_and_validate_session(session_ident, "QUERY_ERROR")) { | ||
sess->receive_query_error_message(raw_error_code, message, query_version); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
receive_query_error_message() only ever returned Status::Ok() the code handling it returning an error was dead.
// Ignore the message if the deactivation process has been initiated, | ||
// because in that case, the associated Realm and SessionWrapper must | ||
// not be accessed any longer. | ||
if (m_state == Active) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checking for m_state == Active was done several time along the way so I eliminated all but one.
{ | ||
auto tr = m_db->start_read(); | ||
auto bootstrap_table = tr->get_table(m_table); | ||
if (bootstrap_table->is_empty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
peek_pending() and pop_front_pending() are only called if has_pending() returns true, so the table should never be empty in either of them.
@@ -245,7 +240,7 @@ PendingBootstrapStore::PendingBatch PendingBootstrapStore::peek_pending(size_t l | |||
progress_obj.get<int64_t>(m_progress_download_client_version); | |||
progress.upload.last_integrated_server_version = progress_obj.get<int64_t>(m_progress_upload_server_version); | |||
progress.upload.client_version = progress_obj.get<int64_t>(m_progress_upload_client_version); | |||
ret.progress = std::move(progress); | |||
ret.progress = progress; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SyncProgress is trivially copyable so moving it doesn't do anything.
|
||
private: | ||
DBRef m_db; | ||
// The pending_bootstrap_store is tied to the lifetime of a session, so a shared_ptr is not needed | ||
util::Logger& m_logger; | ||
_impl::ClientProtocol m_client_protocol; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been unused for a while.
src/realm/sync/subscriptions.cpp
Outdated
@@ -548,7 +558,7 @@ SubscriptionSet MutableSubscriptionSet::commit() | |||
const auto flx_version = version(); | |||
m_tr->commit_and_continue_as_read(); | |||
|
|||
mgr->process_notifications(m_state, flx_version, std::string_view(error_str())); | |||
mgr->report_progress(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notifications will be sent without this call, but the timing would change and that maybe could be a problem.
CHECK_NOT(store.has_pending()); | ||
pending_batch = store.peek_pending(1024); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now an assertion failure rather than returning an empty result.
876436b
to
54a10e7
Compare
m_sess->logger.debug("Marking query version %1 as complete after receiving MARK message", | ||
m_flx_pending_mark_version); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to keep this debug message to show when the bootstrap/version is complete. Perhaps move it to be printed out in download_complete()
.
src/realm/sync/subscriptions.hpp
Outdated
// Mark query_version as having begun bootstrapping. This should be called | ||
// inside the write transaction used to store the first set of changesets. | ||
// | ||
// This should only be called internally within the sync client. | ||
void begin_bootstrap(const Transaction&, int64_t query_version); | ||
// Mark query_version as having completed bootstrapping. This should be | ||
// called inside the write transaction which removes the final pending changeset. | ||
// | ||
// This should only be called internally within the sync client. | ||
void complete_bootstrap(const Transaction&, int64_t query_version); | ||
// Roll query_version back to the Pending state if it is currently Bootstrapping. | ||
// Has no effect if the bootstrap in progress is not the first boostrap for | ||
// this subscription set. | ||
// | ||
// This should only be called internally within the sync client. | ||
void update_state(int64_t version_id, SubscriptionSet::State state, | ||
std::optional<std::string_view> error_str = util::none) | ||
REQUIRES(!m_pending_notifications_mutex); | ||
void cancel_bootstrap(const Transaction&, int64_t query_version); | ||
// Report that a download has completed, meaning that the active subscription | ||
// set should advance to the Completed state if it is currently in the | ||
// AwaitingMark state. Has no effect if it is in any other state. | ||
// | ||
// This should only be called internally within the sync client. | ||
void download_complete(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment line about server initiated bootstraps for these 4 functions - e.g.,
// Has no effect if this is a server initiated bootstrap for the active query version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall the changes look good; just two minor comments
This branch needs to be updated to the latest feature/role-change and I'd prefer to get the feature branch merged to master
prior to merging these changes.
7d3c8d8
to
dd4488c
Compare
Pull Request Test Coverage Report for Build thomas.goyne_466Details
💛 - Coveralls |
dd4488c
to
2f366b5
Compare
if (old_state == State::Complete) { | ||
throw RuntimeError(ErrorCodes::SyncProtocolInvariantFailed, | ||
util::format("Received error '%1' for already-completed query version %2. This " | ||
"may be due to a queryable field being removed in the server-side " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this even possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if the scenario mentioned in the message is real or if this is just guarding against the server doing something invalid.
@@ -1626,15 +1508,34 @@ void SessionWrapper::check_progress() | |||
REALM_ASSERT(!m_finalized); | |||
REALM_ASSERT(m_sess); | |||
|
|||
if (!m_progress_handler && m_upload_completion_handlers.empty() && m_sync_completion_handlers.empty()) | |||
// Check if there's anything which even wants progress or completion information | |||
bool has_progress_handler = m_progress_handler && m_reliable_download_progress; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a valid reason outside of the unit tests to not have a progress handler at this point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, m_progress_handler is always set when using SyncSession and it's only null in the sync unit tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
As with the other multi-process notifications, the core idea here is to eliminate the in-memory state and produce notifications based entirely on the current state of the Realm file. SubscriptionStore::update_state() has been replaced with separate functions for the specific legal state transitions, which also take a write transaction as a parameter. These functions are called by PendingBootstrapStore inside the same write transaction as the bootstrap updates which changed the subscription state. This is both a minor performance optimization (due to fewer writes) and eliminates a brief window between the two writes where the Realm file was in an inconsistent state. There's a minor functional change here: previously old subscription sets were superseded when the new one reached the Completed state, and now they are superseded on AwaitingMark. This aligns it with when the new subscription set becomes the one which is returned by get_active().
2f366b5
to
dec4408
Compare
As with the other multi-process notifications, the core idea here is to eliminate the in-memory state and produce notifications based entirely on the current state of the Realm file.
SubscriptionStore::update_state() has been replaced with separate functions for the specific legal state transitions, which also take a write transaction as a parameter. These functions are called by PendingBootstrapStore inside the same write transaction as the bootstrap updates which changed the subscription state. This is both a minor performance optimization (due to fewer writes) and eliminates a brief window between the two writes where the Realm file was in an inconsistent state.
There's a minor functional change here: previously old subscription sets were superseded when the new one reached the Completed state, and now they are superseded on AwaitingMark. This aligns it with when the new subscription set becomes the one which is returned by get_active().
Fixes #7861.