Lists and Flexible Sync Permissions

Hello community!

I’ve been working on a project where I have a realm model to keep track of every user’s last visit to a specific place-object. Here’s a simple description of my classes:

class Place: Object {
    @Persisted(primaryKey: true) public var _id = UUID().uuidString
    @Persisted public var ownerId: String
    @Persisted public var lastVisits: List<LastVisit> = .init()
}

class LastVisit: Object {
    @Persisted(primaryKey: true) public var _id = UUID().uuidString
    @Persisted public var ownerId: String
    @Persisted public var date = Date()
}

I set up two sync rules:

  1. For the Place Object: Everyone can read and write the Place-Object.
  2. For the LastVisit Object: Only the “owner” of a LastVisit can read and write his LastVisit Data.

The corresponding roles look like this:

"roles": [
    {
        "name": "Place",
        "apply_when": {},
        "document_filters": { "read": true, "write": true },
        "read": true, "write": true, "insert": true, "delete": true, "search": true
    },
    {
        "name": "LastVisit",
        "apply_when": {},
        "document_filters": { "read": { "owner_id": "%%user.id" }, "write": { "owner_id": "%%user.id" } },
        "read": true, "write": true, "insert": true, "delete": true, "search": true
    }
]

However, I’ve run into a rather puzzling issue:
I have a first client and user that adds a first LastVisit to the Place-Object.
When a second user reads the Place-Object, the lastVisit list is empty, which is expected. But, when this client appends a new LastVisit Object to the List, it doesn’t append an object; instead, it overrides the whole List! This leaves me with one LastVisit-Object on the server-side collection, and the first client and user have an empty list.

What I would expect is that the server has all objects of all users stored and not that the Client overrides the whole List.

Is this flexible sync and list behavior a bug or intended? If anyone has any insights or has faced a similar issue, please let me know how you resolved it or if there are any workarounds.

Hello @Dan_Ivan ,

Wanted to confirm a few things:

Did you setup two rules or two roles? To be clear, a “rule” contains “roles” and rules apply at the MongoDB collection level – there’s also the concept of a “default rule”, which applies to any collection that doesn’t have a rule defined (you can tell if a rule is defined for a collection in the Rules page in the UI if you see that the name of the collection is not greyed out). Based on your description, I think that for the collection corresponding to Place you want to have a collection rule with a single read-all/write-all role. And then for the collection corresponding to LastVisit, there will be a collection rule with the “LastVisit” role that you’ve specified above.

Do you mind checking whether the first client is running into a compensating write error when it attempts to append to the lastVisits variable (you can check for this by examining the logs for the client’s session, an ErrorCompensatingWrite will log entry will show up)? The TLDR is that if there is a permissions violation, then a compensating write will be issued on the server, which effectively undos the illegal write.

If you want, you can also link your app URL here (looks like https://realm.mongodb.com/groups/<groupID>/apps/<appID>). I can then poke around to see if there’s anything suspicious going on.

Regards,
Jonathan

In the project I am currently working on and where we have this use case, the following error occurs on the server:

OtherSessionError:
Error:

Failed to apply received changeset: ArrayInsert: Invalid prior_size (list size = 1, prior_size = 0) (instruction target: CBProject[“6485DBFE-2F4F-41C1-A103-83B26B561DB8”].metadata[0], version: 1734, last_integrated_remote_version: 42, origin_file_ident: 316, timestamp: 271621410470). Please contact support. (ProtocolErrorCode=201)

In the logic of the example at the beginning, Location here would be CBProject and LastVisit would be be saved in the metadata array…

We have two roles set - both as described above.

If it helps, I’ll set up an example project with the simplified use case I mentioned at the beginning!