Write Data to a Synced Realm - C++ SDK
이 페이지의 내용
When writing data to a synced realm using Flexible Sync, you can use the same APIs as when performing CRUD operations on a non-synced realm. However, there are some differences in behavior to keep in mind as you develop your application.
동기화된 영역에 쓰는 경우 쓰기 작업이 다음 두 가지 모두 와 일치해야 합니다.
동기화 구독 쿼리
App Services App의 권한
쿼리 구독이나 사용자의 권한과 일치하지 않는 데이터를 쓰려고 하면 Realm은 보상 쓰기라는 치명적이지 않은 오류 작업을 통해 쓰기를 되돌립니다.
앱의 권한을 구성하는 방법에 대해 자세히 알아보려면 Atlas App Services 문서에서 역할 기반 권한 및 Device Sync 권한 가이드 를 참조하세요.
권한 거부 오류, 쓰기 오류 보상 및 기타 Device Sync 오류 유형에 대해 자세히 알아보려면 Atlas App Services 문서에서 동기화 오류 를 참조하세요.
동기화할 데이터 확인
동기화된 영역에 쓸 수 있는 데이터는 다음에 따라 결정됩니다.
Realm Mobile Sync 구성
앱의 권한
영역을 열 때 사용되는 Flexible Sync 구독 쿼리
이 페이지의 예제에서는 다음 Realm Mobile Sync 구성이 있는 Atlas App Services App과 다음 Realm SDK Realm 데이터 모델 및 구독이 있는 클라이언트 앱을 사용합니다.
이 예제에서 클라이언트 앱은 다음 객체 모델을 사용합니다.
struct Item { realm::primary_key<realm::object_id> _id{realm::object_id::generate()}; std::string ownerId; std::string itemName; int64_t complexity; }; REALM_SCHEMA(Item, _id, ownerId, itemName, complexity)
App Services 구성
Based on the above example object model, Device Sync is configured with the following queryable fields:
_id
(항상 포함)complexity
ownerId
App Services App에는 사용자가 자신의 데이터만 읽고 쓸 수 있도록 구성된 권한이 있습니다.
{ "roles": [ { "name": "readOwnWriteOwn", "apply_when": {}, "document_filters": { "write": { "ownerId": "%%user.id" }, "read": { "ownerId": "%%user.id" } }, "read": true, "write": true, "insert": true, "delete": true, "search": true } ] }
ownerId
이(가) 로그인한 사용자의 user.identifier()
와(과) 일치하지 않는 Atlas collection 객체는 이 영역과 동기화할 수 없습니다.
클라이언트 Realm 데이터 모델 및 구성
The examples on this page use the following synced realm configuration with this sync query and object model:
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 dbConfig = user.flexible_sync_configuration(); auto syncRealm = realm::db(dbConfig); // Add subscription auto subscriptionUpdateSuccess = syncRealm.subscriptions() .update([](realm::mutable_sync_subscription_set &subs) { // Get Items from Atlas that match this query. // Uses the queryable field `complexity`. // Sync Item objects with complexity less than or equal to 4. subs.add<realm::Item>( "simple items", [](auto &obj) { return obj.complexity <= 4; }); }) .get();
struct Item { realm::primary_key<realm::object_id> _id{realm::object_id::generate()}; std::string ownerId; std::string itemName; int64_t complexity; }; REALM_SCHEMA(Item, _id, ownerId, itemName, complexity)
With this Sync query, any object in the Atlas collection where the
complexity
property's value is greater than 4
cannot sync to this
realm.
동기화된 Realm에 쓰기
Flexible Sync Realm에 대한 쓰기는 쓰기가 권한 및 Flexible Sync 구독 쿼리와 일치하는지 여부에 따라 크게 두 가지 범주 중 하나로 분류될 수 있습니다.
성공적인 쓰기: 기록된 객체가 쿼리 구독 및 사용자의 권한과 모두 일치합니다. 객체가 영역에 성공적으로 쓰고 App Services 백엔드 및 기타 기기와 성공적으로 동기화됩니다.
쓰기 보상: 기록된 객체가 구독 쿼리와 일치하지 않거나 사용자에게 쓰기를 수행할 수 있는 충분한 권한이 없습니다. Realm은 보상 쓰기 작업을 통해 불법적인 쓰기를 되돌립니다.
팁
쿼리 구독과 일치하지 않는 객체를 작성하려는 경우 객체가 쿼리 구독과 일치하는 다른 영역을 열 수 있습니다. 또는 권한이나 구독 쿼리를 적용하지 않는 동기화되지 않은 영역에 객체를 쓸 수 있습니다.
성공적인 쓰기
When the write matches both user permissions 개인정보 정책에 the query subscription in the client, the Realm C++ SDK can successfully write the object to the synced realm. This object syncs with the App Services backend when the device has a network connection.
// Per the Device Sync permissions, users can only read and write data // where the `Item.ownerId` property matches their own user ID. auto simpleItem = realm::Item{.ownerId = user.identifier(), .itemName = "This item meets sync criteria", .complexity = 3}; // `simpleItem` successfully writes to the realm and syncs to Atlas // because its data matches the subscription query (complexity <= 4) // and its `ownerId` field matches the user ID. syncRealm.write([&] { syncRealm.add(std::move(simpleItem)); });
쓰기 보상
When the write doesn't match either the query subscription or user permissions, Realm reverts the write and provides an array of compensating_write_error_info objects.
좀 더 구체적으로 말하자면, 쿼리 구독의 범위를 벗어나거나 사용자의 권한과 일치하지 않는 데이터를 쓰면 다음과 같은 상황이 발생합니다.
클라이언트 영역에는 '불법적인' 쓰기에 대한 개념이 없으므로 Realm이 App Services 백엔드로 변경 집합을 해결할 때까지 처음에는 쓰기가 성공합니다.
동기화되면 서버는 규칙과 권한을 적용합니다. 서버는 사용자에게 쓰기를 수행할 수 있는 권한 부여가 없다고 판단합니다.
서버는 "보상 쓰기(compensating write)"라고 하는 되돌리기 작업을 클라이언트에 다시 보냅니다.
클라이언트의 영역이 잘못된 쓰기 작업을 되돌립니다.
해당 객체에 대한 불법적인 쓰기와 해당 보상 쓰기 사이에 지정된 객체에 대한 모든 클라이언트 사이드 쓰기는 손실됩니다. 실제로 이는 쓰기가 성공한 것처럼 보일 수 있지만 Realm이 App Services 백엔드와 동기화되고 보상 쓰기를 수행하면 객체가 '사라집니다'.
When this occurs, you can refer to the App Services logs
or use the compensating_writes_info()
function in the client to get
additional information on the error. For more information, refer
to the 쓰기 오류 정보 보상 section on this page.
쓰기가 쿼리 구독과 일치하지 않음
위에서 설명한 Flexible Sync Realm의 구성 을 감안할 때 이 객체를 쓰려고 하면 객체가 쿼리 구독과 일치하지 않기 때문에 보상 쓰기 오류가 발생합니다.
// The complexity of this item is `7`. This is outside the bounds // of the subscription query, which triggers a compensating write. auto complexItem = realm::Item{._id = primaryKey, .ownerId = user.identifier(), .itemName = "Test compensating writes", .complexity = 7}; // This should trigger a compensating write error when it tries to sync // due to server-side permissions, which gets logged with the error handler. syncRealm.write([&] { syncRealm.add(std::move(complexItem)); });
Connection[2]: Session[10]: Received: ERROR "Client attempted a write that is not allowed; it has been reverted" (error_code=231, is_fatal=false, error_action=Warning)
App Services 로그에 다음과 같은 오류 메시지가 표시됩니다.
Error: Client attempted a write that is not allowed; it has been reverted (ProtocolErrorCode=231) Details: { "Item": { "ObjectID(\"6557ddb0bf050934870ca0f5\")": "write to ObjectID(\"6557ddb0bf050934870ca0f5\") in table \"Item\" not allowed; object is outside of the current query view" } }
쓰기가 권한과 일치하지 않음
위에 자세히 설명된 Device Sync 구성의 권한 이 주어졌을 때 이 객체를 쓰려고 하면 ownerId
속성이 로그인한 사용자의 user.identifier()
와 일치하지 않기 때문에 보상 쓰기 오류가 발생합니다.
// The `ownerId` of this item does not match the user ID of the logged-in // user. The user does not have permissions to make this write, which // triggers a compensating write. auto itemWithWrongOwner = realm::Item{ .ownerId = "not the current user", .itemName = "Trigger an incorrect permissions compensating write", .complexity = 1}; syncRealm.write([&] { syncRealm.add(std::move(itemWithWrongOwner)); });
Connection[2]: Session[11]: Received: ERROR "Client attempted a write that is not allowed; it has been reverted" (error_code=231, is_fatal=false, error_action=Warning)
App Services 로그에 다음과 같은 오류 메시지가 표시됩니다.
Error: Client attempted a write that is outside of permissions or query filters; it has been reverted (ProtocolErrorCode=231) Details: { "Item": { "ObjectID(\"6557ddbabf050934870ca0f8\")": "write to ObjectID(\"6557ddbabf050934870ca0f8\") in table \"Item\" was denied by write filter in role \"readOwnWriteOwn\"" } }
쓰기 오류 정보 보상
You can get additional information in the client about why a compensating
write occurs using the compensating_writes_info()
function, which provides an array of compensating_write_error_info
structs that contain:
클라이언트가 쓰려고 시도한 객체의
object_name
특정 객체의
primary_key
보상 쓰기 오류의 경우
reason
입니다.
This information is the same information you can find in the App Services logs. The C++ SDK exposes this object on the client for convenience and debugging purposes.
다음은 쓰기 오류 보상에 대한 정보를 기록하는 방법의 예입니다.
auto info = receivedSyncError.compensating_writes_info(); for (auto &v : info) { std::cout << "A write was rejected with a compensating write error.\n"; std::cout << "An object of type " << v.object_name << "\n"; std::cout << "was rejected because " << v.reason << ".\n"; }
A write was rejected with a compensating write error. An object of type Item was rejected because write to ObjectID("6557ddb0bf050934870ca0f5") in table "Item" not allowed; object is outside of the current query view.
이 메시지의
Item
는 이 페이지의 객체 모델에서 사용된
Item
객체입니다.table "Item"
는 이 객체가 동기화될 Atlas collection을 나타냅니다.The reason
object is outside of the current query view
in this message is because the query subscription was set to require the object'scomplexity
property to be less than or equal to4
. The client attempted to write an object outside of this boundary.기본 키는 클라이언트가 쓰려고 시도한 특정 객체의
objectId
입니다.
성능 향상을 위한 그룹 쓰기
구독 세트에 대한 모든 쓰기 트랜잭션(write transaction)에는 성능이 소모됩니다. 세션 중에 영역 객체를 여러 번 업데이트해야 하는 경우 모든 변경이 완료될 때까지 편집한 객체를 메모리에 보관하는 것이 좋습니다. 이렇게 하면 모든 변경 사항 대신 완전하고 업데이트된 객체만 영역에 기록하므로 동기화 성능이 향상됩니다.