Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /
Device Sync - Flutter SDK

Guardar datos en un Realm sincronizado - Flutter SDK

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.

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.

Sync de dispositivo se configura con los siguientes campos consultables:

  • _id (siempre incluido)

  • miles

  • ownerId

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
}

The examples on this page use the following schema:

@RealmModel()
class _Car {
@MapTo("_id")
@PrimaryKey()
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();

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.

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);
});

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:

  1. 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.

  2. Upon sync, the server applies the rules and permissions. The server determines that the user does not have authorization to perform the write.

  3. The server sends a revert operation, called a "compensating write", back to the client.

  4. 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.

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.

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);

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

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"
}
}

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.

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);

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

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\""
}
}

Volver

Gestionar suscripciones

En esta página