Gravar dados em um Realm sincronizado - Kotlin 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 que ao gravar em um Realm local. 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:
class Item : RealmObject { var _id: ObjectId = ObjectId() var ownerId: String = "" var itemName: String = "" var complexity: Int = 0 }
Configuração do App Services
Com base no modelo de objeto de exemplo acima, o Device Sync é configurado com os seguintes campos 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.id
do usuário conectado não pode ser sincronizado com esse Realm.
Modelo de dados Realm e configuração do cliente
Usando o modelo de objetos, os exemplos configuram o Realm sincronizado para sincronizar objetos correspondentes a esta query de assinatura :
val app = App.create(FLEXIBLE_APP_ID) val user = app.login(credentials) val flexSyncConfig = SyncConfiguration.Builder(user, setOf(Item::class)) // Add subscription .initialSubscriptions { realm -> add( // Get Items from Atlas that match the Realm Query Language query. // Uses the queryable field `complexity`. // Query matches objects with complexity less than or equal to 4. realm.query<Item>("complexity <= 4"), "simple-items" ) } .build() val syncRealm = Realm.open(flexSyncConfig) syncRealm.subscriptions.waitForSynchronization() Log.v("Successfully opened realm: ${syncRealm.configuration}")
Qualquer objeto na coleção Atlas em que o valor da propriedade complexity
for maior que 4
não poderá ser sincronizado com esse domínio.
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 Kotlin SDK pode gravar com êxito o objeto 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. val userId = user.id val newItem = Item().apply { ownerId = userId itemName = "This item meets sync criteria" complexity = 3 } syncRealm.write { // `newItem` is successfully written to the realm and synced to Atlas // because its data matches the subscription query (complexity <= 4) // and its `ownerId` field matches the user ID. copyToRealm(newItem) }
Compensação de escritas
Quando a gravação não corresponde à assinatura da query ou às permissões do usuário, o Realm reverte a gravação e lança uma CompensatingWriteException.
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 ocorre, você pode consultar os registros doAtlas App Services ou usar o objeto CompensatingWriteInfo no cliente para obter informações adicionais sobre o erro.
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. val itemTooComplex = Item().apply { ownerId = user.id itemName = "This item is too complex" complexity = 7 } syncRealm.write { copyToRealm(itemTooComplex) }
[Session][CompensatingWrite(231)] Client attempted a write that is disallowed by permissions, or modifies an object outside the current query, and the server undid the change.
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": { "63bdfc40f16be7b1e8c7e4b7": "write to \"63bdfc40f16be7b1e8c7e4b7\" 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.id
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. val itemWithWrongOwner = Item().apply { ownerId = "not the current user" itemName = "A simple item" complexity = 1 } syncRealm.write { copyToRealm(itemWithWrongOwner) }
[Session][CompensatingWrite(231)] Client attempted a write that is disallowed by permissions, or modifies an object outside the current query, and the server undid the change.
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": { "63bdfc40f16be7b1e8c7e4b7": "write to \"63bdfc40f16be7b1e8c7e4b7\" in table \"Item\" was denied by write filter in role \"readOwnWriteOwn\"" } }
Informações de erro de gravação compensatórias
Novidades na versão 1.9.0.
Você pode obter informações adicionais no cliente sobre o motivo pelo qual ocorre uma gravação compensatória usando o objeto CompensatingWriteInfo , que fornece:
O
objectType
do objeto que o cliente tentou escreverO
primaryKey
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 do App Services. O Kotlin 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:
val syncErrorHandler = SyncSession.ErrorHandler { session, error -> runBlocking { if (error is CompensatingWriteException) { error.writes.forEach { writeInfo -> val errorMessage = """ A write was rejected with a compensating write error The write to object type: ${writeInfo.objectType} With primary key of: ${writeInfo.primaryKey} Was rejected because: ${writeInfo.reason} """.trimIndent() Log.e(errorMessage) } } } }
A write was rejected with a compensating write error The write to object type: Item With primary key of: RealmAny{type=OBJECT_ID, value=BsonObjectId(649f2c38835cc0346b861b74)} Was rejected because: write to "649f2c38835cc0346b861b74" 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.A chave primária é o
objectId
do objeto específico que o cliente tentou escrever.O
table "Item"
refere-se à collection Atlas onde este objeto sincronizaria.A razão
object is outside of the current query view
neste exemplo é porque a assinatura de query foi definida para exigir que a propriedadecomplexity
do objeto seja menor ou igual a4
, e o cliente tentou gravar um objeto fora desse limite.
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.