MongoDB.live, free & fully virtual. Register Now MongoDB.live, free & fully virtual. Register Now

Can't Synchronise Global Realm

I have created a global realm for use in a Xamarin Forms app with a PrintGrade class as follows;

public class PrintGrade : RealmObject
{
	[PrimaryKey]
	[MapTo("id")]
	public int ID { get; set; }

	[Required]
	[MapTo("grade")]
	public string Grade { get; set; }

	[Required]
	[MapTo("name")]
	public string Name { get; set; }

	[MapTo("active")]
	public bool Active { get; set; }
}

And I use the following code to configure, open and synchronise the realm;

public static class AppConfig
{
	...
	public const string CommonRealm = "DriverAppCommon";
	...
}
	
public partial class RunSheet : ContentPage
{
	...
	private readonly string _realmCommon = AppConfig.CommonRealm;
	private Realm _commonRealm;
	...
	protected override async void OnAppearing()
	{
		...
		_commonRealm = await OpenRealm(_realmCommon, _user).ConfigureAwait(true);

		if (_commonRealm != null)
		{
			if (!await SynchroniseRealm(_commonRealm, false, true).ConfigureAwait(true))
				throw new Exception("Failed to download the reference data required by the app.");
		}
	}
	...
	private async Task<Realm> OpenRealm(string realmName, User user)
	{
		Realm realm = null;
		try
		{
			var config = ConnectionServices.GetRealmConfiguration(realmName, user);
			realm = ConnectionServices.ConnectToSyncServer(config);
		}
		catch (Exception ex)
		{
			...
		}
		return realm;
	}
	private async Task<bool> SynchroniseRealm(Realm realm, bool upload, bool download, int timeout = 0)
	{
		bool synchronised = false;
		try
		{
			var session = realm.GetSession();
			Thread.Sleep(150);
			using CancellationTokenSource cts = new CancellationTokenSource();

			switch (timeout)
			{
				case -1:
					// No timeout - wait until finished...
					break;
				default:
					cts.CancelAfter(TimeSpan.FromSeconds(timeout));
					break;
			}
			if (download)
			{
				if (timeout == -1)
					await SynchroniseRealmData(session, download).ConfigureAwait(true);
				else
					await SynchroniseRealmData(session, download).CancelAfter(cts.Token).ConfigureAwait(true);
			}
			if (upload)
			{
				...
			}
			synchronised = true;
		}
		catch (OperationCanceledException)
		{
			Analytics.TrackEvent(nameof(SynchroniseRealm), new Dictionary<string, string> {
				{"User", _userName },
				{ download ? "DownLoad" : "Upload", $"Timed out after {timeout} seconds" }
			});
		}
		catch (Realms.Exceptions.RealmException ex)
		{
			Crashes.TrackError(ex, new Dictionary<string, string> { { "Synchronise Realm", (IsDownloading ? "Downloading" : "Uploading") } });
		}
		catch (Exception ex)
		{
			Crashes.TrackError(ex, new Dictionary<string, string> { { "Synchronise Realm", (IsDownloading ? "Downloading" : "Uploading") } });
		}
		return synchronised;
	}
	private static async Task SynchroniseRealmData(Session session, bool download)
	{
		if (download)
			await session.WaitForDownloadAsync().ConfigureAwait(true);
		else
			await session.WaitForUploadAsync().ConfigureAwait(true);
	}
}

public static class ConnectionServices
{
	private const string _commonRealm = "DriverAppCommon";

	public static Realm ConnectToSyncServer(FullSyncConfiguration config)
	{
		return Realm.GetInstance(config);
	}
	public static FullSyncConfiguration GetRealmConfiguration(string realmName, User user)
	{
		FullSyncConfiguration config;
		Uri serverUrl;
		if (realmName == _commonRealm)
		{
			serverUrl = new Uri(realmName, UriKind.Relative);
			config = new FullSyncConfiguration(serverUrl, user)
			{
				ObjectClasses = new[] { typeof(PrintGrade) }
			};
		}
		else
		{
			...
		}
		return config;
	}
}

My problem is the common realm never finishes synchronising. I have tried it with both an indefinite and a 2 minute timeout. The realm only contains 13 records so I would expect it to take seconds to synchronise.

From what I have read in the documentation a global realm is read-only for all users and the user I pass to the procedures is the same user from previous code who has been authenticated and downloaded their own realm already. Am I missing something?

@Raymond_Brack Are there any client side and server side logs you can share with us please? Just a hunch, have you tried the asyncOpen API?

    public static Task<Realm> OpenRealm(User user)
    {
        var config = new SyncConfiguration(Partition, user);
        return Realm.GetInstanceAsync(config);
    }

@Ian_Ward I get the following error entry in the log;

HTTP response: f2530548-b8c3-421e-bcff-bdbfac7e1201 {"type":"https://docs.realm.io/server/troubleshoot/errors#access-denied","title":"The path is invalid or current user has no access.","status":403,"code":614}

But from what I can determine I am accessing the correct Realm with a valid user;

Do I need to give permission to every user to access the global realm?

@Raymond_Brack No you can just use '*' for the userId when you use the applyPermission API to apply the same permission to all users

@Ian_Ward It appears I can’t add the permission using Realm Studio and I couldn’t locate the API documentation. Is there a REST API to handle permissions? I know there is a GraphQL API but this appears to be for querying the data.

The API is in the SDK itself, see here:
https://docs.realm.io/sync/using-synced-realms/access-control/path-level-permissions