Hello,
Realm keeps throwing this error in a simple use case: Cannot access realm that has been closed
The example I am using is available at the react-native-realm repo:
RealmExample.js
import Realm from 'realm';
class Item {}
Item.schema = {
name: 'Item',
properties: {
name: 'string',
date: 'date',
id: 'string'
},
};
export default new Realm({schema: [Item]});
app.js
//My imports
export default class App extends Component<{}> {
render() {
return (
<RealmProvider realm={realm}>
<ConnectedExample />
</RealmProvider>
);
}
}
ConnectedExample.js
import React, { Component } from 'react';
import {
Text,
ScrollView,
TouchableOpacity,
View,
StyleSheet,
} from 'react-native';
import uuid from 'uuid';
import { connectRealm } from 'react-native-realm';
import ConnectedExampleItem from './ConnectedExampleItem';
const styles = StyleSheet.create({
screen: {
paddingTop: 20,
paddingHorizontal: 10,
backgroundColor: '#2a2a2a',
flex: 1,
},
add: {
height: 44,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 10,
backgroundColor: '#1a1a1a',
},
addText: {
color: 'white',
},
});
class ConnectedExample extends Component {
count = 0;
onPressAddItem = () => {
const { realm } = this.props;
realm.write(() => {
realm.create('Item', {
name: this.count.toString(),
date: new Date(),
id: uuid.v4(),
});
this.count++;
});
};
render() {
return (
<View style={styles.screen}>
<TouchableOpacity onPress={this.onPressAddItem} style={styles.add}>
<Text style={styles.addText}>Add Item</Text>
</TouchableOpacity>
<ScrollView>
{this.props.items.map((item) => (
<View key={item.id}>
<ConnectedExampleItem id={item.id} />
</View>
))}
</ScrollView>
</View>
);
}
}
export default connectRealm(ConnectedExample, {
schemas: ['Item'],
mapToProps(results, realm) {
return {
realm,
items: results.items.sorted('date') || [],
};
},
});
ConnectedExampleItem.js
import React, {
Component,
PropTypes,
} from 'react';
import {
StyleSheet,
TouchableOpacity,
Text,
} from 'react-native';
import { connectRealm } from 'react-native-realm';
const styles = StyleSheet.create({
item: {
height: 44,
justifyContent: 'center',
paddingHorizontal: 10,
marginTop: 10,
backgroundColor: 'cyan',
},
});
class ConnectedExampleItem extends Component {
onPressRemoveItem = (item) => {
const { realm } = this.props;
realm.write(() => {
realm.delete(item);
});
};
render() {
return (
<TouchableOpacity
onPress={() => this.onPressRemoveItem(this.props.item)}
style={styles.item}
>
<Text>{this.props.item.name}</Text>
</TouchableOpacity>
);
}
}
export default connectRealm(ConnectedExampleItem, {
schemas: ['Item'],
mapToProps(results, realm, ownProps) {
return {
realm,
item: results.items.find(item => item.id === ownProps.id),
};
},
});
Running this example in a mint project runs fine. However, when I add Realm to my own project, I run into the Cannot access realm that has been closed (I haven't instantiated Realm anywhere else)
Also, trying the example here works fine as well.
This error is mentioned nowhere in the docs or in SO. What could be causing this? How do I fix it?
Thank you.
My best guess is that your Realm instance has been GC'ed (reference count == 0).
Uh... meaning? In layman's terms? ^^
@Unforgiven-wanda I think https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management explains it better than I can do :smile:
The instance created by export default new Realm({schema: [Item]}); might reach a reference count of 0, and the object is deallocated (and the Realm is closed).
+1 Same issue here.
After calling realm.close() the application keeps getting this error even after a reinstall. The realmjs example doesn't include a usage of realm.close() but it's used once in the RealmJS docs here:
// Initialize a Realm with Car and Person models
Realm.open({schema: [CarSchema, PersonSchema]})
.then(realm => {
// Add persons and their cars
realm.write(() => {
let john = realm.create('Person', {name: 'John', cars: []});
john.cars.push({make: 'Honda', model: 'Accord', miles: 1500});
john.cars.push({make: 'Toyota', model: 'Prius', miles: 2780});
let joan = realm.create('Person', {name: 'Joan', cars: []});
joan.cars.push({make: 'Skoda', model: 'Octavia', miles: 1120});
joan.cars.push({make: 'Ford', model: 'Fiesta', miles: 95});
joan.cars.push({make: 'VW', model: 'Golf', miles: 1270});
let jill = realm.create('Person', {name: 'Jill', cars: []});
let jack = realm.create('Person', {name: 'Jack', cars: []});
jack.cars.push({make: 'Porche', model: '911', miles: 965});
});
// Find car owners
let carOwners = realm.objects('Person').filtered('cars.@size > 0');
console.log('Car owners')
for (let p of carOwners) {
console.log(` ${p.name}`);
}
// Find who has been driver longer than average
let average = realm.objects('Car').avg('miles');
let longerThanAverage = realm.objects('Person').filtered('[email protected] > $0', average);
console.log(`Longer than average (${average})`)
for (let p of longerThanAverage) {
console.log(` ${p.name}: ${p.cars.sum('miles')}`);
}
realm.close(); <========================================== **here**
});
Is using realm.close() a strict requirement to prevent memory leakage or can I continue getting by without ever using it? Also, how on earth do I open the Realm again after a realm.close() without refactoring my code for every instance to use:
Realm.open({schema: [Schema]})
.then(realm => { ... })
Currently I am doing export default new Realm({schema: [Schema]}) and _importing_ as realm so I do not see a way to do a Realm.open() with my imported realm instance. Whenever I attempt to either do a realm.write() or realm.beginTransaction(), I receive the same Error: Cannot access realm that has been closed. Sorry for the long post, I've looked everywhere for a solution and ended up here again.
Nevermind, I found this in the docs again. I guess I missed it the first time around:
Synchronously Opening Realms
You can create a realm instance by simply invoking the constructor and passing a configuration object to it. This is typically not recommended as it blocks and could potentially be a time consuming operation, especially if there are migrations to run or if the realm is synchronized and you don鈥檛 want to risk modifying data before it鈥檚 been completely downloaded.
If you still want to do this, the pattern is simple:
const realm = new Realm({schema: [PersonSchema]});
// You can now access the realm instance.
realm.write(/* ... */);
If a Realm has read-only permissions, then you must use the asynchronous api鈥檚 to open it. Opening a read-only Realm with the above pattern will cause an error.
Will be reverting my changes back to Realm.open({schema: [Schema]}). Question though, is it still necessary to call realm.close() or sandwich my write operations between realm.beginTransaction() and realm.commitTransaction()?
In general, Realms are cached so calling Realm.open isn't as expensive as it sounds. Of course, if the schema has changed, you will pay in terms for updating the Realm file.
@Unforgiven-wanda this is happening on the Android side? I got the same error, but only in Android.
@franzejr Yes it is. Haven't got the chance to try it out on IOS.
I have since circumvented the issue, by exporting my schemas from my exports file and:
constructor(props) {
super(props);
this.realm = new Realm({schemas})
}
in every connected component.
I 'm also doing this.
const realm = new Realm({schema: [PersonSchema]});
// You can now access the realm instance.
realm.write(/* ... */);
and then i get the mmap() fail: out of memory
if i put realm.close() at end of each realm.write.. it will report that realm has been close.
how do i resolve the memory issue
@rebirthtobi Can you provide a little context? For example, which operating system (Android or iOS)? What is the device? How large is your Realm file?
On Android, RN is running as a 32 bit process which will give you less available memory than you might expect. mmap() seems to report that you are running out of memory, and the question above is asked to understand why.
@kneth i'm working on Android and my data is quite large. what is the limitation for the amount of data i can store
I can't give any hard numbers but I believe that on 32 bit Android, the number is low. See also https://stackoverflow.com/questions/43654965/mmap-limitation-on-32-and-64-bit-android-system
@Unforgiven-wanda do you know how your solution fixes the garbage collection problem?
@Progoogler do you mind sharing your end solution? Did you refactor your code to use Realm.open and realm.close every time you needed access to the realm?
@MrHubble I honestly have no idea. In my use-case I was pressed for time and so the first that worked for me (my message above) still holds for the app
been following this issue since to find a more permanent fix
@kneth does the team at Realm plan on releasing an official way to use Realm.open with es6 modules (import/export)? There seems to be a number of different attempted solutions by users in various issues but I have yet to find one that helps me eliminate the bug I am seeing with Realm.
@MrHubble It would be great to get it in. It is not on our short-term priority list and we will appreciate help from our community 馃槃
Please create an issue where we can discuss what and how to implement.
@kneth Thanks for taking the time to reply. I'd be more than happy to help. I previously created a separate issue requesting the example app be updated: https://github.com/realm/realm-js/issues/1320
I would think it would only take a couple of minutes for someone at Realm to just give some general guidelines. For example, create an async function to open your realm and export from your module:
js
//ReactExample/components/realm.js
export default const realm = async () => {
let realm
try {
realm = await Realm.open({ schema, schemaVersion: 1, encryptionKey: key })
.catch((e) => {
console.error(e)
})
} catch (e) {
console.error(e)
}
return realm
}
and then import in your other component:
js
import realm from './realm';
This example is incorrect but it's just me trying to understand how it _could_ be implemented. I don't know how realm.close() plays into this as well. Some best practices to follow would be extremely helpful.
Realm.close() is used to free native ressources (memory, file descriptors, etc.). I guess you will have to export that method too as I can't see an easy way to implement "auto close".
@Unforgiven-wanda, you wrote:
I have since circumvented the issue, by exporting my schemas from my exports file and:
constructor(props) { super(props); this.realm = new Realm({schemas}) }in every connected component.
This worked out better than an instance of export default new Realm({schemas}); imported in files needed? I ask this because I have problems with the import way.
And do you need to call realm.close() somewhen?
Thanks.
@donni106
Yes, in my case it did work better and so far I have noticed no issue.
And no, you don't need to call realm.close() at any point.
Most helpful comment
+1 Same issue here.
After calling
realm.close()the application keeps getting this error even after a reinstall. The realmjs example doesn't include a usage ofrealm.close()but it's used once in the RealmJS docs here:Is using
realm.close()a strict requirement to prevent memory leakage or can I continue getting by without ever using it? Also, how on earth do I open the Realm again after arealm.close()without refactoring my code for every instance to use:Currently I am doing
export default new Realm({schema: [Schema]})and _importing_ asrealmso I do not see a way to do aRealm.open()with my importedrealminstance. Whenever I attempt to either do arealm.write()orrealm.beginTransaction(), I receive the same Error: Cannot access realm that has been closed. Sorry for the long post, I've looked everywhere for a solution and ended up here again.