realm-swift icon indicating copy to clipboard operation
realm-swift copied to clipboard

LinkingObject property doesn't notify correctly

Open ejm01 opened this issue 4 years ago • 6 comments

How frequently does the bug occur?

All the time

Description

~~It appears that a linkingObject property doesn't notify properly when the origin property is on the same class~~ Write transactions where only a backlink property is changed does not notify.

This happens even without a keyPath parameter.

Screen Shot 2021-10-20 at 4 25 02 PM

Expected behavior is to receive a notification on a linkingObject property. Similar to how testBacklinkPropertyKeyPathNotifications4 behaves.

Stacktrace & log output

No response

Can you reproduce the bug?

Yes, always

Reproduction Steps

Example reproduction code

Version

10.17

What SDK flavour are you using?

Local Database only

Are you using encryption?

No, not using encryption

Platform OS and version(s)

All

Build environment

Xcode version: 13.0 Dependency manager and version: SPM

ejm01 avatar Oct 20 '21 20:10 ejm01

The issue is not strictly related to origin table and target table having the same table key. The below passes:

    func testBacklinkPropertyKeyPathToSameClass() {
        var parent: ModernAllTypesObject!
        var child: ModernAllTypesObject!

        let realm = try! Realm()
        try! realm.write {
            child = realm.create(ModernAllTypesObject.self)
        }
        let ex1 = expectation(description: "linking object notification")
        let token1 = child.observe(keyPaths: ["linkingObjects"]) { _ in
            ex1.fulfill()
        }

        try! realm.write {
            parent = realm.create(ModernAllTypesObject.self)
            parent.objectCol = child
        }

        waitForExpectations(timeout: 2, handler: nil)
        token1.invalidate()
    }

But this does not:

    func testBacklinkPropertyKeyPathToSameClass() {
        var parent: ModernAllTypesObject!
        var child: ModernAllTypesObject!

        let realm = try! Realm()
        try! realm.write {
            child = realm.create(ModernAllTypesObject.self)
        }
        let ex1 = expectation(description: "linking object notification")
        let token1 = child.observe(keyPaths: ["linkingObjects"]) { _ in
            ex1.fulfill()
        }
        try! realm.write {
            parent = realm.create(ModernAllTypesObject.self)
        }
        try! realm.write {
            parent.objectCol = child
        }

        waitForExpectations(timeout: 2, handler: nil)
        token1.invalidate()
    }

backlink_count is 0, then 1 (How I think it's expected). Though I'm still trying to figure out why the column isn't making it to change callback.

ejm01 avatar Oct 21 '21 20:10 ejm01

So the fact both the parent and child objects are of the same class is irrelevant. This is reproducible when target and origin properties are on different classes.

The actual issue is when reassigning the "objectCol", and by extension changing the "linkingObjects", is in a write transaction by itself. A transaction like:

try! realm.write {
            parent.objectCol = child
}

would create a transaction log that's parsed with a "instr_set" case. The instr_set case only adds modifications to the changeset.

BUT linkingObject changes are delivered as insertions. So the insertion is never added to the callback.

In a write transaction like:

try! realm.write {
            parent = realm.create(ModernAllTypesObject.self)
            parent.objectCol = child
}

the transaction will also have a "instr_createObject" case because of the new object. Both the insertion for the new object, and the insertion representing the change to the "linkingObject" property are then added to the changeset.

ejm01 avatar Oct 22 '21 19:10 ejm01

This can be reproduce in core alone. So I'll get that and open an issue in core. Because I'm not sure if the solution is changing the transact_log or how we classify changes to a linkingObject property.

ejm01 avatar Oct 22 '21 19:10 ejm01

I've attached an example that uses valuePublisher that shows a short example of this issue happening. Hopefully it helps.

Example.zip

connor-ricks avatar Jan 16 '22 17:01 connor-ricks

Any updates on this issue?

connor-ricks avatar Feb 17 '22 04:02 connor-ricks

Would be nice to have a scary disclaimer about this in the docs for LinkingObject if this is going to remain an issue

aehlke avatar Oct 01 '22 00:10 aehlke