realm / realm-js

Realm is a mobile database: an alternative to SQLite & key-value stores
https://realm.io
Apache License 2.0
5.79k stars 575 forks source link

`onFirstOpen` callback does not run in React Native app #6913

Open purpleCapibara opened 3 days ago

purpleCapibara commented 3 days ago

How frequently does the bug occur?

Always

Description

Realm documentation mentions optional onFirstOpen callback that can be used to seed database with data if necessary. https://www.mongodb.com/docs/realm-sdks/js/latest/types/BaseConfiguration.html

I'm currently trying to implement Realm DB in a React Native app that needs to seed data on the first load. This callback seems like the best way to do it, but I was not able to make it work in my production app and in a simple repro.

This issue seems to be present in the latest available version of realm-js (12.3.1), @realm/react (0.11), and react-native (0.76) with New Architecture enabled. My production app is still on react-native 0.74 using old architecture, realm-js 12.11.1, and @realm/react (0.8.0) and issue seems to be present there too.

Callback is not triggered for encrypted databases too.

Last year there was a similar issue opened, but no repro was provided: https://github.com/realm/realm-js/issues/6143

Stacktrace & log output

No response

Can you reproduce the bug?

Always

Reproduction Steps

  1. Create new React Native app
  2. Install Realm
  3. Setup Realm provider and basic schema
  4. Add onFirstOpen callback and create data that is needed for created schema
  5. Build and install app
  6. Run it and verify that data is not seeded correctly

Repository with reproduction: https://github.com/purpleCapibara/realm-onFirstLoad-repro/blob/main/App.tsx

Version

"@realm/react": "^0.11.0", "react": "18.3.1", "react-native": "0.76.0", "realm": "^12.13.1"

What services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

Android 15 & iOS 18

Build environment

Output from npx react-native info:

System:
  OS: macOS 14.6.1
  CPU: (16) arm64 Apple M3 Max
  Memory: 4.04 GB / 64.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.17.0
    path: /opt/homebrew/opt/node@20/bin/node
  Yarn:
    version: 3.6.4
    path: /opt/homebrew/opt/node@20/bin/yarn
  npm:
    version: 10.8.2
    path: /opt/homebrew/opt/node@20/bin/npm
  Watchman:
    version: 2024.09.09.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.0
      - iOS 18.0
      - macOS 15.0
      - tvOS 18.0
      - visionOS 2.0
      - watchOS 11.0
  Android SDK: Not Found
IDEs:
  Android Studio: 2024.1 AI-241.18034.62.2412.12266719
  Xcode:
    version: 16.0/16A242d
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.12
    path: /usr/bin/javac
  Ruby:
    version: 3.0.7
    path: /opt/homebrew/bin/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 15.0.0-alpha.2
    wanted: 15.0.0-alpha.2
  react:
    installed: 18.3.1
    wanted: 18.3.1
  react-native:
    installed: 0.76.0
    wanted: 0.76.0
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: true
  newArchEnabled: true

