Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug on nested array linking objects update, (bug not happening on Realm 12.2.1) #6889

Open
angelos3lex opened this issue Sep 10, 2024 · 13 comments

Comments

@angelos3lex
Copy link

angelos3lex commented Sep 10, 2024

How frequently does the bug occur?

Always

Description

So we have to update to RN 0.74.5 (from 0.72) and we were on 12.2.1 realm version. Unfortunately, keeping same realm version, makes the app block on white screen, and through logcat I can see it's just saying " Building an exports object", but never proceeds.
So, I tried with 12.12.1 and 12.13.1 realm versions, which do not have this problem and everything goes smoothly, except from one use case.

I have a parent object Participant which has a subTable array of ParticipantMeta objects. ParticipantMeta are linking objects to the parent Participant.

When i create a Participant object with some metas, it's correctly added on the db.
When i query it, and re-add it using realm.create with Realm.UpdateMode.ALL, without any change at all, the meta list array becomes empty.

I have created a reproducible "minimal" repo that you can see on the logs the problem.

Update to RN 0.74 is crucial as we need to target api 34, and be able to release on playstore. Currently, this is our only blocker.

Stacktrace & log output

no crash. Only from the logs we can see that before updating the object we have full array and after the update, the array of the subTable is emptied.

Can you reproduce the bug?

Always

Reproduction Steps

https://github.com/angelos3lex/Realm-js-12.13.1-bug-repro
Check this repo ^
(Mainly App.tsx onPress of the button is where the whole logic begins.)

PS: Didn't test the reproducible example on iOS, but on my real case app it's also happening on iOS.

Version

12.12.1, 12.13.1

What services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

android, iOS, realm 12.12+, RN 0.74.5

Build environment

no debugger

Cocoapods version

didn't test the reproducible example on iOS, but on my real case app it's also happening on iOS.

Copy link

sync-by-unito bot commented Sep 10, 2024

➤ PM Bot commented:

Jira ticket: RJS-2903

@angelos3lex
Copy link
Author

Bonus on above reproducible repo:
If you change realm version to 12.2.1, you will be able to reproduce the freezing white screen after metro loads. By logcat I can just see the following logs:

JSRealm                 com.realmbrokenproject               V  setDefaultRealmFileDirectory
JSRealm                 com.realmbrokenproject               D  Absolute path: /data/data/com.realmbrokenproject/files
JSRealm                 com.realmbrokenproject               V  install
JSRealm                 com.realmbrokenproject               V  Building an exports object

@angelos3lex
Copy link
Author

Hello any news? I also updated the reproducible example by simplifying a lot some business logic so that it's only reproducible with simple Realm objects.

We are available for call or discussion on the subject. We need to release new version on store with targetApi34 and RN 0.74, but this is our only blocker at this point :(

@kraenhansen
Copy link
Member

kraenhansen commented Sep 23, 2024

@angelos3lex thanks for reporting the bug and for reaching out (again). I tried looking into your reproduction repo, but there's a lot going on in there. The use of DAOs are not a pattern we've been promoting as it makes the code more indirect and add a lot of complexity that I'm having a hard time wrapping my head around TBO.

As you've hinted, a bug like this is likely not platform specific and to simplify things, making it easier for us to reproduce the bug and add a test for it, it would be great if you could reproduce it using a simple npm init && npm install realm@12 and have a single file, executed via node which opens the realm without the use of class-based models (if that's possible) and simply performs a write and a read, reproducing the bug. Alternatively, you could spend a bit more time removing stuff which is not realm specific from your repo and add a few comments, making it easier for us to understand what's going on 👍

Thanks a lot in advance!

@angelos3lex
Copy link
Author

angelos3lex commented Sep 23, 2024

Hello @kraenhansen I just created a new branch with a really minimal example,
https://github.com/angelos3lex/Realm-js-12.13.1-bug-repro/tree/remove_RN_completely
There is no need for RN etc, just yarn and then yarn reproduce. All code exists in example.js file and i commented some parts.
Actual code starts at line 105, above are just class definitions

@kraenhansen
Copy link
Member

That's a huge improvement! Thanks - I'll get back as I get to investigate this further.

@angelos3lex
Copy link
Author

Hey @kraenhansen any advancements on the matter?

@angelos3lex
Copy link
Author

Another comment on the subject:
If i clone the queried object, and then save it, then the update works flawlessly, and the bug is not reproduced.
Which seems like the problem is only when updating a managed object directly, without cloning it first..

@angelos3lex
Copy link
Author

Hello @kraenhansen, kindly asking for any update on this. Or maybe we could help more from our side somehow?

@kraenhansen
Copy link
Member

kraenhansen commented Oct 10, 2024

Havn't had time to investigate this.

@angelos3lex
Copy link
Author

Hello guys, we are on 2.5 months now since the original post of the issue. I have added a very minimal repo to reproduce as asked. Anything I can do more on my side? This is huge block for us and unfortunately we cant update our apps..

@NickMoutsios
Copy link

Hello everyone, @kraenhansen, as @angelos3lex mentioned above, this is a huge blocker for us.
In order to upgrade react-native, we need to update realm. Our application is used by thousands of people and soon we are going to have a major problem.

Angelos provided a repo that reproduces the issues. Can you please look into it?
At the very least, to have an explanation why this is happening in the newest versions and not before.

I am not sure how else to approach this. If you are not the correct person, can you please point us to the right direction?

@kraenhansen
Copy link
Member

kraenhansen commented Nov 27, 2024

