Skip to content

Commit

Permalink
(DOCSP-35302): C++: Add new sync control methods (#3148)
Browse files Browse the repository at this point in the history
## Pull Request Info

Jira ticket: https://jira.mongodb.org/browse/DOCSP-35302

- [Quick
Start](https://preview-mongodbdacharyc.gatsbyjs.io/realm/DOCSP-35302/sdk/cpp/quick-start/#open-a-realm-1):
Regenerate the "Open a Synced Realm" example without the sync session
initialization and method call.
- [Sync/Manage Sync
Sessions](https://preview-mongodbdacharyc.gatsbyjs.io/realm/DOCSP-35302/sdk/cpp/sync/manage-sync-session/):
Add new sync control methods for C++ v1.0.0.

### Reminder Checklist

Before merging your PR, make sure to check a few things.

- [x] Did you tag pages appropriately?
  - genre
  - meta.keywords
  - meta.description
- [x] Describe your PR's changes in the Release Notes section
- [x] Create a Jira ticket for related docs-app-services work, if any

### Release Notes

- **C++ SDK**
- Quick Start: Remove sync session initialization and method call from
the "Open a Synced Realm" example.
- Sync/Manage Sync Sessions: Add new sync control methods for C++
v1.0.0.

### Review Guidelines


[REVIEWING.md](https://github.com/mongodb/docs-realm/blob/master/REVIEWING.md)

---------

Co-authored-by: cbullinger <115956901+cbullinger@users.noreply.github.com>
  • Loading branch information
dacharyc and cbullinger committed Jan 9, 2024
1 parent c7a44f5 commit 3c316d3
Show file tree
Hide file tree
Showing 16 changed files with 244 additions and 25 deletions.
1 change: 1 addition & 0 deletions examples/cpp/sync/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ add_executable(examples-sync
flexible-sync.cpp
quick-start.cpp
sync-errors.cpp
sync-session.cpp
)

target_link_libraries(examples-sync PRIVATE Catch2::Catch2WithMain)
Expand Down
20 changes: 2 additions & 18 deletions examples/cpp/sync/quick-start.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,8 @@ TEST_CASE("sync quick start", "[realm][write][sync][sync-logger]") {
// :snippet-start: open-synced-realm
auto syncConfig = user.flexible_sync_configuration();
auto realm = realm::db(syncConfig);
// :snippet-start: sync-session
auto syncSession = realm.get_sync_session();
// :snippet-end:
// :snippet-start: sync-state
syncSession->state();
// :snippet-end:
// :remove-start:
syncSession->wait_for_download_completion().get();
realm.get_sync_session()->wait_for_download_completion().get();
realm.refresh();
// Remove any existing subscriptions before adding the one for this example
auto clearInitialSubscriptions =
Expand All @@ -150,13 +144,6 @@ TEST_CASE("sync quick start", "[realm][write][sync][sync-logger]") {
.get();
// :snippet-end:
CHECK(updateSubscriptionSuccess == true);
// We don't actually need this here - we use it up above
// in the Bluehawk remove block, but adding it to show
// a relevant Bluehawked example in the docs
// :snippet-start: wait-for-download
syncSession->wait_for_download_completion().get();
realm.refresh();
// :snippet-end:
// :snippet-start: write-to-synced-realm
auto todo = realm::Sync_Todo{.name = "Create a Sync todo item",
.status = "In Progress",
Expand All @@ -169,10 +156,7 @@ TEST_CASE("sync quick start", "[realm][write][sync][sync-logger]") {
CHECK(todos.size() == 1);
auto specificTodo = todos[0];
realm.write([&] { realm.remove(specificTodo); });

// :snippet-start: wait-for-upload
syncSession->wait_for_upload_completion().get();
// :snippet-end:
realm.get_sync_session()->wait_for_upload_completion().get();
}

// :replace-end:
97 changes: 97 additions & 0 deletions examples/cpp/sync/sync-session.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include <catch2/catch_test_macros.hpp>
#include <cpprealm/sdk.hpp>

static const std::string APP_ID = "cpp-tester-uliix";

// :replace-start: {
// "terms": {
// "Local_": "",
// "Sync_": ""
// }
// }

namespace realm {
struct Sync_Todo {
realm::primary_key<realm::object_id> _id{realm::object_id::generate()};
std::string name;
std::string status;
std::string ownerId;
};
REALM_SCHEMA(Sync_Todo, _id, name, status, ownerId);
} // namespace realm

TEST_CASE("sync session", "[realm][write][sync]") {
auto appConfig = realm::App::configuration();
appConfig.app_id = APP_ID;
auto app = realm::App(appConfig);
auto user = app.login(realm::App::credentials::anonymous()).get();
auto syncConfig = user.flexible_sync_configuration();
auto realm = realm::db(syncConfig);
// :snippet-start: sync-session
auto syncSession = realm.get_sync_session();
// :snippet-end:
// :snippet-start: sync-state
syncSession->state();
// :snippet-end:
// :snippet-start: wait-for-download
syncSession->wait_for_download_completion().get();
realm.refresh();
// :snippet-end:
// Remove any existing subscriptions before adding the one for this example
auto clearInitialSubscriptions =
realm.subscriptions().update([](auto& subs) { subs.clear(); }).get();
CHECK(clearInitialSubscriptions == true);
CHECK(realm.subscriptions().size() == 0);
// For this example, get the userId for the Flexible Sync query
auto userId = user.identifier();
auto subscriptions = realm.subscriptions();
auto updateSubscriptionSuccess =
subscriptions
.update([&](realm::mutable_sync_subscription_set& subs) {
subs.add<realm::Sync_Todo>("todos", [&userId](auto& obj) {
// For this example, get only Sync_Todo items where the ownerId
// property value is equal to the userId of the logged-in user.
return obj.ownerId == userId;
});
})
.get();
CHECK(updateSubscriptionSuccess == true);
auto todo = realm::Sync_Todo{.name = "Create a Sync todo item",
.status = "In Progress",
.ownerId = userId};

realm.write([&] { realm.add(std::move(todo)); });

// :snippet-start: pause
syncSession->pause();
// :snippet-end:
// :snippet-start: connection-state
syncSession->connection_state();
// :snippet-end:
CHECK(syncSession->connection_state() ==
realm::internal::bridge::sync_session::connection_state::disconnected);
auto todos = realm.objects<realm::Sync_Todo>();
CHECK(todos.size() == 1);
auto specificTodo = todos[0];
realm.write([&] { realm.remove(specificTodo); });
// :snippet-start: resume
syncSession->resume();
// :snippet-end:
// :snippet-start: observe-connection-change
auto connectionToken = syncSession->observe_connection_change(
[&](enum realm::sync_session::connection_state,
enum realm::sync_session::connection_state new_state) {
// Register a block to execute when connection state changes.
});
// :snippet-end:
// :snippet-start: reconnect
syncSession->reconnect();
// :snippet-end:
// :snippet-start: unregister-observation-token
syncSession->unregister_connection_change_observer(connectionToken);
// :snippet-end:
// :snippet-start: wait-for-upload
syncSession->wait_for_upload_completion().get();
// :snippet-end:
}
// :replace-end:
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
auto syncConfig = user.flexible_sync_configuration();
auto realm = realm::db(syncConfig);
auto syncSession = realm.get_sync_session();
syncSession->state();
// For this example, get the userId for the Flexible Sync query
auto userId = user.identifier();
auto subscriptions = realm.subscriptions();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->connection_state();
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
auto connectionToken = syncSession->observe_connection_change(
[&](enum realm::sync_session::connection_state,
enum realm::sync_session::connection_state new_state) {
// Register a block to execute when connection state changes.
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->pause();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->reconnect();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->resume();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
syncSession->unregister_connection_change_observer(connectionToken);
8 changes: 8 additions & 0 deletions source/sdk/cpp/quick-start.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
Quick Start - C++ SDK
=====================

.. meta::
:keywords: code example
:description: Learn how to quickly get started with Atlas Device SDK for C++.

.. facet::
:name: genre
:values: tutorial

.. contents:: On this page
:local:
:backlinks: none
Expand Down
131 changes: 126 additions & 5 deletions source/sdk/cpp/sync/manage-sync-session.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
Manage a Sync Session - C++ SDK
===============================

.. meta::
:keywords: code example
:description: Check and manage network and Device Sync connection state through the sync session.

.. facet::
:name: genre
:values: reference

.. contents:: On this page
:local:
:backlinks: none
Expand Down Expand Up @@ -39,26 +47,102 @@ You can use the member function :cpp-sdk:`get_sync_session()
object for any synced realm. The SDK returns this object as an optional.
It is a lightweight handle that you can pass around by value.

.. literalinclude:: /examples/generated/cpp/quick-start.snippet.sync-session.cpp
.. literalinclude:: /examples/generated/cpp/sync-session.snippet.sync-session.cpp
:language: cpp

.. _cpp-check-network-connection:

Check the Network Connection
----------------------------

.. tip::

The SDK's *offline-first* design means that you generally don't
need to check the current network connection state. That said, the
``connection_state()`` property is available if your app calls for some
indication of connection state.

To check the connection state, you can read the
:cpp-sdk:`sync session instance's <structrealm_1_1internal_1_1bridge_1_1sync__session.html>`
``connection_state()`` property directly.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.connection-state.cpp
:language: cpp

You can also observe the connection state with the
:cpp-sdk:`observe_connection_change() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#a38096e71024b3fd252d3356af5eee113>`
function. This function registers a callback that the SDK invokes when the
underlying sync session changes its connection state.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.observe-connection-change.cpp
:language: cpp

If you register a connection change listener, you can unregister it when
you're done listening for changes. Call the sync session instance's
:cpp-sdk:`unregister_connection_change_observer() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#a86b911bc662f02f607c5e9513d80c0c7>`
method to unregister an observation token.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.unregister-observation-token.cpp
:language: cpp

The network connection state is distinct from the Device Sync connection state
that you can check with the ``state()`` method. For more information about
sync connection state, refer to the Check the Sync State documentation on
this page.

.. _cpp-pause-resume-sync-session:

Pause or Resume a Sync Session
------------------------------

You can pause and resume the sync session on the realm.
Pausing a sync session only suspends that realm's sync session. If you have
more than one open realm, suspend does not affect the sync sessions for
other realms.

To pause a sync session, call the sync session's
:cpp-sdk:`pause() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#ab187b267f0391c8262a0edcff32fcc29>`
method.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.pause.cpp
:language: cpp

To resume a sync session, call the sync session's
:cpp-sdk:`resume() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#a253ef9e08d9f4cf7c42edfd6b6f6df80>`
method.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.resume.cpp
:language: cpp

When to Pause a Sync Session
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. include:: /includes/when-to-pause-sync.rst

.. _cpp-sync-wait-for-changes:

Wait for Changes to Upload and Download
---------------------------------------

You can use the
:cpp-sdk:`sync_session <structrealm_1_1internal_1_1bridge_1_1sync__session.html>`'s
``wait_for_upload_completion()`` and ``wait_for_download_completion()``
methods to wait for changes to upload to or download from Atlas. Both of these
methods can optionally take a callback to execute when upload or download
is complete.

To wait for all changes to upload to Atlas from your synced realm,
use the member function ``.wait_for_upload_completion()``.
use the member function ``wait_for_upload_completion()``.

.. literalinclude:: /examples/generated/cpp/quick-start.snippet.wait-for-upload.cpp
.. literalinclude:: /examples/generated/cpp/sync-session.snippet.wait-for-upload.cpp
:language: cpp

To wait for all changes from Atlas
to download to your synced realm, use the member function
``wait_for_download_completion()``. Refresh the realm after downloading
any changes to be sure it reflects the most recent data.

.. literalinclude:: /examples/generated/cpp/quick-start.snippet.wait-for-download.cpp
.. literalinclude:: /examples/generated/cpp/sync-session.snippet.wait-for-download.cpp
:language: cpp

.. _cpp-check-sync-state:
Expand All @@ -71,5 +155,42 @@ You can use the :cpp-sdk:`sync_session
member function ``state()`` to check whether the sync session is active.
This returns an enum whose value reflects possible Device Sync states.

.. literalinclude:: /examples/generated/cpp/quick-start.snippet.sync-state.cpp
.. literalinclude:: /examples/generated/cpp/sync-session.snippet.sync-state.cpp
:language: cpp

The sync connection state is distinct from the network connection state
that you can check with the ``connection_state()`` method. For more information
about network connection state, refer to the Check the Network Connection
documentation on this page.

.. _cpp-reconnect-sync-sessions:

Manually Reconnect All Sync Sessions
------------------------------------

Realm automatically detects when a device regains connectivity after being
offline and attempts to reconnect using an incremental backoff strategy.

You can choose to manually trigger a reconnect attempt with a sync session's
:cpp-sdk:`reconnect() <structrealm_1_1internal_1_1bridge_1_1sync__session.html#af5557f2a2ae8869e6a129afc264e8695>`
method instead of waiting for the duration of the incremental backoff. This is
useful if you have a more accurate understanding of the network conditions
and don't want to rely on Realm's automatic reconnect detection.

.. literalinclude:: /examples/generated/cpp/sync-session.snippet.reconnect.cpp
:language: cpp

When you call this method, the SDK forces all sync sessions to *attempt* to
reconnect immediately. This resets any timers used for incremental
backoff.

Calling this method does not guarantee the device can reconnect. If the SDK
gets a fatal error, or if the device is already connected or is trying to
connect, calling this method has no effect.

.. important:: Cannot Reconnect Within Socket Read Timeout Duration

Realm has an internal default socket read timeout of 2 minutes, where
Realm will time out if a read operation does not receive any data
within a 2-minute window. If you call ``reconnect()`` within that window,
the SDK does *not* attempt to reconnect.

0 comments on commit 3c316d3

Please sign in to comment.