realm / realm-js

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

onMigration does not working with app crashing #5881

Closed devethan closed 1 year ago

devethan commented 1 year ago

How frequently does the bug occur?

Always

Description

The below error always occur when I try to migrate type of date to string.

com.facebook.react.JavaScript (10): EXC_BAD_ACCESS (code=1, address=0x30)

And I got this message from issue navigator on Xcode

I've tested various migration cases, but I got the same error every time I did a migration that changed types. Here's I reffered for the migration

I've also tried to clean up cache on yarn and watchman

yarn cache clean
rm -rf node_modules yarn.lock
yarn
watchman watch-del-all

migration codes

class Transaction extends Realm.Object<Transaction> {...}
class Plan extends Realm.Object<Plan> {
  _id!: Realm.BSON.ObjectId;
  startAt!: string; // date to string
  endAt!: string; // date to string
  transactions!: Realm.List<Transaction>;

  static schema = {
    name: 'Plan',
    properties: {
      _id: 'objectId',
      startAt: 'string',
      endAt: 'string',
      transactions: 'Transaction[]',
    },
    primaryKey: '_id',
  };
}
class OldPlan extends Realm.Object<OldPlan> {
  _id!: Realm.BSON.ObjectId;
  startAt!: Date;
  endAt!: Date;
  transactions!: Realm.List<Transaction>;

  static schema = {
    name: 'Plan',
    properties: {
      _id: 'objectId',
      startAt: 'date',
      endAt: 'date',
      transactions: 'Transaction[]',
    },
    primaryKey: '_id',
  };
}

const REALM_SCHEME_VERSION = 2;
const config: Realm.Configuration = {
  schema: [Currency, Category, Transaction, Plan],
  deleteRealmIfMigrationNeeded: false,
  schemaVersion: REALM_SCHEME_VERSION,
  onMigration(oldRealm, newRealm) {
    // 1 -> 2
    if (oldRealm.schemaVersion < REALM_SCHEME_VERSION) {
      // Plan
      const oldPlans = oldRealm.objects(OldPlan);
      const newPlans = newRealm.objects(Plan);

      oldPlans.forEach((oldPlan, pIdx) => {
        newPlans[pIdx].startAt = format(oldPlan.startAt, 'yyyy-MM-dd');
        newPlans[pIdx].endAt = format(oldPlan.endAt, 'yyyy-MM-dd');
      });
    }
  },
};

// Create a realm context
export const {RealmProvider, useObject, useQuery, useRealm} =
  createRealmContext(config);

Screenshot on Xcode

Screenshot on simulator

pacakge.json

{
"dependencies": {
    "@react-native-community/datetimepicker": "^6.4.2",
    "@react-native-firebase/analytics": "^18.0.0",
    "@react-native-firebase/app": "^18.0.0",
    "@react-native-firebase/firestore": "^18.0.0",
    "@react-navigation/native": "^6.0.8",
    "@react-navigation/native-stack": "^6.9.12",
    "@react-navigation/stack": "^6.2.0",
    "@realm/react": "^0.4.3",
    "axios": "^1.4.0",
    "date-fns": "^2.29.3",
    "date-fns-tz": "^2.0.0",
    "native-base": "^3.4.28",
    "react": "18.2.0",
    "react-native": "0.71.8",
    "react-native-bootsplash": "^4.7.1",
    "react-native-calendars": "^1.1294.0",
    "react-native-gesture-handler": "^2.9.0",
    "react-native-haptic-feedback": "^2.0.3",
    "react-native-reanimated": "^3.2.0",
    "react-native-safe-area-context": "^4.4.1",
    "react-native-screens": "^3.19.0",
    "react-native-svg": "^13.7.0",
    "react-native-svg-transformer": "^1.0.0",
    "react-native-vector-icons": "^9.2.0",
    "realm": "11.9.0",
    "recoil": "^0.7.7"
  },
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "@babel/runtime": "^7.12.5",
    "@bam.tech/react-native-make": "^3.0.3",
    "@react-native-community/eslint-config": "^2.0.0",
    "@testing-library/jest-native": "^5.4.2",
    "@testing-library/react-native": "^12.1.2",
    "@tsconfig/react-native": "^2.0.2",
    "@types/jest": "^26.0.23",
    "@types/react": "^18.0.21",
    "@types/react-native": "^0.70.6",
    "@types/react-native-calendars": "^1.1264.3",
    "@types/react-native-vector-icons": "^6.4.13",
    "@types/react-test-renderer": "^18.0.0",
    "@types/realm": "^1.13.0",
    "@typescript-eslint/eslint-plugin": "^5.37.0",
    "@typescript-eslint/parser": "^5.37.0",
    "babel-jest": "^26.6.3",
    "eslint": "^7.32.0",
    "jest": "^29.2.1",
    "jest-config": "^26.6.3",
    "metro-react-native-babel-preset": "^0.73.7",
    "react-native-version": "^4.0.0",
    "react-test-renderer": "18.2.0",
    "typescript": "^4.8.3"
  }
}

Stacktrace & log output

com.facebook.react.JavaScript (10): EXC_BAD_ACCESS (code=1, address=0x30)

Can you reproduce the bug?

Always

Reproduction Steps

  1. Add an onMigration function
  2. yarn ios or Run build on Xcode

Version

"realm": "11.9.0", "@realm/react": "^0.4.3"

What services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

macOS 13.4, iOS 16.4

Build environment

