-
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-2099 Restore progress notifier behavior when sync session is already caught up #7681
Changes from 4 commits
31aa243
3024fc7
99605de
45aa3af
ab82d79
8e0248c
2681a3d
ebc3728
458da4b
187c6c8
c199c6f
396a820
b97054b
7b4b429
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -821,10 +821,10 @@ void SyncSession::cancel_pending_waits(util::CheckedUniqueLock lock, Status erro | |
|
||
void SyncSession::handle_progress_update(uint64_t downloaded, uint64_t downloadable, uint64_t uploaded, | ||
uint64_t uploadable, uint64_t snapshot_version, double download_estimate, | ||
double upload_estimate) | ||
double upload_estimate, int64_t query_version) | ||
{ | ||
m_progress_notifier.update(downloaded, downloadable, uploaded, uploadable, snapshot_version, download_estimate, | ||
upload_estimate); | ||
upload_estimate, query_version); | ||
} | ||
|
||
static sync::Session::Config::ClientReset make_client_reset_config(const RealmConfig& base_config, | ||
|
@@ -962,10 +962,10 @@ void SyncSession::create_sync_session() | |
m_session->set_progress_handler([weak_self](uint_fast64_t downloaded, uint_fast64_t downloadable, | ||
uint_fast64_t uploaded, uint_fast64_t uploadable, | ||
uint_fast64_t snapshot_version, double download_estimate, | ||
double upload_estimate) { | ||
double upload_estimate, int64_t query_version) { | ||
if (auto self = weak_self.lock()) { | ||
self->handle_progress_update(downloaded, downloadable, uploaded, uploadable, snapshot_version, | ||
download_estimate, upload_estimate); | ||
download_estimate, upload_estimate, query_version); | ||
} | ||
}); | ||
|
||
|
@@ -1267,7 +1267,12 @@ void SyncSession::wait_for_download_completion(util::UniqueFunction<void(Status) | |
uint64_t SyncSession::register_progress_notifier(std::function<ProgressNotifierCallback>&& notifier, | ||
ProgressDirection direction, bool is_streaming) | ||
{ | ||
return m_progress_notifier.register_callback(std::move(notifier), direction, is_streaming); | ||
int64_t pending_query_version = 0; | ||
assert_mutex_unlocked(); | ||
if (auto sub_store = get_flx_subscription_store()) { | ||
pending_query_version = sub_store->get_version_info().latest; | ||
} | ||
return m_progress_notifier.register_callback(std::move(notifier), direction, is_streaming, pending_query_version); | ||
} | ||
|
||
void SyncSession::unregister_progress_notifier(uint64_t token) | ||
|
@@ -1519,22 +1524,23 @@ void SyncSession::did_drop_external_reference() | |
} | ||
|
||
uint64_t SyncProgressNotifier::register_callback(std::function<ProgressNotifierCallback> notifier, | ||
NotifierType direction, bool is_streaming) | ||
NotifierType direction, bool is_streaming, | ||
int64_t pending_query_version) | ||
{ | ||
util::UniqueFunction<void()> invocation; | ||
uint64_t token_value = 0; | ||
{ | ||
std::lock_guard<std::mutex> lock(m_mutex); | ||
token_value = m_progress_notifier_token++; | ||
NotifierPackage package{std::move(notifier), m_local_transaction_version, is_streaming, | ||
direction == NotifierType::download}; | ||
direction == NotifierType::download, pending_query_version}; | ||
if (!m_current_progress) { | ||
// Simply register the package, since we have no data yet. | ||
m_packages.emplace(token_value, std::move(package)); | ||
return token_value; | ||
} | ||
bool skip_registration = false; | ||
invocation = package.create_invocation(*m_current_progress, skip_registration, true); | ||
invocation = package.create_invocation(*m_current_progress, skip_registration); | ||
if (skip_registration) { | ||
token_value = 0; | ||
} | ||
|
@@ -1553,13 +1559,14 @@ void SyncProgressNotifier::unregister_callback(uint64_t token) | |
} | ||
|
||
void SyncProgressNotifier::update(uint64_t downloaded, uint64_t downloadable, uint64_t uploaded, uint64_t uploadable, | ||
uint64_t snapshot_version, double download_estimate, double upload_estimate) | ||
uint64_t snapshot_version, double download_estimate, double upload_estimate, | ||
int64_t query_version) | ||
{ | ||
std::vector<util::UniqueFunction<void()>> invocations; | ||
{ | ||
std::lock_guard<std::mutex> lock(m_mutex); | ||
m_current_progress = Progress{uploadable, downloadable, uploaded, downloaded, | ||
upload_estimate, download_estimate, snapshot_version}; | ||
m_current_progress = Progress{uploadable, downloadable, uploaded, downloaded, | ||
upload_estimate, download_estimate, snapshot_version, query_version}; | ||
|
||
for (auto it = m_packages.begin(); it != m_packages.end();) { | ||
bool should_delete = false; | ||
|
@@ -1579,49 +1586,50 @@ void SyncProgressNotifier::set_local_version(uint64_t snapshot_version) | |
} | ||
|
||
util::UniqueFunction<void()> | ||
SyncProgressNotifier::NotifierPackage::create_invocation(Progress const& current_progress, bool& is_expired, | ||
bool initial_registration) | ||
SyncProgressNotifier::NotifierPackage::create_invocation(Progress const& current_progress, bool& is_expired) | ||
{ | ||
uint64_t transferred = is_download ? current_progress.downloaded : current_progress.uploaded; | ||
uint64_t transfered = is_download ? current_progress.downloaded : current_progress.uploaded; | ||
uint64_t transferable = is_download ? current_progress.downloadable : current_progress.uploadable; | ||
double progress_estimate = is_download ? current_progress.download_estimate : current_progress.upload_estimate; | ||
|
||
// If the sync client has not yet processed all of the local | ||
// transactions then the uploadable data is incorrect and we should | ||
// not invoke the callback | ||
if (!is_download && snapshot_version > current_progress.snapshot_version) | ||
return [] {}; | ||
|
||
// for download only invoke the callback on registration if is in active data transfer, | ||
// otherwise delay notifying until an update with the new transfer signaled | ||
if (is_download && !started_notifying && progress_estimate >= 1) { | ||
if (initial_registration) { | ||
initial_transferred = transferred; | ||
return [] {}; | ||
} | ||
else if (initial_transferred == transferred) | ||
double estimate = is_download ? current_progress.download_estimate : current_progress.upload_estimate; | ||
|
||
if (!is_streaming) { | ||
// If the sync client has not yet processed all of the local | ||
// transactions then the uploadable data is incorrect and we should | ||
// not invoke the callback | ||
if (!is_download && snapshot_version > current_progress.snapshot_version) | ||
return [] {}; | ||
} | ||
|
||
started_notifying = true; | ||
// If this is a non-streaming download progress update and this notifier was | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this check prevents reporting any progress until the latest query at the time the user registered the notifier starts bootstrapping right? I guess this is nice because there is no 0->1->0->....->1 anymore, but it may take a long time until the user gets any progress. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, for the most common case this means we skip query version zero and you get progress notifications for version 1. But yeah, in the worst case if you had a whole bunch of outstanding subscription sets you could end up having to wait for them all to sync. |
||
// created for a later query version (i.e. we're currently downloading | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. e.g., not i.e. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
// subscription set version zero, but subscription set version 1 existed | ||
// when the notifier was registered), then we want to skip this callback. | ||
if (is_download && current_progress.query_version < pending_query_version) { | ||
danieltabacaru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return [] {}; | ||
} | ||
|
||
// only capture and adjust transferable bytes for upload non-streaming to provide | ||
// the progress of upload for the callback registered right after the commit | ||
if (!is_streaming && !is_download) { | ||
// The initial download size we get from the server is the uncompacted | ||
// size, and so the download may complete before we actually receive | ||
// that much data. When that happens, transferrable will drop and we | ||
Comment on lines
+1609
to
+1611
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the mention of compaction here is out of date. transferable can still sometimes drop with BaaS, but it no longer due to compaction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, i dunno. it can also drop due to compaction with pbs. tbh, i don't know the full set of circumstances when this can happen, but i wanted to preserve the behavior. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't this actually also used for upload as a way to save the uploadable bytes at the time of registration (or when update is called first)? The change below also suggests this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well, yeah, but why we do that exactly is not clear anymore. |
||
// need to use the new value instead of the captured one. | ||
if (!captured_transferable || *captured_transferable > transferable) | ||
captured_transferable = transferable; | ||
transferable = *captured_transferable; | ||
} | ||
|
||
// A notifier is expired for upload if at least as many bytes have been transferred | ||
// as were originally considered transferable based on local committed version | ||
// on callback registration, or when simply 1.0 progress is reached for download | ||
// since the amount of bytes is not precisely known until the end | ||
if (!is_streaming) | ||
is_expired = is_download ? progress_estimate >= 1 : transferred >= transferable; | ||
// Since we can adjust the transferrable downwards the estimate for uploads | ||
// won't be correct since the sync client's view of the estimate is based on | ||
// the total number of uploadable bytes available rather than the number of | ||
// bytes this NotifierPackage was waiting to upload. | ||
if (!is_download) { | ||
estimate = std::min(transfered / double(transferable), 1.0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice catch! but you need to check transferable is greater than 0. Running There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm. okay. |
||
} | ||
} | ||
|
||
// A notifier is expired if at least as many bytes have been transferred | ||
// as were originally considered transferrable. | ||
is_expired = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understand it correctly, this also fires when there is a fresh realm with no subscriptions and the schema is downloaded (as opposed to previous implementation) right? If that's the case it could be worth mentioning it in the description. Edit: the check at line 1605 actually prevents this. |
||
!is_streaming && (transfered >= transferable && (!is_download || !pending_query_version || estimate >= 1.0)); | ||
return [=, notifier = notifier] { | ||
notifier(transferred, transferable, progress_estimate); | ||
notifier(transfered, transferable, estimate); | ||
}; | ||
} | ||
|
||
|
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.
register_progress_notifier()
needs to be annotated asREQUIRES(!m_state_mutex)
rather than this.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.
fixed