realm-js
realm-js copied to clipboard
Realm with sync enabled CRASHES the whole app when "path" is specified
How frequently does the bug occur?
All the time
Description
I created a demo project that crashes after login immediatelly when in realm configuration we add path variable. It seems like unintended behaviour. When removing path no crash appears.
https://github.com/boltss/realm-sync-demo demo project. That crashes when path is specified.
In our case having 2 realms side by side is mandatory. And to separate them we need to specify path to one.
Stacktrace & log output
ERROR [Error: Unable to open realm: Incompatible histories. Expected an empty or synced Realm, but found history type 2, top ref 344 Path: /Users/username/Library/Developer/CoreSimulator/Devices/A1DFCA63-1C6D-4666-AA79-024DB7533A92/data/Containers/Data/Application/67EA29FA-8BC7-470C-8B4D-950175BF4B50/Documents/sync.realm
Exception backtrace:
0 RealmSyncDemo 0x00000001032b655c _ZN5realm21IncompatibleHistoriesC1ERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_ + 84
1 RealmSyncDemo 0x00000001032b54f4 _ZN5realm2DB4openERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEbNS_9DBOptionsE + 6728
2 RealmSyncDemo 0x00000001032b704c _ZN5realm2DB4openERNS_11ReplicationERKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS_9DBOptionsE + 124
3 RealmSyncDemo 0x00000001032bd868 _ZN5realm2DB6createENSt3__110unique_ptrINS_11ReplicationENS1_14default_deleteIS3_EEEERKNS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_9DBOptionsE + 156
4 RealmSyncDemo 0x00000001030bc618 _ZN5realm5_impl16RealmCoordinator7open_dbEv + 1212
5 RealmSyncDemo 0x00000001030bbe9c _ZN5realm5_impl16RealmCoordinator19create_sync_sessionEv + 36
6 RealmSyncDemo 0x00000001030be614 _ZN5realm5_impl16RealmCoordinator22get_synchronized_realmENS_11RealmConfigE + 80
7 RealmSyncDemo 0x000000010311a094 _ZN5realm5Realm22get_synchronized_realmENS_11RealmConfigE + 108
8 RealmSyncDemo 0x00000001030266ec _ZN5realm2js10RealmClassINS_3jsc5TypesEE16async_open_realmEPK15OpaqueJSContextP13OpaqueJSValueRNS0_9ArgumentsIS3_EERNS0_11ReturnValueIS3_EE + 772
9 RealmSyncDemo 0x000000010301be00 _ZN5realm2js4wrapIXadL_ZNS0_10RealmClassINS_3jsc5TypesEE16async_open_realmEPK15OpaqueJSContextP13OpaqueJSValueRNS0_9ArgumentsIS4_EERNS0_11ReturnValueIS4_EEEEEEPKS9_S8_SA_SA_mPKSI_PSI_ + 56
10 JavaScriptCore 0x000000019190e9c4 _ZN3JSCL22callJSCallbackFunctionEPNS_14JSGlobalObjectEPNS_9CallFrameE + 524
11 ??? 0x0000000134205664 0x0 + 5169501796
12 JavaScriptCore 0x0000000191882320 llint_entry + 151568
13 JavaScriptCore 0x0000000191882288 llint_entry + 151416
14 JavaScriptCore 0x0000000191882288 llint_entry + 151416
15 JavaScriptCore 0x0000000191882288 llint_entry + 151416
16 JavaScriptCore 0x0000000191883038 llint_entry + 154920
17 JavaScriptCore 0x0000000191882320 llint_entry + 151568
18 JavaScriptCore 0x0000000191882288 llint_entry + 151416
19 JavaScriptCore 0x0000000191882288 llint_entry + 151416
20 JavaScriptCore 0x0000000191882288 llint_entry + 151416
21 JavaScriptCore 0x0000000191882288 llint_entry + 151416
22 JavaScriptCore 0x0000000191882288 llint_entry + 151416
23 JavaScriptCore 0x0000000191882288 llint_entry + 151416
24 JavaScriptCore 0x0000000191882288 llint_entry + 151416
25 JavaScriptCore 0x0000000191882288 llint_entry + 151416
26 JavaScriptCore 0x0000000191882fa0 llint_entry + 154768
27 JavaScriptCore 0x0000000191882288 llint_entry + 151416
28 JavaScriptCore 0x0000000191882288 llint_entry + 151416
29 JavaScriptCore 0x0000000191882320 llint_entry + 151568
30 JavaScriptCore 0x0000000191882320 llint_entry + 151568
31 JavaScriptCore 0x0000000191882288 llint_entry + 151416
32 JavaScriptCore 0x0000000191882288 llint_entry + 151416
33 JavaScriptCore 0x0000000191882288 llint_entry + 151416
34 JavaScriptCore 0x0000000191882288 llint_entry + 151416
35 JavaScriptCore 0x0000000191882288 llint_entry + 151416
36 JavaScriptCore 0x0000000191882288 llint_entry + 151416
37 JavaScriptCore 0x0000000191882288 llint_entry + 151416
38 JavaScriptCore 0x0000000191882288 llint_entry + 151416
39 JavaScriptCore 0x0000000191882288 llint_entry + 151416
40 JavaScriptCore 0x0000000191882320 llint_entry + 151568
41 JavaScriptCore 0x0000000191882320 llint_entry + 151568
42 JavaScriptCore 0x00000001918835ec llint_entry + 156380
43 JavaScriptCore 0x0000000191882288 llint_entry + 151416
44 JavaScriptCore 0x0000000191882288 llint_entry + 151416
45 JavaScriptCore 0x00000001918835ec llint_entry + 156380
46 JavaScriptCore 0x0000000191882288 llint_entry + 151416
47 JavaScriptCore 0x0000000191882288 llint_entry + 151416
48 JavaScriptCore 0x0000000191882288 llint_entry + 151416
49 JavaScriptCore 0x000000019185d064 vmEntryToJavaScript + 264
50 JavaScriptCore 0x0000000192000e98 _ZN3JSC11Interpreter11executeCallEPNS_14JSGlobalObjectEPNS_8JSObjectERKNS_8CallDataENS_7JSValueERKNS_7ArgListE + 640
51 JavaScriptCore 0x0000000192329110 _ZN3JSC27boundThisNoArgsFunctionCallEPNS_14JSGlobalObjectEPNS_9CallFrameE + 744
52 JavaScriptCore 0x000000019185d210 vmEntryToNative + 280
53 JavaScriptCore 0x0000000192000ec0 _ZN3JSC11Interpreter11executeCallEPNS_14JSGlobalObjectEPNS_8JSObjectERKNS_8CallDataENS_7JSValueERKNS_7ArgListE + 680
54 JavaScriptCore 0x0000000191927b0c JSObjectCallAsFunction + 760
55 RealmSyncDemo 0x0000000102eb14b4 _ZN8facebook3jsc10JSCRuntime4callERKNS_3jsi8FunctionERKNS2_5ValueEPS7_m + 268
56 RealmSyncDemo 0x0000000102ec0820 _ZNK8facebook3jsi8Function4callERNS0_7RuntimeEPKNS0_5ValueEm + 100
57 RealmSyncDemo 0x0000000102ec075c _ZNK8facebook3jsi8Function4callERNS0_7RuntimeESt16initializer_listINS0_5ValueEE + 112
58 RealmSyncDemo 0x0000000102ee2800 _ZNK8facebook3jsi8Function4callIJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESB_NS0_5ValueEEEESC_RNS0_7RuntimeEDpOT_ + 284
59 RealmSyncDemo 0x0000000102ee2674 _ZZN8facebook5react11JSIExecutor12callFunctionERKNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEESA_RKN5folly7dynamicEENK3$_5clEv + 140
60 RealmSyncDemo 0x0000000102ee25c8 _ZNSt3__1L8__invokeIRZN8facebook5react11JSIExecutor12callFunctionERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESB_RKN5folly7dynamicEE3$_5JEEEDTclscT_fp_spscT0_fp0_EEOSI_DpOSJ_ + 24
61 RealmSyncDemo 0x0000000102ee2580 _ZNSt3__128__invoke_void_return_wrapperIvLb1EE6__callIJRZN8facebook5react11JSIExecutor12callFunctionERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESD_RKN5folly7dynamicEE3$_5EEEvDpOT_ + 28
62 RealmSyncDemo 0x0000000102ee2558 _ZNSt3__110__function12__alloc_funcIZN8facebook5react11JSIExecutor12callFunctionERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESC_RKN5folly7dynamicEE3$_5NS8_ISH_EEFvvEEclEv + 28
63 RealmSyncDemo 0x0000000102ee12f8 _ZNSt3__110__function6__funcIZN8facebook5react11JSIExecutor12callFunctionERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESC_RKN5folly7dynamicEE3$_5NS8_ISH_EEFvvEEclEv + 28
64 RealmSyncDemo 0x0000000102f217e8 _ZNKSt3__110__function12__value_funcIFvvEEclEv + 60
65 RealmSyncDemo 0x0000000102f216f8 _ZNKSt3__18functionIFvvEEclEv + 24
66 RealmSyncDemo 0x0000000102c53620 _ZN8facebook5react11JSIExecutor21defaultTimeoutInvokerERKNSt3__18functionIFvvEEENS3_IFNS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEvEEE + 24
67 RealmSyncDemo 0x0000000102c56584 _ZNSt3__1L8__invokeIRPFvRKNS_8functionIFvvEEENS1_IFNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEvEEEEJS5_SD_EEEDTclscT_fp_spscT0_fp0_EEOSH_DpOSI_ + 92
68 RealmSyncDemo 0x0000000102c564f8 _ZNSt3__128__invoke_void_return_wrapperIvLb1EE6__callIJRPFvRKNS_8functionIFvvEEENS3_IFNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEvEEEES7_SF_EEEvDpOT_ + 72
69 RealmSyncDemo 0x0000000102c5647c _ZNSt3__110__function12__alloc_funcIPFvRKNS_8functionIFvvEEENS2_IFNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEvEEEENSA_ISG_EESF_EclES6_OSE_ + 72
70 RealmSyncDemo 0x0000000102c54ea0 _ZNSt3__110__function6__funcIPFvRKNS_8functionIFvvEEENS2_IFNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEvEEEENSA_ISG_EESF_EclES6_OSE_ + 72
71 RealmSyncDemo 0x0000000102ee0854 _ZNKSt3__110__function12__value_funcIFvRKNS_8functionIFvvEEENS2_IFNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEvEEEEEclES6_OSE_ + 104
72 RealmSyncDemo 0x0000000102ed0800 _ZNKSt3__18functionIFvRKNS0_IFvvEEENS0_IFNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEvEEEEEclES4_SC_ + 72
73 RealmSyncDemo 0x0000000102ed04b0 _ZN8facebook5react11JSIExecutor12callFunctionERKNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEESA_RKN5folly7dynamicE + 324
74 RealmSyncDemo 0x0000000102e97d88 _ZZN8facebook5react16NativeToJsBridge12callFunctionEONSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEES9_ON5folly7dynamicEENK3$_2clEPNS0_10JSExecutorE + 636
75 RealmSyncDemo 0x0000000102e97aec _ZNSt3__1L8__invokeIRZN8facebook5react16NativeToJsBridge12callFunctionEONS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESA_ON5folly7dynamicEE3$_2JPNS2_10JSExecutorEEEEDTclscT_fp_spscT0_fp0_EEOSI_DpOSJ_ + 36
76 RealmSyncDemo 0x0000000102e97a98 _ZNSt3__128__invoke_void_return_wrapperIvLb1EE6__callIJRZN8facebook5react16NativeToJsBridge12callFunctionEONS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESC_ON5folly7dynamicEE3$_2PNS4_10JSExecutorEEEEvDpOT_ + 52
77 RealmSyncDemo 0x0000000102e97a58 _ZNSt3__110__function12__alloc_funcIZN8facebook5react16NativeToJsBridge12callFunctionEONS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESB_ON5folly7dynamicEE3$_2NS8_ISF_EEFvPNS3_10JSExecutorEEEclEOSI_ + 52
78 RealmSyncDemo 0x0000000102e964fc _ZNSt3__110__function6__funcIZN8facebook5react16NativeToJsBridge12callFunctionEONS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESB_ON5folly7dynamicEE3$_2NS8_ISF_EEFvPNS3_10JSExecutorEEEclEOSI_ + 52
79 RealmSyncDemo 0x0000000102ea3fb8 _ZNKSt3__110__function12__value_funcIFvPN8facebook5react10JSExecutorEEEclEOS5_ + 84
80 RealmSyncDemo 0x0000000102ea3f30 _ZNKSt3__18functionIFvPN8facebook5react10JSExecutorEEEclES4_ + 48
81 RealmSyncDemo 0x0000000102ea3ef0 _ZZN8facebook5react16NativeToJsBridge18runOnExecutorQueueENSt3__18functionIFvPNS0_10JSExecutorEEEEENK3$_8clEv + 92
82 RealmSyncDemo 0x0000000102ea3e74 _ZNSt3__1L8__invokeIRZN8facebook5react16NativeToJsBridge18runOnExecutorQueueENS_8functionIFvPNS2_10JSExecutorEEEEE3$_8JEEEDTclscT_fp_spscT0_fp0_EEOSB_DpOSC_ + 24
83 RealmSyncDemo 0x0000000102ea3e2c _ZNSt3__128__invoke_void_return_wrapperIvLb1EE6__callIJRZN8facebook5react16NativeToJsBridge18runOnExecutorQueueENS_8functionIFvPNS4_10JSExecutorEEEEE3$_8EEEvDpOT_ + 28
84 RealmSyncDemo 0x0000000102ea3e04 _ZNSt3__110__function12__alloc_funcIZN8facebook5react16NativeToJsBridge18runOnExecutorQueueENS_8functionIFvPNS3_10JSExecutorEEEEE3$_8NS_9allocatorISA_EEFvvEEclEv + 28
85 RealmSyncDemo 0x0000000102ea2764 _ZNSt3__110__function6__funcIZN8facebook5react16NativeToJsBridge18runOnExecutorQueueENS_8functionIFvPNS3_10JSExecutorEEEEE3$_8NS_9allocatorISA_EEFvvEEclEv + 28
86 RealmSyncDemo 0x0000000102f217e8 _ZNKSt3__110__function12__value_funcIFvvEEclEv + 60
87 RealmSyncDemo 0x0000000102f216f8 _ZNKSt3__18functionIFvvEEclEv + 24
88 RealmSyncDemo 0x0000000102ca8f70 _ZN8facebook5react17tryAndReturnErrorERKNSt3__18functionIFvvEEE + 24
89 RealmSyncDemo 0x0000000102cd64a8 _ZN8facebook5react16RCTMessageThread7tryFuncERKNSt3__18functionIFvvEEE + 36
90 RealmSyncDemo 0x0000000102cdc200 _ZZN8facebook5react16RCTMessageThread10runOnQueueEONSt3__18functionIFvvEEEENK3$_1clEv + 80
91 RealmSyncDemo 0x0000000102cdc190 _ZNSt3__1L8__invokeIRZN8facebook5react16RCTMessageThread10runOnQueueEONS_8functionIFvvEEEE3$_1JEEEDTclscT_fp_spscT0_fp0_EEOSA_DpOSB_ + 24
92 RealmSyncDemo 0x0000000102cdc148 _ZNSt3__128__invoke_void_return_wrapperIvLb1EE6__callIJRZN8facebook5react16RCTMessageThread10runOnQueueEONS_8functionIFvvEEEE3$_1EEEvDpOT_ + 28
93 RealmSyncDemo 0x0000000102cdc120 _ZNSt3__110__function12__alloc_funcIZN8facebook5react16RCTMessageThread10runOnQueueEONS_8functionIFvvEEEE3$_1NS_9allocatorIS9_EES6_EclEv + 28
94 RealmSyncDemo 0x0000000102cdab84 _ZNSt3__110__function6__funcIZN8facebook5react16RCTMessageThread10runOnQueueEONS_8functionIFvvEEEE3$_1NS_9allocatorIS9_EES6_EclEv + 28
95 RealmSyncDemo 0x0000000102f217e8 _ZNKSt3__110__function12__value_funcIFvvEEclEv + 60
96 RealmSyncDemo 0x0000000102f216f8 _ZNKSt3__18functionIFvvEEclEv + 24
97 RealmSyncDemo 0x0000000102cd6204 ___ZN8facebook5react16RCTMessageThread8runAsyncENSt3__18functionIFvvEEE_block_invoke + 48
98 CoreFoundation 0x0000000180361f94 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20
99 CoreFoundation 0x0000000180361268 __CFRunLoopDoBlocks + 408
100 CoreFoundation 0x000000018035ba04 __CFRunLoopRun + 724
101 CoreFoundation 0x000000018035b218 CFRunLoopRunSpecific + 572
102 RealmSyncDemo 0x0000000102c7a8c8 +[RCTCxxBridge runRunLoop] + 764
103 Foundation 0x0000000180851578 __NSThread__start__ + 792
104 libsystem_pthread.dylib 0x00000001cc0b16c8 _pthread_start + 116
105 libsystem_pthread.dylib 0x00000001cc0ac910 thread_start + 8.]
Can you reproduce the bug?
Yes, always
Reproduction Steps
open App.tsx and either removing or adding path makes it run or crash.
const RealmContext = createRealmContext({
schema: [],
// This line crashes the app with sync.
path: 'sync.realm',
});
Crash happens after realm.logIn()
Version
10.19.1
What SDK flavour are you using?
Atlas App Services (auth, functions, etc.)
Are you using encryption?
No, not using encryption
Platform OS and version(s)
iOS 15.5
Build environment
System: OS: macOS 12.4 CPU: (10) arm64 Apple M1 Pro Memory: 105.23 MB / 16.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 17.9.0 - ~/.volta/tools/image/node/17.9.0/bin/node Yarn: 1.22.18 - ~/.volta/tools/image/yarn/1.22.18/bin/yarn npm: 8.5.5 - ~/.volta/tools/image/node/17.9.0/bin/npm Watchman: 2022.03.21.00 - /opt/homebrew/bin/watchman Managers: CocoaPods: 1.11.3 - /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5 Android SDK: Android NDK: 23.0.7344513-beta4 IDEs: Android Studio: Chipmunk 2021.2.1 Patch 1 Chipmunk 2021.2.1 Patch 1 Xcode: 13.4.1/13F100 - /usr/bin/xcodebuild Languages: Java: 18.0.1 - /Users/username/.jenv/shims/javac npmPackages: @react-native-community/cli: Not Found react: 17.0.2 => 17.0.2 react-native: 0.68.2 => 0.68.2 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found
Cocoapods version
1.11.3
Thanks for the report @rogerkerse, and for the reproduction repo. I can confirm I see the same issue here. I'll do some investigation into the root cause and let you know more when I do.
Hey @rogerkerse, I've had a closer look at your example and the issue here is that you are initially opening sync.realm as a non-synced Realm, then after the timeout fires, you are reopening the same file as a synced Realm, which is not supported unless you convert the non-synced Realm to a synced Realm first. It works when you do not specify a path because Realm will use a different default filename for the synced vs non-synced Realm.
Do you actually want to open the same Realm file as non-synced and synced? If so, I believe you need to use writeCopyTo to convert the local Realm to a synced Realm. It might be that there's a better way to handle what you want to do though, please feel free to describe your use case in more detail and I can try to help.
Well our situation is that app is usable logged in or logged out. Once user decides to log in, then we want to sync all data that he already produced or new data that he is going to produce.
Keeping 2 separate realms for that purpose makes it extremely complicated to handle. Explaining the scenario that I mean:
- App uses "local realm"
- User logs in
- We copy over all the data to "synced realm"
- We have to replace realm instance in all places in the app so no more data would be produced to the wrong realm.
Yes, RealmSync does not conform to modern UX/onboarding standards where a user may want to experience the app before logging in. Thats why we use Local Realm and built our own syncing logic from scratch. We would prefer to switch to Realm's solution, but it simply is too complicated currently.
We use a local realm that awaits user login. Then we even give the user the option if they want to have their data synced to the cloud. If they consent, we sync the current state of the db and start version tracking from there. I'm sure Realm Sync can achieve the same thing easily by considering all data entered into a synced realm as offline data until credentials are provided (same behavior as if the device is in airplane mode).
@ZComwiz We hare very recently made an option to convert the local realm to a synced realm for this particular use case (writeCopyTo) as @tomduncalf described. I assume that option wasn't available when you ran into the issue? It's definitely a use case we want to support, so feedback on how that solution works is appreciated.
@rogerkerse, it sounds like you would need to use writeCopyTo in this situation.
I am double checking with my colleague who implemented the feature, but I think the way to use it would be to open one Realm as a local Realm, then when the user logs in, write a copy of this Realm to a new, synced Realm.
I've created an example using our Node.js API here: https://github.com/tomduncalf/realm_4659_path_crashes_sync_node/blob/main/app.js, you can see that initially we open a local Realm, local.realm, then when the user logs in, we convert it to a synced Realm, sync.realm, and then reopen it with the new sync config.
Please let me know if anything is unclear or if you need more assistance with this!
@rogerkerse Did you have a chance to try the suggestion outline above?
@rogerkerse Did you have a chance to try the suggestion outline above?
Trying to implement it in real world scenario but getting <RealmProvider> and RealmContext to dynamically switch out is a headache. I'll keep you posted if I arrive at some success. Tho right now it seems to be more pain than benefit. I would have expected database framework to have done it internally.
@rogerkerse Would it be helpful if we provided an example of doing the same using Realm React? I didn't realise you were using React Native when I provided my example
@tomduncalf yes that would be extremely helpful to see in react-native (on the fly switching out RealmContext, useQuery etc.), thank you
@rogerkerse, good luck. In the real world this task is riddled with race conditions. I look forward to seeing what the realm team proposes, but this should just be a boolean on realm directly that should handle offline and online storage. Its much easier to do on the C level / managing the web-socket connection as a parameter than for us to manually throw data around on the front end, but I don't know how Realm implemented Sync.
@rogerkerse I'm still looking into this, you're right that it's non trivial with Realm React so I am trying to work out the best way to do this, and talking to the team about if we need to add additional functionality to support this flow. Sorry for the trouble!
@ZComwiz I'll chat to the team about your suggestion as I do agree this is somewhat complicated but there might be valid reasons for it being so. I'll get back to you with our thoughts, it might be an area where we can improve our API in future for sure.
Hey @tomduncalf is there any update on the issue? 😊
@kneth @tomduncalf @bmunkholm, this is a major issue for our team. We have been working extensively on a work around, but it makes far more sense to work together to help you make a proper API.
Is there anything we can do to help move this along?
@ZComwiz @rogerkerse Just want to chime in here. Instead of migrating a local realm to sync when a user logs in, why not start with an anonymous user in a synced realm, and then link the user to an authorized one (email, oauth, etc) when they sign up?
We have documentation on this.
Also we are planning on making the migration from non-sync to synced a bit more intuitive.
@takameyer thanks for your reply! Is it possible in that model to not sync certain collections until a user toggles a setting (keeping some objects purely local)?
@ZComwiz We don't support exactly what you are describing. If that’s something you need, please create a feature request and we can get it on our radar.
It is possible to pause sync, but this can't be done on a per model basis: https://www.mongodb.com/docs/realm-sdks/js/latest/Realm.App.Sync.Session.html#pause
In the pause() case, can you pause indefinitely until a user toggles a switch? Will records appear locally in their device? We don't need a collection by collection condition per say. We need to disable sync until a user explicitly enables it but we need them to have their local realm be populated in all cases for functionality purposes.
@ZComwiz That should theoretically work. Realm is offline first, so if sync is disabled, you would just be writing to a local realm. I can foresee a possible issue, that on app restart, it will attempt to sync that local data when the realm is opened.
Is there a reason you don't want the anonymous data in the cloud? You could create a cleanup trigger that purges anonymous user data after a period of time.
@takameyer minimize data collection where possible if the user does not require it.
We investigated this as a team today and found the app needs a change to convert from a local to a synced Realm.
Most notably:
- We changed the path of the Realm config given to
createRealmContextto keep it separate from the synced Realm. - We grab a reference to the local Realm using the
realmRefprop onRealmProviderto enable callingwriteCopyToon it. - We changed from
syncConfigtoconfig(of typeConfiguration) and set a value when theusergets authenticated. When thatconfiggets a value and we check if thesync.realmexists and if it doesn't we call thewriteCopyTomethod to convert the local Realm to a synced Realm, before theRealmProvidergets a chance to open the Realm.
We acknowledge that the process for upgrading a local to a synced Realm is a bit involved and we're tracking this issue to improve the experience: https://github.com/realm/realm-js/issues/4334.
Please see our suggestions applied below. If you keep experiencing the issue, please reply and feel free to re-open or create another issue if something else pops up.
import React, {useMemo, useEffect, useCallback, useState, useRef} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {AppProvider, createRealmContext, useApp} from '@realm/react';
import {Configuration, Credentials, SyncConfiguration} from 'realm';
const RealmContext = createRealmContext({
schema: [],
// This line crashes the app with sync.
path: 'local.realm',
});
const {RealmProvider} = RealmContext;
const MONGO_ATLAS_APP_ID = '---'; // Change to your mongo atlas app id
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'lightblue',
},
});
const AppComponent = () => {
const realmApp = useApp();
const [user, setUser] = useState<Realm.User<any>>();
const realm = useRef<Realm>(null);
const config: Configuration | undefined = useMemo(
() =>
user
? {path: 'sync.realm', sync: {flexible: true, user: user}}
: undefined,
[user],
);
const loginToRealm = useCallback(async () => {
// Waiting 2 seconds until login as a test simulating logging in later in the app's user flow
await new Promise(resolve => setTimeout(resolve, 2000));
const credentials = Credentials.anonymous();
try {
const realmUser = await realmApp.logIn(credentials);
console.log('Successfully logged in!', realmUser.id);
setUser(realmUser);
} catch (error) {
console.error('Failed to log in', (error as any).message);
}
}, [realmApp]);
useEffect(() => {
loginToRealm();
}, [loginToRealm]);
if (config && !Realm.exists(config) && realm.current) {
console.log(`Converting ${realm.current.path} to a synced Realm`);
// We need to convert the local realm into a synced Realm before opening it
realm.current.writeCopyTo(config);
}
return (
<RealmProvider {...config} realmRef={realm}>
<View style={styles.container}>
<Text>User {user?.id ?? 'not logged in'}</Text>
</View>
</RealmProvider>
);
};
const App = () => {
return (
<AppProvider id={MONGO_ATLAS_APP_ID}>
<AppComponent />
</AppProvider>
);
};
export default App;
@kraenhansen I’ve been trying to implement your solution, but unfortunately running into problems with setting up a subscription. This is the error that I'm getting after logging in:
Error:
Client attempted a write that is outside of permissions or query filters; it has been reverted (ProtocolErrorCode=231)
Details:
{
"Contact": {
"0.8426483083351229_synced": "cannot write to table \"Contact\" before opening a subscription on it"
}
}
Function Call Location:
IE
Remote IP Address:
213.35.181.75
SDK:
ios vRealmJS/10.19.1
Platform Version:
15.4
It seems that the subscription doesn't exist at the time and so the write gets reverted. Can you help us by pointing out a way to get past this?
Also, what about the data from local realm, that we want to carry over to the synced realm, but don't want to sync with the server? Our app has the option to turn off cloud syncing. Thus, user can limit what data gets synced, but we still want to store some information about their account. Is there any way to implement this?
My code demo: https://github.com/soliloquyx/realm-sync-demo-1
@soliloquyx We have recently discovered that converting a local Realm to a flexible sync Realm is not yet supported: https://github.com/realm/realm-core/issues/5798
It means that you will have to copy objects from your local Realm to your flexible sync Realm.
@kneth, has the realm team tried talking to some of their MDB customers to understand their use cases? The current implementation of Atlas Sync Realm seems pretty out of touch with the needs of modern day mobile apps. For instance, if an app developer wants to implement a task app, good UX may let them use the app and start adding objects to realm, but only sign in to save their data to the cloud or share it with colleagues. That would delay user login to an unknown time in the app life cycle and while it may be necessary for Mongo Atlas to set the owner of those documents, Realm should be able to persist data locally until being logged in which will give app developers a lot more flexibility to create proper UX flows that do not force a user to sign in before they understand more about the app they are using and if they feel comfortable letting that app handle their data on the cloud.
I'd love to chat with your team more and give some references to design patterns that should be supported that we use in industry developed at UC Berkeley and Stanford.
@ZComwiz Product for Realm here. Yes - we speak to users and customers all the time. I'm always happy to have a call and discuss your app, company, and use cases you'd like to support. You can email me at [email protected] and we can setup a time to talk.