Already in write transaction at stress test

I’m confuse with a behaviour im observing at realm (swift).

So from beginning, I got this error
Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=io.realm Code=9 "mmap() failed: Cannot allocate memory size: 45088768 offset

i found out this might be related to ios limitations on memory allocation due the way realm works here.
https://github.com/realm/realm-swift/issues/6431

So I’ve decided to create a stress test to test my solution.

Current situation:
I’ve added this code

DispatchQueue(label: "filldatabase").async {
                while true {
                    let data : myDataClass = myDataClass.generateFakeData() as! myDataClass
                    Services.persistence.store(data: data, timestamp: Date()) // save to realm
                }
            }

I’m observing a weird behaviour here… seems like I’m able to create something like “500” entries in the database but then i get a crash with ‘The Realm is already in a write transaction’

this doesnt seem to make much sense… why does it crash after being sucessfull on adding something like “500” entries , whats different on entry “501”? is there any limitation im missing?

While I kinda understand the question, it’s still a bit vague and the included code doesn’t appear to have anything to do with Realm (e.g. there are no Realm calls).

I suggest including a verifiable example of actual Realm code so we can copy/paste it in an attempt to duplicate the issue.

In other words, .generateFakeData probably does something with Realm but we don’t know what that is. Likewise Services.persistence.store probably stores something in Realm, but again, we don’t know what.

All of that being said, it really looks like you’re taking Realm objects, which are lazily loaded, and loading all of them into an array of some kind and then casting it to Data()? Which then makes them NOT lazily loaded so you’re running out of app memory. But that’s just a guess.

Can you update the question with more details and code?

yes i was going to edit the post to add the code but couldn’t find the button so i’ve decided to wait to see any answers.

the generate fake data just creates a object with data, nothing special
the store does something like this

 func store(data: myDataClass, timestamp : Date) {
        let blocksToSave = data.dailygoalBlocks.map{ ... }
        let goalsToSave = data.previousDailygoals.map{ ... }
        let eventsToSave = data.event.map{ ... }
        
        let realm = try! Realm()
        
        do {
            try realm.write {
                realm.add(blocksToSave, update: .modified)
                realm.add(goalsToSave, update: .modified)
                realm.add(eventsToSave, update: .modified)
            }
        } catch  {
            FTLog(.realm, .error, "Could not store blocks")
        }
    }

the objects i create are more or less like this

import Foundation
import SwiftProtobuf
extension myDataClass  {
    
    static func generateFakeData() -> AnyObject {
        
        var data = myDataClass()
        data.goal = 30
        data.goalBlocks = []
        data.event = []
        data.uuid = UUID().uuidString
        data.timestamp = Google_Protobuf_Timestamp.generateFakeData() as! Google_Protobuf_Timestamp
        data.profileUuid = UUID().uuidString
        data.previousGoals = []
        
        return data as AnyObject
    }
    
}

Yep. Got it

Well, AnyObject is not a Realm object, and I think the same situation applies as mentioned above; not enough code to understand the use case - you’re extending myDataClass… is that a Realm Object?

Then, and maybe more importantly, what’s the purpose of this code block…

while true {
...
}

…that’s an infinite loop - are you intentionally creating an infinite number of objects? That will both lock up your devices thread and would likely eventually crash due to running out of disk space, depending on the other code being called.

no, thats an api object, which is converted to 3 realm objects collections here:

Clarification

the idea is to replicate the crash error (from first post) that I was getting with a test.

Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=io.realm Code=9 "mmap() failed: Cannot allocate memory size: 45088768 offset

this error was obtained from QA team while communicating with the API (which is in development)

so in order to work on the crash ( due lack on memory size) I’ve created that test with while loop to fill the database until I replicate the same message… but seems the crash messages vary from what i was expecting and have different behaviours?

i was also expecting an immediate crash on the app but seems it take several loops (see following code)

i just made a few changes adding this code to prevent the same crash (write transaction)
while (realm.isInWriteTransaction){return }

and i got this as an answer

*** Terminating app due to uncaught exception ‘NSMallocException’, reason: ‘*** -[NSObject allocWithZone:]: attempt to allocate object of class ‘RLMRealmConfiguration’ failed’

terminating with uncaught exception of type NSException

with a crash while trying to find out current realm filesize
let url : URL? = Realm.Configuration.defaultConfiguration.fileURL // crash
confirming that its an issue with memory allocation

what I fail to understand is why we get a crash instead a memory warning

But now talking on solutions

what is the expected to do in this scenario, when the memory available is not enough? shouldn’t I be receiving a memory warning?

sorry i forgot to edit it to this
guard (!realm.isInWriteTransaction) else { return }

I’ve been finding lots of people with memory alloc issues
So I’ve take 3 actions. Some of the links for reference ( can only post 2 links since im a new user)

  1. I’ve added autorelease pool to the realm operations
  2. I’ve added the option to compact realm at start of the app
  3. I’ve set the maximum active connections to a limit (this is currently causing my a crash due exceeding the limit)

You’ve provided quite a bit of data and unfortunately, none of that is helpful to us as is. We don’t know the scope of your project or where the data is coming from.

Let me provide an example to consider and maybe this will help refine the question:

Suppose you have a Realm Class “Person”

class Person: Object {
   @Persisted var name = ""
}

and your app is designed to store and display every persons name in the United States - that’s a lot of people.

So your device is running this app and you want to display the user names in a table

let myPeople = realm.objects(People.self)
... show the names in a table.

Well, how would it be possible to show everyones names in a tableView without overloading the device. It’s because those objects are lazily loaded and take up almost no device memory - even when there are millions of objects.

However, as soon as you use any high level, non-realm functions, ALL of the data is loaded. So I see this in your question

let blocksToSave = data.dailygoalBlocks.map{ ... }

DOH! .map is a high level function and all of those objects stop being lazy and become full-fledged-memory-gobbling-objects, and boom, your app crashes.

Then, as I mentioned above, you’ve got an infinite loop writing an infinite about of data, and potentially creating an infinite amount of objects. So that too could be an issue

Does that make sense? So our issue is there is not enough continuous code presented to understand what those objects are, or where they came from, or what you’re doing with them

1 Like