Gravar dados em um Realm sincronizado - C++ SDK
Nesta página
- Determinar quais dados são sincronizados
- Configuração do App Services
- Modelo de dados Realm e configuração do cliente
- Escreva para um domínio sincronizado
- Gravações bem-sucedidas
- Compensação de escritas
- A gravação não corresponde à assinatura da query
- A gravação não corresponde às permissões
- Informações de erro de gravação compensatórias
- Escritas em grupo para melhorar o desempenho
Ao gravar dados em um Realm sincronizado usando o Flexible Sync, você pode usar as mesmas APIs ao executar operações CRUD em um Realm não sincronizado. No entanto, há algumas diferenças de comportamento a serem lembradas à medida que você desenvolve seu aplicação.
Quando você escreve em um Realm sincronizado, suas operações de escrita devem corresponder aos dois itens a seguir:
A query de assinatura de sincronização
As permissões em seu App Services App
Se você tentar gravar dados que não correspondam à assinatura da query ou às permissões do usuário, o Realm reverterá a gravação com uma operação de erro não fatal chamada gravação compensatória.
Para saber mais sobre como configurar permissões para seu aplicativo, consulte Permissões baseadas em funções e o Guia de permissões deDevice Sync na documentação do Atlas App Services .
Para saber mais sobre erros de permissão negada, erros de gravação compensatórios e outros tipos de erro do Device Sync , consulte Erros de Sincronização na documentação do Atlas App Services .
Determinar quais dados são sincronizados
Os dados que você pode gravar em um Realm sincronizado são determinados pelo seguinte:
Sua configuração do Realm Mobile Sync
Permissões em sua aplicação
A query de assinatura do Flexible Sync usada quando você abre o domínio
Os exemplos nesta página usam um Atlas App Services App com a seguinte configuração Realm Mobile Sync e um aplicativo cliente com o seguinte Modelo de dados Realm e assinaturas do Realm SDK.
Neste exemplo, o aplicativo cliente usa o seguinte modelo de objeto:
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)
Configuração do App Services
Com base no Realm Object Model de exemplo acima, o Realm Mobile Sync é configurado com os seguintes campo de query:
_id
(Sempre incluído)complexity
ownerId
O App Services App tem permissões configuradas para permitir que os usuários leiam e escrevam somente seus próprios dados:
{ "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 } ] }
Qualquer objeto na collection Atlas em que o ownerId
não corresponda ao user.identifier()
do usuário conectado não pode ser sincronizado com esse Realm.
Modelo de dados Realm e configuração do cliente
Os exemplos nesta página usam a seguinte configuração de domínio sincronizada com esta query de sincronização e modelo de objeto:
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)
Com essa consulta de sincronização, qualquer objeto na coleção Atlas em que o valor da propriedade complexity
for maior que 4
não poderá ser sincronizado com essa região.
Escreva para um domínio sincronizado
As gravações em domínios do Flexible Sync podem se enquadrar em uma das duas categorias, dependendo se a gravação corresponde às permissões e à query de assinatura do Flexible Sync:
Gravações bem-sucedidas: o objeto gravado corresponde à assinatura da query e às permissões do usuário. O objeto é gravado com êxito no Realm e sincroniza com êxito com o backend do App Services e outros dispositivos.
Escritas compensatórias: o objeto gravado não corresponde à query de assinatura ou o usuário não tem permissões suficientes para realizar a gravação. O Realm reverte a escrita ilegal com uma operação de escrita compensatória.
Dica
Se você quiser gravar um objeto que não corresponde à query, poderá abrir um Realm diferente onde o objeto corresponde à query. Como alternativa, você pode gravar o objeto em um Realm não sincronizado que não impõe permissões ou query de assinatura.
Gravações bem-sucedidas
Quando a gravação corresponde às permissões do usuário e à assinatura da query no cliente, o Realm C++ SDK pode gravar o objeto com êxito no domínio sincronizado. Este objeto é sincronizado com o backend do App Services quando o dispositivo tem uma conexão de rede.
// 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)); });
Compensação de escritas
Quando a escrita não corresponde à assinatura da query ou às permissões do usuário, o Realm reverte a escrita e fornece uma array de objetos compensating_write_error_info .
Em mais detalhes, quando você grava dados que estão fora dos limites de uma assinatura de query ou não correspondem às permissões do usuário, ocorre o seguinte:
Como o domínio do cliente não tem nenhum conceito de escrita "ilegal", a escrita inicialmente é bem-sucedida até que o Realm resolva o changeset com o backend do App Services.
Após a sincronização, o servidor aplica as regras e permissões. O servidor determina que o usuário não tem autorização para realizar a gravação.
O servidor envia uma operação de reversão, chamada de "escrita compensatória", de volta ao cliente.
O Realm do cliente reverte a operação de escrita ilegal.
Qualquer escrita do lado do cliente em um determinado objeto entre uma escrita ilegal nesse objeto e a escrita compensatória correspondente será perdida. Na prática, isso pode parecer que a gravação foi bem-sucedida, mas o objeto "desaparece" quando o Realm sincroniza com o backend do App Services e executa a gravação compensatória.
Quando isso ocorrer, você pode consultar os registros doAtlas App Services ou usar a função compensating_writes_info()
no cliente para obter informações adicionais sobre o erro. Para obter mais informações, consulte a seção Informações de erro de gravação compensatória nesta página.
A gravação não corresponde à assinatura da query
Dada a configuração do domínio Flexible Sync detalhada acima, a tentativa de gravar esse objeto resulta em um erro de gravação compensatório porque o objeto não corresponde à assinatura da query:
// 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)
Você verá a seguinte mensagem de erro nos registros do 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" } }
A gravação não corresponde às permissões
Dadas as permissões na configuração do Device Sync detalhadas acima, a tentativa de gravar esse objeto resulta em um erro de gravação compensatório porque a propriedade ownerId
não corresponde ao user.identifier()
do usuário conectado:
// 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)
Você verá a seguinte mensagem de erro nos registros do 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\"" } }
Informações de erro de gravação compensatórias
Você pode obter informações adicionais no cliente sobre o motivo pelo qual ocorre uma gravação compensatória usando a função compensating_writes_info () , que fornece uma array de estruturas compensating_write_error_info
que contêm:
O
object_name
do objeto que o cliente tentou escreverO
primary_key
do objeto específicoO
reason
para o erro de gravação compensatória
Essas informações são as mesmas que você pode encontrar nos registros doAtlas App Services . O C++ SDK expõe esse objeto no cliente para conveniência e fins de depuração.
Veja a seguir um exemplo de como você pode registrar informações sobre a compensação de erros de gravação:
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.
O
Item
nesta mensagem é o objetoItem
usado no modelo de objeto nesta página.O
table "Item"
refere-se à collection Atlas onde este objeto sincronizaria.O motivo
object is outside of the current query view
nessa mensagem é porque a assinatura da query foi definida para exigir que a propriedadecomplexity
do objeto seja menor ou igual a4
. O cliente tentou gravar um objeto fora deste limite.A chave primária é o
objectId
do objeto específico que o cliente tentou escrever.
Escritas em grupo para melhorar o desempenho
Cada transação de escrita para um conjunto de assinaturas tem um custo de desempenho. Se você precisar fazer várias atualizações em um objeto do Realm durante uma sessão, considere manter objetos editados na memória até que todas as alterações sejam concluídas. Isso melhora o desempenho da sincronização, gravando apenas o objeto completo e atualizado em seu domínio, em vez de cada alteração.