Performing destructive changes to synced Realm

What’s the intended way to perform a destructive change to a synced realm? It seemingly causes issues with data sync for me and looks as if I’m no longer able to sync certain objects, I can still see them in Mongodb however they won’t sync back to my client (realm-cocoa rc1).

1 Like

Glad you asked as we’ve just been through that process and have the same question. We were happily coding along when suddenly everything stopped working as it had and we’ve spent close to a week tracking down what we did to cause it.

The good news is for us at least, additive changes seem to go smoothly - either adding collection (class) or adding a property to an existing class.

From our perspective though, performing any other changes may result in sync problems:

  • if you remove a class from your app
  • change a property within a class (either remove or alter the name of the property)
  • drop a collection (class) in the MongoDB console

The result is that Realm sync goes offline and in many cases will refuse to sync your objects. You will see a bunch of recurring Translator Errors in the logs (View All Logs)

The only option is to use the console to Terminate Sync and re-set it up from scratch.

In some cases you may also get a BadChangeset error which may then be followed by Invalid Session errors. The resolution is the same. Terminate Sync and re set it up.

In some cases we’ve have to totally delete all local files as well which forces the server to re-sync the data (some data loss may occur doing this depending on when the list successful sync was).

For us at least, altering classes (collections), changing property names etc is just part of app development and occurs frequently during that process so I am not sure what the expectation is from the Realm folks on how to handle those types of changes as it seems that’s a lot of hoops to jump through.

Hopefully they will chime in and correct my observations and provide clear direction.

Thanks for the quick reply @Jay. The change that prompted me to ask the question was the removal of a field that was only ever added in the MongoDB Realm console and I’d never actually written as part of my local client’s schema.

Interestingly removing that field made me seemingly lose data in another collection on the client, digging into this deeper I noticed that some objects in that collection were not validating due to some missing required fields. I ran some commands in the mongo shell to fix up those items, deleted the app and reinstalled and they started to sync back to my client again, it seems like changing a field in the mongo admin console prompted a client reset and some fields with missing required values (likely left over from earlier development builds) failed to sync.

I’m still looking into this and making sure this was actually the case, would be great to know how to handle this better in the future.

Hey Y’all - thanks for the comments here - I will comment here that from the client, your mobile app class definitions, you should be able to:

  • Add a Class
  • Add a Field
  • Remove a Class
  • Remove a Field

Without needing to trigger a re-sync on the server. The client should be able to sync with a subset of classes that are defined on the cloud. If you define an extra class on the server side, then yes, you will need to add this class to to your server side schema - but it should not trigger a resync. If you are encountering different behavior with any of the above I have described then please let us know.

The above are interpreted as “additive” changes - A destructive change is defined as:

  • Changing the primary key
  • Changing a field from required to optional, or vice versa
  • Changing a field type but keeping the name the same
  • Dropping a collection in MongoDB that was already synced - this is because there is sync history metadata stored for this collection
  • Deleting Classes or Fields from your cloud sync schema

The above destructive changes will necessitate a full re-sync, ie. a termination of sync on the cloud and the re-enablement.

If you are looking to make a destructive change then you can do two things, both follow an “API versioning” methodology -

  • If you need to make a destructive change on class you can increment the version on the class. ie - Account vs AccountV2
  • If you need to make a destructive change on a field you can increment the version on the field. ie -
    name vs nameV2

In this way, both version will be able to co-exist out in the wild without the need of a re-sync. You can use triggers to copy data between them if necessary. I hope this helps, let me know if you have any questions

Best
Ian

5 Likes

Thank you @Ian_Ward that helps considerably. That detailed info would be awesome to add to the documentation.

To be clear, if there’s a destructive change, for example, changing a property from a String to an Int there are only two options and both involve API Versioning.

What if we just want to start from scratch with a Class but use the same name - like in an early development stage where there’s no data to be concerned with. Is it possible to terminate sync, drop the collection (class) in the console, update the class in code and then re-set up sync? Or is there metadata issues with that?

For us during early development and model planning, we are adding, changing and removing classes all the time and want to know the best process for that which won’t negatively impact the server.

1 Like

If you are just iterating on the Schema then you should be able to control all of that from the client code using Developer mode - that is what it is there for. You shouldn’t need to mess with the cloud sync schema at all - unless you are making breaking destructive changes as mentioned above, in which case, yes terminate and re-enable sync.

@Ian_Ward Thanks. The question was specifically about a destructive change as defined by your post

Perhaps I have overlooked a step so let me provide a simple use case.

Here’s the class

class TestObject: Object {
    @objc dynamic var _id: ObjectId = ObjectId.generate()
    @objc dynamic var _partitionKey: TaskProjectId = ""
    @objc dynamic var myProperty = ""

    override static func primaryKey() -> String? {
        return "_id"
    }
}

And we want to change myProperty to an Int - we do not care about loosing any existing data since we are just starting this project fresh. Here’s the updated class:

class TestObject: Object {
    @objc dynamic var _id: ObjectId = ObjectId.generate()
    @objc dynamic var _partitionKey: TaskProjectId = ""
    @objc dynamic var myProperty = 1234

    override static func primaryKey() -> String? {
        return "_id"
    }
}
  1. Quit the App, make the change to the class in XCode
  2. Delete all local files
  3. Terminate Sync in the console (Development Mode)
  4. Re-enable Sync in console (Development Mode)
  5. Run the app

The following changes cannot be made in additive-only schema mode:

  • Property ‘TestObject.myProperty’ has been changed from ‘string’ to ‘int’.

If the goal is to change myProperty from String to Int, what’s the process?

I am asking for it to be spelled out so we don’t break the server (again) and cause it to stop sync’ing (per the OP’s original question).

You also need to delete the old TestObject Schema from the cloud UI - after terminating sync and before re-enabling it.

Does that mean to go Realm->Select App->Left Column ‘Schema’ then the Schema Tab, then select the class and click Remove configuration

or

Does that that that mean drop the collection by going to Atlas->Collections and clicking the trash can icon next to the collection

Just trying to not break the server.

Realm App - Schema - Remove Schema

Great! Just tried it. For anyone else following along… The button is labeled REMOVE CONFIGURATION and then a popup window with REMOVE COLLECTION.

Seems to work but as it stated, it would not delete any data so in Atlas, the collection (Class) now has both Strings and Ints for the same property (based on my example above). Kinda unexpected.

However when the class is read in code into a Results object, it ignores the ‘old’ version and only reads the ‘new’ objects.

3 Likes

Yeah if you want to delete the data you will need to delete it in Atlas :slight_smile:

As a followup - while Remove Collection removes the schema and allows changes to the class, if you also want to totally delete ALL of the prior data, the Collection itself must be dropped as well as Ian mentioned.

That’s done in Atlas->Collections select the class and click the Trash icon - this should be done in conjunction with Remove Configuration.

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.