System:
    OS: macOS 13.4
    CPU: (10) arm64 Apple M1 Pro
    Memory: 134.27 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.16.0 - ~/.nvm/versions/node/v18.16.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v18.16.0/bin/yarn
    npm: 9.5.1 - ~/.nvm/versions/node/v18.16.0/bin/npm
    Watchman: 2023.04.10.00 - /opt/homebrew/bin/watchman
  Managers:
    CocoaPods: 1.12.0 - /opt/homebrew/bin/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4
    Android SDK: Not Found
  IDEs:
    Android Studio: 2020.3 AI-203.7717.56.2031.7784292
    Xcode: 14.3.1/14E300c - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.11 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.2.0 => 18.2.0 
    react-native: 0.71.8 => 0.71.8 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Cocoapods version

1.12.0

kneth commented 1 year ago

@devethan Thank you for your detailed bug report. Your code looks fine but could be it related to https://github.com/facebook/hermes/issues/986? To test it, you could disable Hermes (and use JSC instead)?

devethan commented 1 year ago

@kneth I really appreciate your support🙏 There is one thing I want to check first. I'm using hermes, and it's been working fine, but from the moment the following code in onMigration is added, I'm getting an error.

const oldPlans = oldRealm.objects(OldPlan);

When I check the console.log(old.schemaVersion) without importing the existing object, it's fine. I still have to use hermes.

I think it's related to an error in the migration step that gets objects from OldPlan. What is your opinion?

kneth commented 1 year ago

We have a test similar to your code.

I hope you can find time to test and answer the two questions. If the answer to the second question is "yes", my guess is that our interacting with TypeScript class models might have some subtle bug which our tests cannot catch.

devethan commented 1 year ago

@kneth Okay, I'm gonna try right now.

devethan commented 1 year ago

@kneth OMG, it works.

const oldPlans = oldRealm.objects<OldPlan>(Plan.name!);
const newPlans = newRealm.objects<Plan>(Plan.name!);

But it didn't work

// Same error
const oldPlans = oldRealm.objects<OldPlan>(OldPlan.name!);
const newPlans = newRealm.objects<Plan>(Plan.name!);

I'm guessing it's related to Typescript, as you say. Maybe the problem is caused by the difference in how JS and TS interpret class objects?

OldPlan.name // 'OldPlan'
Plan.name // 'Plan'
devethan commented 1 year ago

In my opinion, the examples in Modify a Property Type should be updated to look like this

class Person extends Realm.Object<Person> {
  _id!: Realm.BSON.ObjectId;
  firstName!: string;
  lastName!: string;
  age!: number;

  static schema = {
    name: 'Person',
    properties: {
      // update the data type of '_id' to be 'objectId' within the schema
      _id: 'objectId',
      firstName: 'string',
      lastName: 'string',
    },
  };
}

// `OldObjectModel` is only used for type injection for `oldRealm` and are not related to the actual schema, `Person`
interface OldObjectModel {
  _id: string;
  firstName: string;
  lastName: string;
  age: number;
}

const config: Realm.Configuration = {
  schema: [Person],
  // increment the 'schemaVersion', since the property type of '_id'
  // has been modified
  schemaVersion: 2,
  onMigration: (oldRealm: Realm, newRealm: Realm) => {
    if (oldRealm.schemaVersion < 2) {
      const oldObjects = oldRealm.objects<OldObjectModel>(Person.name!); // OR 'Person'
      const newObjects = newRealm.objects<Person>(Person.name!); // OR 'Person'
      // loop through all objects and set the _id property
      // in the new schema
      for (const objectIndex in oldObjects) {
        const oldObject = oldObjects[objectIndex];
        const newObject = newObjects[objectIndex];
        newObject._id = new Realm.BSON.ObjectId(oldObject._id);
      }
    }
  },
};

// Pass the configuration object with the updated
// 'schemaVersion' and 'migration' function to createRealmContext()
const {RealmProvider} = createRealmContext(config);

Without change those scheme, like this

...
const oldObjects = oldRealm.objects<OldObjectModel>(OldObjectModel.scheme.name);
const newObjects = newRealm.objects<Person>(Person.scheme.name);
...
kneth commented 1 year ago

@devethan It is great to see some progress. We should investigate the differences between TypeScript and JavaScript.

I will ask the documentation team to take a look at your suggestion.

krollins-mdb commented 1 year ago

Thank you, @devethan! Definitely an issue with the docs. I apologize for sending you down this long path. I'll update this docs sample ASAP. And set aside some time to re-evaluate all the TypeScript samples just in case there are more issues like this elsewhere in the docs.

devethan commented 1 year ago

Hi @kneth @krollins-mdb I have an opinion about the document. I got an error during migration related toschemaVersion is 0. But no document said that schemaVersion starts with 0. I just thought that schemaVersion starts with 1. Because there's only example that migrate schemaVersion to 2 from 1

How about saying in the document that "initial schemaVersion is 0" I think it's helpful for the relam newbie.

krollins-mdb commented 1 year ago

@devethan, that's excellent feedback - thank you! I have a PR in progress right now to fix the example. I'll update to have it go from 0 > 1.

krollins-mdb commented 1 year ago

@devethan, changes haven't been deployed to the live docs site yet, but the PR is merged! Thanks again for providing your feedback and helping to make the docs better.

kneth commented 1 year ago

The docs change has been merged, and I am closing the issue. Feel free to create a new issue if you experience issues with migration.