When writing data to a synced realm using Flexible Sync, you can use the same APIs as writing to a local realm. However, there are some differences in behavior to keep in mind as you develop your application. To learn more about reading and writing data to a realm, refer to Read & Write Data.
When you write to a synced realm, your write operations must match both of the following:
- La consulta de suscripción de sincronización.
Si su operación de guardar no coincide con la query en la suscripción, el guardar se revierte con un error no fatal de guardar compensatorio (ErrorCompensatingWrite).
Para obtener más información sobre cómo compensar errores de escritura y cómo evitarlos, consulte la Sección deescrituras compensatorias.
- The permissions in your App Services App.
If your try to write data that doesn't match the permissions expression, the write reverts with a non-fatal permission denied error. In the client, this shows as an error (ErrorCompensatingWrite). On the server, you can see more details about how the write was denied was by a write filter in the role.
To learn more about configuring permissions for your app, see Role-based Permissions and the Device Sync Permissions Guide in the App Services documentation.
Para obtener más información sobre los errores de denegación de permisos, errores de escritura compensatoria y otros tipos de errores de Device Sync, consulta Errores de sincronización en la documentación de App Services.
Determining What Data Syncs
The examples on this page use an Atlas App Services App with the following Device Sync configuration and a client app with the following Realm SDK data model and subscriptions.
Configuración de servicios de aplicaciones
Sync de dispositivo se configura con los siguientes campos consultables:
_id(siempre incluido)milesownerId
The App Services App has permissions configured to let users read and write only their own data:
{ "name": "owner-read-write", "apply_when": {}, "document_filters": { "read": { "ownerId": "%%user.id" }, "write": { "ownerId": "%%user.id" } }, "read": true, "write": true }
Client Data Model and Configuration
The examples on this page use the following schema:
() class _Car { ("_id") () late ObjectId id; // This is the queryable field late String ownerId; late String make; late String? model; late int? miles; }
Usando ese esquema, los ejemplos configuran el reino sincronizado para sincronizar objetos que coinciden con esta consulta de suscripción:
final app = App(AppConfiguration(APP_ID)); final user = await app.logIn(Credentials.anonymous()); final config = Configuration.flexibleSync(user, [Car.schema]); final realm = Realm(config); // Add subscriptions realm.subscriptions.update((mutableSubscriptions) { // Get Cars from Atlas that match the Realm Query Language query. // Uses the queryable field `miles`. // Query matches cars with less than 100 miles or `null` miles. final newCarQuery = realm.query<Car>("miles < 100 OR miles == \$0", [null]); mutableSubscriptions.add(newCarQuery, name: "new-car-subscription"); }); await realm.subscriptions.waitForSynchronization();
Write to a Synced Realm
Los guardados en realms de Flexible Sync pueden clasificarse generalmente en una de dos categorías:
Escrituras exitosas: El objeto escrito coincide tanto con la suscripción de la query como con los permisos del usuario. El objeto se escribe correctamente en el realm y se sincroniza correctamente con el backend de Servicios de aplicación y otros dispositivos.
Compensating writes: When the written object does not match the subscription query, or where the user does not have sufficient permissions to perform the write, Realm reverts the illegal write.
Successful Writes
Cuando la operación de escritura coincide tanto con los permisos de App Services como con la suscripción de Flexible Sync query en el cliente, el SDK de Realm Flutter puede guardar correctamente el objeto en el realm sincronizado. Este objeto se sincroniza con el backend de aplicación Services cuando el dispositivo tiene una conexión de red.
// Per the Device Sync permissions, users can only read and write data // where the `Car.ownerId` property matches their own user ID. final userId = user.id; realm.write(() { // WRITE SUCCEEDS // `newCar` is successfully written to the realm and synced to Atlas // because it's data matches the subscription query (miles < 100) // and it's `ownerId` field matches the user ID. final newCar = Car(ObjectId(), userId, 'Toyota', miles: 2); realm.add(newCar); });
Compensating Writes
In some cases, a write that initially appears to succeed is actually an illegal write. In these cases, the object writes to the database, but when the database syncs to the backend, Realm reverts the write in a non-fatal error operation called a compensating write. Compensating writes can occur when:
Las escrituras no coinciden con la suscripción de consulta: el objeto escrito coincide con los permisos del usuario, pero no coincide con la suscripción de consulta.
Guardados no coinciden con los permisos: El objeto guardado coincide con la suscripción de la query, pero no coincide con los permisos del usuario.
In more detail, when you write data that is outside the bounds of a query subscription or does not match the user's permissions, the following occurs:
Como el realm del cliente no tiene concepto de guardados "ilegales", el guardado tiene éxito inicialmente hasta que realm resuelve el conjunto de cambios con el backend de Servicios de aplicación.
Upon sync, the server applies the rules and permissions. The server determines that the user does not have authorization to perform the write.
The server sends a revert operation, called a "compensating write", back to the client.
The client's realm reverts the illegal write operation.
Cualquier guardado realizado del lado del cliente en un objeto determinado entre un guardado ilegal y el correspondiente guardado de compensación se perderá.
En la práctica, esto puede parecer que se escribe un objeto en el realm y luego desaparece después de que el servidor envía el guardado compensatorio de vuelta al cliente.
Para obtener más información sobre los errores de denegación de permisos, errores de escritura compensatoria y otros tipos de errores de Device Sync, consulta Errores de sincronización en la documentación de App Services.
The App Services logs contain more information about why a compensating write error occurs.
Operaciones de escritura que no coinciden con la suscripción de query
You can only write objects to a Flexible Sync realm if they match the subscription query. If you perform a write that does not match the subscription query, Realm initially writes the object, but then performs a compensating write. This is a non-fatal operation that reverts an illegal write that does not match the subscription query.
If you want to write an object that does not match the query subscription, open a different realm where the object matches the query subscription. Alternately, you could write the object to a local realm that does not enforce permissions or subscription queries.
Code Example
Given the configuration for the synced realm above, attempting to write this object does not match the query subscription:
final carId = ObjectId(); final ownerId = app.currentUser!.id; realm.write(() { // WRITE REVERTED BY QUERY SUBSCRIPTION COMPENSATING WRITE // `oldCar` is initially written to the realm, then later removed // in a compensating write when the server processes the write. // This is because the `miles` property of `oldCar` doesn't match // the subscription query, which is only for cars with less than 100 miles. final oldCar = Car(carId, ownerId, 'Honda', miles: 90000); realm.add(oldCar); }); // Let changes sync to and from server await realm.syncSession.waitForUpload(); await realm.syncSession.waitForDownload(); final noCar = realm.find<Car>(carId); // The Car is no longer in the realm because of // the compensating write from the server. expect(noCar, isNull);
Client Error
El mensaje de error en los registros del lado del cliente en este escenario es:
[INFO] Realm: Connection[1]: Session[1]: Received: ERROR "Client attempted a write that is outside of permissions or query filters; it has been reverted" (error_code=231, try_again=true, error_action=Warning) [INFO] Realm: Connection[1]: Session[1]: Reporting compensating write for client version 21 in server version 2877: Client attempted a write that is outside of permissions or query filters; it has been reverted [ERROR] Realm: SyncSessionError message: Client attempted a write that is outside of permissions or query filters; it has been reverted Logs: https://services.cloud.mongodb.com/groups/5f60207f14dfb25d23101102/apps/639340a757271cb5e3a0f0cf/logs?co_id=6424433efb0c6bbcc330347c category: SyncErrorCategory.session code: SyncSessionErrorCode.compensatingWrite isFatal: false
App Services Error
El mensaje de error en los registros de aplicación Services en este escenario es:
Error: Client attempted a write that is outside of permissions or query filters; it has been reverted (ProtocolErrorCode=231) Details: { "Car": { "6424433fd4d9f52ee93ad590": "write to \"6424433fd4d9f52ee93ad590\" in table \"Car\" not allowed; object is outside of the current query view" } }
Guardados que no coinciden con los permisos
Attempting to write to the client can also trigger a compensating write error when the object does not match the user's server-side write permissions.
En el cliente, este tipo de guardar se comporta igual que un guardar que no coincide con la suscripción de query. En la práctica, esto podría implicar que el guardado es exitoso, pero luego el objeto "desaparece" cuando Realm se sincroniza con el backend de aplicación Services y realiza el guardado compensatorio.
Code Example
Dado los permisos en la configuración Device Sync detallados anteriormente, intentar escribir un objeto donde la propiedad ownerId no coincida con la user.id del usuario que ha iniciado sesión no es una escritura legal:
final carId = 'someOtherId'; realm.write(() { // WRITE REVERTED BY PERMISSION ERROR // `otherUsersCar` is initially written to the realm, then removed upon synchronization // because it's `ownerId` property doesn't match the user ID of the user // making the request. final otherUsersCar = Car(ObjectId(), carId, 'Ford'); realm.add(otherUsersCar); }); // sync changes await realm.syncSession.waitForUpload(); await realm.syncSession.waitForDownload(); final noCar = realm.find<Car>(carId); // The Car is no longer in the realm because of // the compensating write from the server. expect(noCar, isNull);
Client Error
The client error in this scenario is the same as when you attempt to write an object that is outside the App Services permissions:
[INFO] Realm: Connection[1]: Session[1]: Received: ERROR "Client attempted a write that is outside of permissions or query filters; it has been reverted" (error_code=231, try_again=true, error_action=Warning) [INFO] Realm: Connection[1]: Session[1]: Reporting compensating write for client version 25 in server version 2879: Client attempted a write that is outside of permissions or query filters; it has been reverted [ERROR] Realm: SyncSessionError message: Client attempted a write that is outside of permissions or query filters; it has been reverted Logs: https://services.cloud.mongodb.com/groups/5f60207f14dfb25d23101102/apps/639340a757271cb5e3a0f0cf/logs?co_id=6424433efb0c6bbcc330347c category: SyncErrorCategory.session code: SyncSessionErrorCode.compensatingWrite isFatal: false
App Services Error
The error message in the App Services logs provides some additional information to help you determine that it is a permissions issue, and not a query subscription issue. In this example, the error message shows that the the object does not match the user's role:
Error: Client attempted a write that is outside of permissions or query filters; it has been reverted (ProtocolErrorCode=231) Details: { "Car": { "6424433fd4d9f52ee93ad591": "write to \"6424433fd4d9f52ee93ad591\" in table \"Car\" was denied by write filter in role \"owner-read-write\"" } }