I am not sure how else to approach this. If you are not the correct person, can you please point us to the right direction?

In the current state of the project, I'd say the best option is to fork the repository and create a PR with the failing test somewhere around here:

it("can be updated", function (this: Mocha.Context & RealmContext) {
const _id = new Realm.BSON.ObjectId();
const john = this.realm.write(() => {
return this.realm.create(PersonWithId, {
_id,
name: "John Doe",
age: 42,
});
});
expect(john._id.equals(_id)).equals(true);
expect(john.name).equals("John Doe");
expect(john.age).equals(42);
this.realm.write(() => {
this.realm.create(PersonWithId, { _id, age: 43 }, Realm.UpdateMode.All);
});
expect(john._id.equals(_id)).equals(true);
expect(john.name).equals("John Doe");
expect(john.age).equals(43);
const update: Partial<PersonWithId> = {
_id,
name: "Mr. John Doe",
};
this.realm.write(() => {
this.realm.create(PersonWithId, update, Realm.UpdateMode.Modified);
});
expect(john._id.equals(_id)).equals(true);
expect(john.name).equals("Mr. John Doe");
expect(john.age).equals(43);
expect(() =>
this.realm.write(() => {
this.realm.create(PersonWithId, { _id, name: "John Doe", age: 42 }, Realm.UpdateMode.Never);
}),
).throws(
`Attempting to create an object of type '${PersonWithId.schema.name}' with an existing primary key value '${_id}'.`,
);
// Expect only one instance of 'PersonWithId' in db after all updates
const persons = this.realm.objects(PersonWithId);
expect(persons.length).equals(1);
});
describe("applying 'UpdateMode' recursively", () => {
let aliceId: Realm.BSON.ObjectId;
let bobId: Realm.BSON.ObjectId;
let maxId: Realm.BSON.ObjectId;
beforeEach(function (this: RealmContext & Mocha.Context) {
aliceId = new Realm.BSON.ObjectId();
bobId = new Realm.BSON.ObjectId();
maxId = new Realm.BSON.ObjectId();
// Create two mutual friends (Alice and Bob) and one
// that will be added as a friend later (Max).
this.realm.write(() => {
const alice = this.realm.create<ICollectionObject>(CollectionSchema.name, {
_id: aliceId,
name: "Alice",
});
const bob = this.realm.create<ICollectionObject>(CollectionSchema.name, {
_id: bobId,
name: "Bob",
});
const max = this.realm.create<ICollectionObject>(CollectionSchema.name, {
_id: maxId,
name: "Max",
});
// Make them mutual friends.
alice.list.push(bob);
bob.list.push(alice);
});
});
it("can be updated recursively in lists", function (this: Mocha.Context & RealmContext) {
const alice = this.realm.write(() => {
return this.realm.create(
CollectionSchema.name,
{
_id: aliceId,
name: "UpdatedAlice",
list: [
{
_id: bobId,
name: "UpdatedBob",
list: [
{
_id: maxId,
name: "UpdatedMax",
},
],
},
],
},
Realm.UpdateMode.All,
);
});
expect(alice._id.equals(aliceId)).to.be.true;
expect(alice.name).to.equal("UpdatedAlice");
const bob = alice.list[0];
expect(bob._id.equals(bobId)).to.be.true;
expect(bob.name).to.equal("UpdatedBob");
const max = bob.list[0];
expect(max._id.equals(maxId)).to.be.true;
expect(max.name).to.equal("UpdatedMax");
});
it("can be updated recursively in dictionaries", function (this: Mocha.Context & RealmContext) {
const alice = this.realm.write(() => {
return this.realm.create(
CollectionSchema.name,
{
_id: aliceId,
name: "UpdatedAlice",
dictionary: {
bob: {
_id: bobId,
name: "UpdatedBob",
dictionary: {
max: {
_id: maxId,
name: "UpdatedMax",
},
},
},
},
},
Realm.UpdateMode.All,
);
});
expect(alice._id.equals(aliceId)).to.be.true;
expect(alice.name).to.equal("UpdatedAlice");
const { bob } = alice.dictionary;
expect(bob._id.equals(bobId)).to.be.true;
expect(bob.name).to.equal("UpdatedBob");
const { max } = bob.dictionary;
expect(max._id.equals(maxId)).to.be.true;
expect(max.name).to.equal("UpdatedMax");
});
it("can be updated recursively in sets", function (this: Mocha.Context & RealmContext) {
const alice = this.realm.write(() => {
return this.realm.create(
CollectionSchema.name,
{
_id: aliceId,
name: "UpdatedAlice",
set: [
{
_id: bobId,
name: "UpdatedBob",
set: [
{
_id: maxId,
name: "UpdatedMax",
},
],
},
],
},
Realm.UpdateMode.All,
);
});
expect(alice._id.equals(aliceId)).to.be.true;
expect(alice.name).to.equal("UpdatedAlice");
const bob = alice.set[0];
expect(bob._id.equals(bobId)).to.be.true;
expect(bob.name).to.equal("UpdatedBob");
const max = bob.set[0];
expect(max._id.equals(maxId)).to.be.true;
expect(max.name).to.equal("UpdatedMax");
});
});
});
});

Regarding a fix, my current suspicion is that it's due to a change introduced to src/Object.ts in #6694, specifically the change from setting properties via the set trap result[propertyName] to a call directly to the set property helper (locally setProperty) here

setProperty(obj, propertyValue, created);

and here

setProperty(obj, extractedValue, created);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants