Problems with .NET API in Unity and a few other issues

Hi,

I recently discovered Realm and I really would like to use it for my new game project, however there are quite some problems when using Realm with Unity.
I’m using the latest .NET API v10.9.0 and tried both the recommended Unity version 2020.3.12 and the latest LTS release.
Note: My game will only work if sync is active.

  1. If sync is interrupted (device offline, sync server offline, otherwise stuck, …) I want to display an offline screen to my players while reconnecting in the background and/or with an optional “Reconnect” button. This is important, because the game wouldn’t work anymore if sync is not active. It cannot be played offline. However I cannot find a way to get the correct connection status of sync. “CurrentRealm.GetSession().State” is always returning “SessionState.Active” even if the logs are showing that Realm is already trying to reconnect in the background.
    The only possibility to get the connection state seems to be adding a “CustomLogger” and searching in the info messages for “Connected to endpoint” or “Connection closed”, but this is rather hacky.
    Also in a forum post it is mentioned that the Swift API already has a callback for changes of the connection status… Can this also be added to the .NET API?
    It would additionally be nice to get some control over the reconnect procedure happening in the background to display information on the offline screen. Or to be able to react to the event that the background reconnection process is stuck or to implement a custom reconnect function.

  2. The Unity Editor is crashing very often after leaving the Play mode. This is happening with my game project after I added Realm and also with a small test project that uses only the few most important functions (authentication, connecting to realm, starting sync). I tried with the recommended Unity version 2020.3.12 as well as the latest version. It’s always the same problem.
    It crashes also sometimes when just clicking back into the Unity Editor after a few minutes of inactivity.
    This makes Realm almost unusuable in my main project as it takes a long time to startup due to enabled collaboration.
    Fixing these crashes in the Unity Editor should be the top priority for the .NET API!

Here’s a stacktrace for the most common crash (only happens if realm had been opened via “await Realm.GetInstanceAsync(config)” before):

Stacktrace:

  at <unknown> <0xffffffff>
  at (wrapper managed-to-native) Realms.Sync.SessionHandle/NativeMethods.destroy (intptr) [0x00009] in <4588409fe1d84ee9b4108329f1189e94>:0
  at Realms.Sync.SessionHandle.Unbind () [0x00006] in <4588409fe1d84ee9b4108329f1189e94>:0
  at Realms.RealmHandle.RequestUnbind (Realms.RealmHandle) [0x00020] in <4588409fe1d84ee9b4108329f1189e94>:0
  at Realms.RealmHandle.ReleaseHandle () [0x0002d] in <4588409fe1d84ee9b4108329f1189e94>:0
  at System.Runtime.InteropServices.SafeHandle.DangerousReleaseInternal (bool) [0x000a4] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Runtime.InteropServices.SafeHandle.InternalFinalize () [0x0000a] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Runtime.InteropServices.SafeHandle.Dispose (bool) [0x0000b] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Runtime.InteropServices.SafeHandle.Finalize () [0x00002] in <695d1cc93cca45069c528c15c9fdd749>:0
  at (wrapper runtime-invoke) object.runtime_invoke_void__this__ (object,intptr,intptr,intptr) [0x00020] in <695d1cc93cca45069c528c15c9fdd749>:0

=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
=================================================================

Unloading 0 Unused Serialized files (Serialized files now loaded: 0)
Crash!!!

With just having “App.Create(appconfig)” and “LoginAsync” in ~1/20 cases it crashes on Play/Stop with the following Stacktrace.

