Excessive writes occurring when initializing app with app.currentUser

I’ve been wrestling with a performance issue for awhile. While scrolling through a list of Results on iOS, Xcode Instruments shows about 1.5MB/s writes to numerous sync_metadata.realm.lock files. These writes are occurring when reading any data from a realm file. These writes only occur if the realm was initialized from an already logged in user.

My code is roughly:

if let user = app.currentUser {
   let realm = openRealm(user)
   let results = realm.objects(Object.self)
  // Performing any read of this results object causes several disk writes 
  // to occur per second, resulting in slow UI performance

} else { 
   let user = app.login(credentials)        
   let realm = openRealm(user)
   let results = realm.objects(Object.self)

   // Performing reads of this results object, after logging in, 
   // does not cause any disk writes and is much more performant. 

The question and code are a little unclear.

It appears a synced Realm is being used by checking for a user here app.currentUser but then what does this let realm = openRealm(user) do? Does it open a Flex Sync? Full Sync? Something else?

This code let results = realm.objects(Object.self) gets all of your objects (reads them) so then is there an additional read or is something else being done with the results?

Bearing in mind that a results object always reflects the current status of the underlying data, so it would not surprise me if there was additional disk access but why do you think it’s causing performance issues?

e.g. We have a number of databases with megabytes of databases and are not encountering what’s being described (if I understand it correctly)

Can you elaborate a bit or better yet, how about a brief sample code that duplicates the issue - what’s the expected result and what result are you getting?

Each time I access an object in those results, a small write is being performed to a sync_metadata file. But this only occurs if the realm was opened with an already logged in user, not after opening a realm with the user returned from app.login.

Maybe this code will explain better:

if let user = app.currentUser {
   let realm = try await Realm(configuration: user.flexibleSyncConfiguration)
   let results = realm.objects(Object.self)    // about 2000 objects  

   results.forEach { object in       // this causes a disk write for each object accessed

   // This exact snippet takes close to 1 second to run and *writes* 26mb of data to disk 

} else { 
   let user = app.login(credentials)        
   let realm = try await Realm(configuration: user.flexibleSyncConfiguration)
   let results = realm.objects(Object.self)   

   results.forEach { object in     // this does not cause any disk writes for each object accessed

  // This snippet takes 0.06 seconds to run and does not perform any disk writes.

The worst offender in my case is when I need to build a printable report on all the objects in those results, and have to iterate through them all to insert numerous properties into columns of a table. If the user is already logged in when the app launches, generating the report takes ~15 seconds and about 360mb is written to disk. If the user logs in, generating the report takes ~1 second with zero disk writes.

The problem occurs everywhere my app reads from live objects. If the user is already logged in when the app launches, reading from live objects causes writes to a sync_metadata file.

I’m expecting performance to be the same regardless if the realm is opened with an already logged in user, or one that is opened after the user logs in that session.

For clarify and testing purposes, I don’t believe this line of code is valid; login is asynchronous and has a closure

unless you’re using async/await, which would then be

let user = try await app.login(credentials: creds)

unless you have a some other code involved.

I have no idea if this will help, but here’s our results using your code and roughly 2000 Sync’d Realm objects with two properties per object.

Testing your code execution times produced an opposite and expected result. Reading the data with an existing current user was faster than authenticating and then reading. I pretty much copy and pasted your code - the only difference is we use partition sync, not flex sync.

The first column in the below chart (in seconds) runs when there is no logged in user, so the authentication code must run, which is the code after the else statement in the question.

The second column is if there’s already an existing user logged in user = app.currentUser which is the first section of code after the if

No Current User Current User
runs after ‘else’ runs after ‘if’
0.7071 0.03
0.4909 0.04
0.439 0.04
0.70 0.04
0.682 0.04

If we change the speed testing to eliminate the delay caused by the authentication code, I ran the test 5 more times and the reading of the data is identical between the first section and second section of code

In summary - the auth code causes the second section of code to be slower, as expected. However, if we remove that from the performance test, it’s identical in speed.

Here’s a disk writing chart. The first section is on app start, no logged in user and the app is idling.

The second section is when the user logs in and reads the data (the code after the else) and the third section is if the current user exists, which is the first section of code. Total Data written is roughly 1.4 Mb

Summary; we’re not seeing any big difference in disk writes between when a user is already logged in user = app.currentUser or when they log in fresh. Total amount of written data was roughly 1.4Mb in either case.

Your results are exactly what I expect would happen. Here are the graphs I captured in my case:

Using external auth service to get JWT credentials, login to app with those credentials, get the results, and iterate through them:

versus skipping auth and just using app.currentUser to get results and iterate:

Here’s the Instruments capture showing, in a 10 millisecond span, 15 writes to 5 separate sync_metadata.realm.lock files

And here’s the entire 6 second capture:

Here’s the same code ran, but with a fresh user from app.login:

Here’s the exact code I used to capture these screenshots. There are 1659 LogbookEntry objects in the realm, and the number of writes seems to correspond with that.

As expected, the authentication pipeline takes more time than simply grabbing app.currentUser. But once the code reaches the loop to print the dates, the “else” section is much faster because it isn’t making writes to disk for each item in the loop.

Within the rest of the app, if I don’t get a “fresh” user, merely scrolling through a lazy list of Results will cause tens/hundreds/thousands of writes.

This is with Realm-Swift v10.34.

We are not seeing that at all. Over a run of 10 tests, both sections performed exactly the same and we are not seeing any kind of writing or high disk usage in either case (as shown in my prior post)

I wonder what the difference is as we copied and pasted your code.

So, with the code in the question, the issue is not duplicatable. I suspect there’s something else affecting the performance or operation - possibly other code in the app.

May I suggest creating a fresh project? Only add code that writes 2000 objects, and then the code in your question that reads them in and see if it behaves differently.

I’m at a loss for what might be causing my results. The code I posted an image of is the first and only code running when my app launched in the test cases. I even went on to take out the date formatting and printing operations, and just iterated through the results with nothing else being performed.

I’ll try a fresh project and report back.

I was able to discover the root cause of my issue. Initializing objects with a _ownerID string, read from the current user ID, was causing the writes to disk.

class SomeObject: Object {
    @Persisted var _ownerID: String = app.currentUserID    // This was causing the writes to the .lock file

It’s interesting this problem only showed up when referencing a “stale” user object, but not one received from the app.login function.

Thanks for your assistance!

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.