Logs

 (NOBRIDGE) LOG  Bridgeless mode is enabled
 (NOBRIDGE) LOG  realm log detail DB: 48481 Thread 0x16dffb000: Open file: /Users/john/Library/Developer/CoreSimulator/Devices/C355F37F-AA20-4E33-92AD-D20BC7EB491D/data/Containers/Data/Application/02BB93BD-C5B2-44FB-A3CF-6E57D5475BE8/Documents/mongodb-realm/edek/server-utility/metadata/sync_metadata.realm Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000:    Number of participants: 1 Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000:    Durability: Full Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000:    EncryptionKey: no Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000:    Empty file Realm.Storage
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Start read 47315: 1 ref 0 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: End transaction 47315 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Start read 47315: 1 ref 0 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: acquire writemutex Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: writemutex acquired Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Tr 47315: Acquired write lock in 144 us Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Tr 47315: Already on version: 1 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Tr 47315: Promote to write: 1 -> 1 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Creating schema version 7 in mode 'Automatic' Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Add class 'metadata' Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'metadata': Add property 'version' of type_Int Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Create object 'metadata' Realm.Storage.Object
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000:    Set 'version' to -1 Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Add TopLevel class 'FileActionMetadata' with primary key property 'original_name' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Add Embedded class 'class_UserIdentity' Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Add class 'class_UserMetadata' Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Add class 'class_current_user_identity' Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'FileActionMetadata': Add property 'new_name' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'FileActionMetadata': Add property 'action' of type_Int Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'FileActionMetadata': Add property 'url' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'FileActionMetadata': Add property 'identity' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserIdentity': Add property 'id' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserIdentity': Add property 'provider_type' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserMetadata': Add property 'identity' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserMetadata': Add property 'legacy_uuids' list of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserMetadata': Add property 'refresh_token' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserMetadata': Add property 'access_token' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserMetadata': Add property 'identities' list linking 'UserIdentity' Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserMetadata': Add property 'state' of type_Int Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserMetadata': Add property 'device_id' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserMetadata': Add property 'profile_data' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'UserMetadata': Add property 'local_realm_paths' set of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: On class 'current_user_identity': Add property 'current_user_identity' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Mutating anonymous object 'metadata'[ObjKey(0)] Realm.Storage.Object
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000:    Set 'version' to 7 Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Migration did run in 3393 us (8 changes) Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Initiate commit version: 2 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Commit of size 10752 done in 6720 us ref 2272 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: writemutex released Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: End transaction 47315 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Start read 47315: 2 ref 2272 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: acquire writemutex Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: writemutex acquired Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Tr 47315: Acquired write lock in 155 us Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Tr 47315: Already on version: 2 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Tr 47315: Promote to write: 2 -> 2 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: Start frozen 47911: 2 ref 2272 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Initiate commit version: 3 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log debug DB: 48481 Thread 0x16dffb000: Commit of size 328 done in 1131 us ref 2400 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: writemutex released Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: End transaction 47911 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 48481 Thread 0x16dffb000: End transaction 47315 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log detail DB: 53665 Thread 0x16dffb000: Open file: /Users/john/Library/Developer/CoreSimulator/Devices/C355F37F-AA20-4E33-92AD-D20BC7EB491D/data/Containers/Data/Application/02BB93BD-C5B2-44FB-A3CF-6E57D5475BE8/Documents/default.realm Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000:    Number of participants: 1 Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000:    Durability: Full Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000:    EncryptionKey: no Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000:    Empty file Realm.Storage
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: Start read 18688: 1 ref 0 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: End transaction 18688 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: Start read 18688: 1 ref 0 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: acquire writemutex Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: writemutex acquired Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: Tr 18688: Acquired write lock in 142 us Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: Tr 18688: Already on version: 1 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: Tr 18688: Promote to write: 1 -> 1 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Creating schema version 0 in mode 'Automatic' Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Add class 'metadata' Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: On class 'metadata': Add property 'version' of type_Int Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Create object 'metadata' Realm.Storage.Object
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000:    Set 'version' to -1 Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Add TopLevel class 'FeatureFlags' with primary key property 'name' of type_String Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: On class 'FeatureFlags': Add property 'enabled' of type_Bool Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Mutating anonymous object 'metadata'[ObjKey(0)] Realm.Storage.Object
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000:    Set 'version' to 0 Realm.Storage.Object
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Migration did run in 658 us (2 changes) Realm.Storage
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Initiate commit version: 2 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Commit of size 4480 done in 5666 us ref 728 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: writemutex released Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: End transaction 18688 Realm.Storage.Transaction
 INFO  
 💡 JavaScript logs will be removed from Metro in React Native 0.77! Please use React Native DevTools as your default tool. Tip: Type j in the terminal to open (requires Google Chrome or Microsoft Edge).
 (NOBRIDGE) LOG  Running "RealmRepro" with {"rootTag":1,"initialProps":{"concurrentRoot":true},"fabric":true}
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: Start read 14143: 2 ref 728 Realm.Storage.Transaction
 (NOBRIDGE) LOG  featureFlag value undefined
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Creating ResultsNotifier for 'FeatureFlags' Realm.Storage.Notification
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: Start read 42802: 2 ref 728 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16e343000: Start read 13460: 2 ref 728 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16e343000: Start read 10454: 2 ref 728 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16e343000: Tr 13460: Already on version: 2 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16e343000: End transaction 42802 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16e343000: Query find all: 'TRUEPREDICATE', limit = -1 Realm.Storage.Query
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16e343000: Query found: 0, Duration: 9 us Realm.Storage.Query
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16e343000: ResultsNotifier 'FeatureFlags' ran in 28 us Realm.Storage.Notification
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16e343000: Start read 10307: 2 ref 728 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16e343000: Start read 18022: 2 ref 728 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16e343000: End transaction 10454 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000: Start read 45473: 2 ref 728 Realm.Storage.Transaction
 (NOBRIDGE) LOG  realm log debug DB: 53665 Thread 0x16dffb000: Delivering notifications for 'FeatureFlags' after 4365 us Realm.Storage.Notification
 (NOBRIDGE) LOG  realm log trace DB: 53665 Thread 0x16dffb000:    No changes Realm.Storage.Notification

Cocoapods version

1.15.2

sync-by-unito[bot] commented 3 days ago

➤ PM Bot commented:

Jira ticket: RJS-2914

purpleCapibara commented 3 days ago

I managed to seed database by using realmRef, checking whether db is empty and running seeding script, but not sure whether there are any edge cases to be aware in this case.

function App(): React.JSX.Element {
  const realmRef = useRef<Realm | null>(null);

  useEffect(() => {
    if (realmRef.current?.isEmpty) {
      realm.write(() => {
        Object.entries(RealmFeatureFlag).forEach(([, value]) => {
          realm.create(REALM_TYPE_FEATURE_FLAGS, {
            name: value,
            value: false,
          });
        });
      });
    }
  }, []);

  return (
    <RealmProvider
      realmRef={realmRef}
      schema={[FeatureFlagsSchema]}
    >
      <SafeAreaView>
        <FeatureFlagView />
      </SafeAreaView>
    </RealmProvider>
  );
}

Edit: found even better solution

const realm = new Realm(realmConfig);

// From the docs:
// **Note:** the hooks returned from `createRealmContext` using an existing Realm can be used outside of the scope of the provider.
const { RealmProvider, useRealm, useQuery } = createRealmContext(realm);

const useSeedRealmOnFirstOpen = () => {
  const realm = useRealm();

  useEffect(() => {
    if (realm.isEmpty) {
      // initialise feature flags
      realm.write(() => {
        Object.entries(RealmFeatureFlag).forEach(([, value]) => {
          realm.create<RealmFeatureFlags>(
            REALM_TYPE_FEATURE_FLAGS,
            {
              name: value,
              value: false,
            },
            Realm.UpdateMode.Never,
          );
        });
      });
    }
  }, [realm]);
};

export const RealmProvider = ({ children }: Props) => {
  useSeedRealmOnFirstOpen();

  return <ReactRealmProvider>{children}</ReactRealmProvider>;
};