Embarrassingly dumb question - can't get sync going on .Net 6 or 7

I have an app that works on Xam.Mac and iOS, but SG and CBL are not supported there. Upgrading my mac to Ventura caused the Mac app to stop working. I could never get it going again (after several weeks of effort).

So I’m working on upgrading my app to .Net 6 or 7. First step was to create a test application - a simple default “Mac OS .Net App”. That runs fine. Then I added the CBL nuget package and libLiteCore native reference. Then the only code I added was to open a DB, check it works, and start a sync (I’ll paste the code below).

Open/Write/Read to the database works, but the sync fails with the following errors:

The type initializer for 'Couchbase.Lite.Support.MacProxy' threw an exception.
2023-1-26 10:09:39.521+13:00 [3]| ERROR)  [Network] {C4SocketImpl#15} No response received after 15 sec -- disconnecting
...
2023-1-26 10:09:39.524+13:00 [24]| ERROR)  [Replicator] {Repl#14} Got LiteCore error: Network error 3, "connection timed out"

I’ve turned the logging up on the sync server, and no connection comes in (i.e. no messages). I’ve tried a sync server running remotely and locally:

2023-1-26 10:09:39.525+13:00 [24]| INFO)  [Replicator] (Replicator) [24] Replicator[<*> ws://localhost:4984/testdb] is Connecting, progress 0/0

Same results either way. I’ve also tried this using .Net6 and .Net7 and over TLS.

I know the sync server does work because prior to the Ventura upgrade, everything was fine. Also, the iOS app on my phone still connects and syncs and works.

Does anyone have an idea as to how I can troubleshoot this? Or what might have gone wrong after the Ventura upgrade? Why is that simple program generating an exception in MacProxy (BTW - I think that MacProxy has some bad paths - see here)

Many thanks.
Paul.

Below is a copy of the code that was added to the ViewController.ViewDidLoad

    DatabaseConfiguration options = null;
    Database database = null;
	Replicator replicator = null;
    public void DoTest()
	{
		
		try
		{
            Database.Log.Console.Domains = LogDomain.All;
            Database.Log.Console.Level = LogLevel.Verbose;

            options = new DatabaseConfiguration
			{
				Directory = "."
			};
			database = new Database("MyTest", options);
			Console.WriteLine("Database opened ok");
		}
		catch (Exception e)
		{
			Console.WriteLine("Failed to open database: " + e.Message);
			return;
		}
		//	Prove the database works by saving and loading a document.
		var id = System.Guid.NewGuid().ToString();
		var doc = new MutableDocument(id);
		doc.SetBoolean("b", true);
		doc.SetInt("i", 1024);
		database.Save(doc);
		var newdoc = database.GetDocument(id);
		if (newdoc is null)
		{
			Console.WriteLine("Failed to write/read from the database");
			return;
		}

		//	Lets start the sync engine.
		try
		{
			var url = new Uri("ws://localhost:4984/testdb");
			var target = new URLEndpoint(url);
			var auth = new BasicAuthenticator("8afA2PdF8qYS", "the-password");
			var sync_config = new ReplicatorConfiguration(database, target)
			{
				ReplicatorType = ReplicatorType.PushAndPull,
				Continuous = true,
				Authenticator = auth,
				ConflictResolver = null,
			};
			replicator = new Replicator(sync_config);
			var listenerToken = replicator.AddChangeListener(SyncStatusUpdate);

			Console.WriteLine("StartSyncAgent: Using url: " + url);
			replicator.Start();
			Console.WriteLine("Completed startup");
		}
		catch (Exception e)
		{
			Console.WriteLine("Got exception");
		}
	}

	public void SyncStatusUpdate(object? sender, ReplicatorStatusChangedEventArgs e)
	{
		var changes = (int)e.Status.Progress.Total;
		var completed = (int)e.Status.Progress.Completed;

		Console.WriteLine("SyncStatusUpdate for changes {0}, completed {1}, status {2}",
			changes, completed, e?.Status.Activity);

        if (e?.Status.Error != null)
        {
            Console.WriteLine($"Error :: {e?.Status.Error}");
        }
    }

I’m not a Sync Gateway guy - but the first thing I would check is if something is listening on the host:port you are trying to open. You can test that something is listening on a port with curl (or browser) - see “To confirm that Sync Gateway is running” on Postman Collections for Sync Gateway Administration - The Couchbase Blog

curl http://localhost:4984

It should give a welcome message. If it gives “Connection Refused”, then nothing is listening. If it hangs - then you have some other connectivity issue - if AWS, then likely the your ip address is not allowed to connect (which won’t be the case for localhost).

Hi @mreiche, thanks for the notes. I realise I probably didn’t give enough information originally. I am pretty experienced with SG, so I have definitely tested the endpoint with curl. I use the SG REST interface for some functions, and I have used my unit tests to hit that interface, which works. I have also tested using the curl command you provide and that works too.

I think the problem might be related to the exception that MacProxy is throwing. I can’t seem to get the inner exception on it (I’ve tried setting a breakpoint on exceptions, to no avail).

Maybe this post will help c# - Couchbase.Lite fails to replicate - Stack Overflow

I can spin up an SG on my local machine, and the app still won’t connect to it (even though I can via curl). The possible issue they were highlighting was routing over an SSH tunnel, which is not the case here. Thx.

I think you’ve already found the issue here:

Why is that simple program generating an exception in MacProxy (BTW - I think that MacProxy has some bad paths - see here )

Perhaps, although I’ve had a previous discussion with @borrrden on this. He doesn’t think it would cause the problem. Further, why am I affected when I am guessing that no-one else is (I mean, I can’t even create a minimal .net 6/7 app and use SG). Something seems off.

I’ve already forgotten what discussions we had on this, but if you can send a repro app I can look and see what is happening. If possible I’d like to see exactly what exception is being thrown. It may be a change in Ventura, I am still not running it actually (too much stuff breaks every time I update macOS so I resist) but if your repro works fine on my machine that is an area for exploration.

I thoroughly agree about upgrading too soon. I sure am regretting it now.
I’m just packaging up a project to send you.

Aside from some weird Visual Studio issues with the debugger that I had to fix (codesigning of debugger executables…) the sample you sent me has no crash or anything and appears to run just fine. The documents are synced correctly. So this might be something either Ventura specific or specific to your environment. Perhaps you could copy and paste the mac proxy code (it’s not that much) and see what happens when you run it directly in your app?

Hi @borrrden,

An update on this problem.

I did run your experiment - I copied the MacProxy code to my project, and attempted to call CreateProxyAsync. And lo’ and behold, I do get an exception. Below is my analysis.

The MacProxy code comes from here

First - here is my call:

    var proxy = new CoreLibrary.MacProxy();
    proxy.CreateProxyAsync(new Uri("http://google.com")).Wait();

In CreateProxyAsync, the call to LoadCFNetwork works, which it should since it tries both the old and new CFNetwork path.

Then it calls:

    var proxySettings = CFNetworkCopySystemProxySettings(cFNetworkHandle);

which calls a generic:

    return GetDelegate<CFNetworkCopySystemProxySettingsDel>(cFNetworkHandle)();

which in turn calls:

IntPtr symbolHandle = GetPointer(libHandle, typeof(T).Name);

T is CFNetworkCopySystemProxySettingsDel, so typeof(T).Name is “CFNetworkCopySystemProxySettingsDel”, and the handle is that of CFNetwork library.

The GetPointer calls does this:

    var symbolHandle = dlsym(libHandle, symbolName);

which returns null, so this is where the exception happens. The string message that doesn’t reach the top level is:

    Unable to find the symbol CFNetworkCopySystemProxySettingsDel in 0x000000048cbd7ea8

So you are trying to create a delegate to CFNetworkCopySystemProxySettings so you can get the internet proxy settings. But the code that I am looking at seems to be attempting to get a pointer to CFNetworkCopySystemProxySettingsDel (the name of the delegate type).

I must be missing something. This looks like a typo?
Cheers.
Paul.

Taking this one step further… what happens if I fix the code?
If you change the GetPointer function so that it takes the correct name:

    private static T GetDelegate<T>(IntPtr libHandle, string name)
    {
        IntPtr symbolHandle = GetPointer(libHandle, name);
        return Marshal.GetDelegateForFunctionPointer<T>(symbolHandle);
    }

and fix up the calls to GetDelegate so they pass the string name in rather than using the type:

    return GetDelegate<CFNetworkCopySystemProxySettingsDel>(cFNetworkHandle, "CFNetworkCopySystemProxySettings")();
    return GetDelegate<CFNetworkCopyProxiesForURLDel>(cFNetworkHandle, "CFNetworkCopyProxiesForURL")(url, proxySettings);

then everything magically works.

Of course I don’t know why MacProxy works for everyone else. And I cannot replace the MacProxy in the library I have, but this looks pretty curious, right?

Cheers.
Paul.

Have you seen the updated version of this? We received a PR from a Ventura user to fix things: macos: fix MacProxy initialization in >= Ventura · couchbase/couchbase-lite-net@4576536 · GitHub.

Yeah - I used the most recent release of that code and I note in my analysis that you try the old and new locations for CFNetwork, which works on Ventura. What doesn’t make sense to me is that MacProxy.cs still has the line in it that shouldn’t work:

    IntPtr symbolHandle = GetPointer(libHandle, typeof(T).Name);

typeof(T).Name seems wrong, since T is CFNetworkCopyProxiesForURLDel, and “CFNetworkCopyProxiesForURLDel” is not the name of the function you want (its actually CFNetworkCopyProxiesForURL"). In fact your MacProxy code does not work on my machine (Ventura) until I make the fix I’ve outlined above.

I can’t figure out how MacProxy works for you without my changes.

I’m confused :-).
Cheers.
Paul.

It’s easy to answer why it works for me in particular…I’m still not running Ventura. I will try to ask someone else on the team that does, and in the meantime this sounds like it is PR worthy. @blake.meike This will probably go into 3.1 and @pasin Are you running Ventura and can you try a simple program to see if you can drive this? Alternatively I guess I could update to Ventura myself but I am nervous doing that during a release cycle.

EDIT I see what you mean about it should not be working for me either though, now that I look a bit closer!

I don’t have an answer quite yet but it is still the case that just starting a replicator (a direct connection to SG) causes this issue?

Sorry one more question, can you reproduce this behavior with a regular .NET console application as well? That will help with the simplicity of reproducing here.

I’ve created a console app that has two copies of MacProxy - one from GitHub and one with my fixes. The fixed one runs. The GitHub version throws the following:

“Unable to find the symbol CFNetworkCopySystemProxySettingsDel”.

You can switch between the two with a

    #if FIXED_MACPROXY

I’ll upload a zip privately.
Its running on .Net 6 (I hope that’s ok… but the bug should fail on any version of .net because its an incorrect name).

And finally… to answer your previous question:

I don’t have an answer quite yet but it is still the case that just starting a replicator (a direct connection to SG) causes this issue?

Yes. I start the sync agent, the first callback immediately arrives with “Not Started” status (which seems normal), and my app continues with other stuff. Then about 300ms later, I get the message:

The type initializer for 'Couchbase.Lite.Support.MacProxy' threw an exception.

A few ms later I get a more messages:

2023-3-5 03:56:00.445+13:00 [13]| ERROR)  [Network] {C4SocketImpl#2} No response received after 15 sec -- disconnecting
2023-3-5 03:56:00.445+13:00 [13]| ERROR)  [Network] {C4SocketImpl#2} No response received after 15 sec -- disconnecting
2023-3-5 03:56:00.459+13:00 [.NET Long Running Task]| ERROR)  [Network] {C4SocketImpl#2} WebSocket failed to connect! (reason=Network error 3)
2023-3-5 03:56:00.459+13:00 [.NET Long Running Task]| ERROR)  [Network] {C4SocketImpl#2} WebSocket failed to connect! (reason=Network error 3)
2023-3-5 03:56:00.468+13:00 [26]| ERROR)  [Replicator] {Repl#1} Got LiteCore error: Network error 3, "connection timed out"
2023-3-5 03:56:00.468+13:00 [26]| ERROR)  [Replicator] {Repl#1} Got LiteCore error: Network error 3, "connection timed out"
2023-3-5 03:56:00.494+13:00 [26]| ERROR)  [Replicator] {C4Replicator#3} Transient error (Network error 3, "connection timed out"); attempt #2 in 2 sec...
2023-3-5 03:56:00.494+13:00 [26]| ERROR)  [Replicator] {C4Replicator#3} Transient error (Network error 3, "connection timed out"); attempt #2 in 2 sec...

And then the callback:

SyncStatusUpdate for db /Users/Paul/.local/share/denote.cblite2/: changes 0, completed 0, status Connecting
Got an error! Couchbase.Lite.CouchbaseNetworkException: CouchbaseLiteException (NetworkDomain / 3): connection timed out.

Cheers.
Paul

Have sent the console app via message.

We’ve opened Loading... to track this

Thanks for opening the bug report and providing a link.

As an aside - I can’t figure out how that code works on any version of MacOS.
It’s attempting to get a pointer to a non-existent function name. It should
always throw.

Oh well… if y’all can make the exception go away, I’ll be happy.

Cheers.
Paul.