Stacktrace:

  at <unknown> <0xffffffff>
  at (wrapper managed-to-native) Realms.Native.HttpClientTransport.respond (Realms.Native.HttpClientTransport/HttpClientResponse,Realms.Sync.Native.StringStringPair[],int,intptr) [0x0010c] in <4588409fe1d84ee9b4108329f1189e94>:0
  at Realms.Native.HttpClientTransport/<ExecuteRequest>d__9.MoveNext () [0x00335] in <4588409fe1d84ee9b4108329f1189e94>:0
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore/MoveNextRunner.InvokeMoveNext (object) [0x00006] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x00073] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x00004] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore/MoveNextRunner.Run () [0x00032] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction (System.Action,bool,System.Threading.Tasks.Task&) [0x0001d] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.Task.FinishContinuations () [0x0006f] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.Task.FinishStageThree () [0x0003d] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.Task`1<TResult_REF>.TrySetResult (TResult_REF) [0x00050] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<TResult_REF>.SetResult (TResult_REF) [0x00040] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Net.Http.HttpClient/<SendAsyncWorker>d__47.MoveNext () [0x001e6] in <a1ea9757398742d6979db7cbaf288344>:0
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore/MoveNextRunner.InvokeMoveNext (object) [0x00006] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x00073] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x00004] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore/MoveNextRunner.Run () [0x00032] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction (System.Action,bool,System.Threading.Tasks.Task&) [0x0001d] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.Task.FinishContinuations () [0x0006f] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.Task.FinishStageThree () [0x0003d] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.Task`1<TResult_REF>.TrySetResult (TResult_REF) [0x00050] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<TResult_REF>.SetResult (TResult_REF) [0x00040] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Net.Http.HttpClientHandler/<SendAsync>d__65.MoveNext () [0x00542] in <a1ea9757398742d6979db7cbaf288344>:0
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore/MoveNextRunner.InvokeMoveNext (object) [0x00006] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x00073] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext,System.Threading.ContextCallback,object,bool) [0x00004] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore/MoveNextRunner.Run () [0x00032] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction (System.Action,bool,System.Threading.Tasks.Task&) [0x0001d] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.Task.FinishContinuations () [0x0006f] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Threading.Tasks.Task.FinishStageThree () [0x0003d] in <695d1cc93cca45069c528c15c9fdd749>:0
  1. When sync is disabled in the Realm UI, Unity gets stuck on awaiting the result of Realm.GetInstanceAsync(config). Nothing is thrown and control is completely lost at this point.
    The last life sign is a comprehensive info text from Realm about sync not being activated shown in the Unity Editor. My App would be stuck here and there’s no workaround to treat this error. This must also be fixed as soon as possible.
    Note: When using Realm.GetInstance(config) instead, the same info text about sync not being activated is shown in Unity Editor but program flow continues until I call WaitForDownloadAsync()
    which never returns and doesn’t even show any error message…

  2. When Realm tries to reconnect in background it causes extremely high CPU usage during this process (at least on Windows in Unity Editor).
    Once it has reconnected, the CPU goes down again. Maybe something like a sleep(1) is missing in a loop? This should be checked.

  3. Reconnection time increases with every failed try by almost factor 2. This is way too much. I need it to reconnect in much shorter and constant intervals, e.g. every 10-15 secs, so that my users
    can play the game again immediately when internet connection is back. Otherwise the automatic reconnecting in background is useless for my game. Can this be configured somehow?

  4. How to decrease ping intervals to detect disconnects (default seems to be 60 secs)? When starting Realm it says something like “Config param: ping_keepalive_period = 60000 ms”, “Config param: connect_timeout = 120000 ms” or “Config params: fast_reconnect_limit = 60000ms” in the logs. How to change these values? And what does “fast_reconnect_limit” mean? I would need a faster detection of the current connection status, e.g. every 10 or 15 seconds. If this is currently not possible, can it be added?

  5. Is there any recommended way to rate limit function calls per user? Or a way to see how much functions calls specific users are doing in a certain amount of time? This would be very important to mitigate attacks or prevent too high costs.

  6. When using the CLI it should be possible to only update a certain part of the realm configuration, e.g. only the functions or even only a specific function.

Thanks a lot,
MetalMonkey

1 Like

Hey, thank you so much for the detailed feedback. It’s super valuable and we’d like to understand your use case better. I won’t be able to address all your points in a single post and will probably get some help from our PM for some of these, but let me take the more technical ones first.

  1. This is an oversight and we’ll add it. Filed realm-dotnet/issue#2801 to track progress on this one.

  2. The Editor crashing is definitely unexpected and we should investigate what’s going on. I filed realm-dotnet/issue#2800 to track this. These stacktraces should be enough to get us started but may need to reach out to you in case we have difficulty reproducing it.

  3. This behavior is by design, though we may want to change it. I’ll bring it up with the team responsible for building Sync to understand what we want to do there. As a workaround, you can pass in a cancellation token to the GetInstanceAsync method that cancels the wait after some time and fallback to using GetInstance instead.

  4. This too is not something we’ve seen before and we’d like to try and reproduce. How are you simulating the disconnected state? Are you pausing sync on the server or are you using some firewall rule to prevent connections from your machine to the server?

  5. This is by design and is intended to avoid swarming the server with requests in case the connection failure is caused by the server being under heavy load. You can use the Reconnect method exposed by the sync client (app.Sync.Reconnect()) to force reconnection earlier than that, though you’ll probably want to wait for connection state to get exposed to setup a timer - it will probably look something like:

    realm.SyncSession.ConnectionStateChange += (session, args)
    {
        if (args.NewState == ConnectionState.Disconnected)
        {
            // Show a banner to alert the user they're offline
    
            // Try to reconnect after 15s
            Task.Delay(15_000).ContinueWith(_ =>
            {
                // Check if we're still disconnected
                if (session.ConnectionState == ConnectionState.Disconnected)
                {
                    // Try reconnecting
                    session.User.App.Sync.Reconnect();
                }
            });
        }
    }
    
  6. I’ll talk to the Sync team and see if we can expose more granular controls over these parameters.

Again, truly appreciate the detailed report and we’ll do our best to find a resolution for the bugs you posted. I’ll reach out to our cloud PM to get you an answer for points 7 and 8 as those are outside my area of expertise.

1 Like

Hi @MetalMonkey -

  1. We do automatically Rate Limit Realm Apps - https://docs.mongodb.com/realm/reference/service-limitations/#request-traffic, there is also a notification sent to Atlas Alerts when this happens. This isn’t configurable at the moment, but you can vote for it: Configure rate limit – MongoDB Feedback Engine!

  2. Can you explain why you want to add a way to only change one part of the config? When using the CLI, the current developer experience is to modify any part of the app config and use realm-cli push to push any of those changes…

Let me know your thoughts,

Sumedha

1 Like