realm / realm-dart

Realm is a mobile database: a replacement for SQLite & ORMs.
Apache License 2.0
773 stars 86 forks source link

No change notification about a child RealmObject change. #1754

Closed serhii-k closed 3 months ago

serhii-k commented 4 months ago

What happened?

I've encountered this bug with a device synced realm but replicated it in a simple local-only example.

Let's say our schema contains three RealmModels:

In our realm, we have one instance of each:

Also, we are listening to the following changes:

Now, when we modify the value inside a.b:

Please see the full example to understand the problem more clearly.

Repro steps

1. schemas.dart

import 'package:realm/realm.dart';

part 'schemas.realm.dart';

@RealmModel()
class _Folder {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;

  late List<_A> list;
}

@RealmModel()
class _A {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;

  late _B? b;
}

@RealmModel()
class _B {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;

  late int value;
}

2. main.dart

// ignore_for_file: avoid_print

import 'package:buggy/schemas.dart';
import 'package:flutter/material.dart';
import 'package:realm/realm.dart';

Future<void> main() async {
  final config = Configuration.local([
    A.schema,
    B.schema,
    Folder.schema,
  ]);
  Realm.deleteRealm(config.path); // Just to clear all data
  final realm = Realm(config);

  realm.write(() {
    final a = realm.add(A(
      ObjectId.fromValues(0, 0, 0),
      b: B(ObjectId.fromValues(0, 0, 1), 0),
    ));
    realm.add(Folder(ObjectId.fromValues(0, 0, 2), list: [a]));
  });

  // Find the objects.
  final a = realm.find<A>(ObjectId.fromValues(0, 0, 0))!;
  final b = a.b!;
  final folder = realm.find<Folder>(ObjectId.fromValues(0, 0, 2))!;

  // Subscribe to the changes.
  folder.changes.listen((_) => print('Folder changed'));
  folder.list.changes.listen((_) => print('Folder.list changed'));
  a.changes.listen((_) => print('A changed'));

  await Future.delayed(const Duration(milliseconds: 1000));
  print('* b.value = 1');
  realm.write(() => b.value = 1);

  await Future.delayed(const Duration(milliseconds: 1000));
  print('* b.value = 2');
  realm.write(() => b.value = 2);

  await Future.delayed(const Duration(milliseconds: 1000));
  print('* Done.');
  print('---------');
  print('Problem');
  print('??? Where is "A changed" after its b was modified ???');
  print('??? If the Folder.list was triggered, why A was not ???');

  realm.close();

  runApp(const MaterialApp(home: Scaffold()));
}

The output

I/flutter (26316): Folder changed
I/flutter (26316): Folder.list changed
I/flutter (26316): A changed
I/flutter (26316): * b.value = 1
I/flutter (26316): Folder.list changed
I/flutter (26316): * b.value = 2
I/flutter (26316): Folder.list changed
I/flutter (26316): * Done.
I/flutter (26316): ---------
I/flutter (26316): Problem
I/flutter (26316): ??? Where is "A changed" after its b was modified ???
I/flutter (26316): ??? If the Folder.list was triggered, why A was not ???

Version

3.3.0

What Atlas Services are you using?

Atlas Device Sync

What type of application is this?

Flutter Application

Client OS and version

Android 14 (SDK 34)

Code snippets

No response

Stacktrace of the exception/crash you're getting

No response

Relevant log output

No response

sync-by-unito[bot] commented 4 months ago

➀ PM Bot commented:

Jira ticket: RDART-1073

serhii-k commented 4 months ago

It turns out that it was implemented this way to optimize performance.

And there is another method, changesFor([keyPaths]), which takes into account how deep you want to observe changes. So it's possible to specify the following keyPaths:

a.changesFor(['*', 'b.value']).listen((changes) => print('B changed ${changes.properties}'));
serhii-k commented 4 months ago

So, the only request about this ticket is to please mention the correct way to register a RealmObject Change Listener here:

https://www.mongodb.com/docs/atlas/device-sdks/sdk/flutter/realm-database/react-to-changes/#register-a-realmobject-change-listener

There is no any bug here. Realm is soooooo COOL! 🀩

nielsenko commented 3 months ago

@serhii-k Just back from vacation. Happy you worked it out πŸ‘