launchdarkly / dotnet-client-sdk

LaunchDarkly Client-side SDK for .NET
Other
7 stars 10 forks source link

Support for connectivity change in .NET Standard #39

Open raksanyi opened 2 years ago

raksanyi commented 2 years ago

Currently in .NET Standard there's no way to listen on connectivity changes as described in Connectivity.netstandard.cs. SetOffline is already part of the public API. What I would like to have is either:

The "SetOnline" solution seems to be the easiest change, but I don't know if it would break any other behaviour.

Would it be possible to implement a solution for .net standard? If not, are there any ways to force the LD client to go online whenever there's a network change?

eli-darkly commented 2 years ago

As far as we're aware, there is no API in .NET Standard 2.0 for finding out about the host machine's network connectivity state. That is an issue with the runtime— Microsoft just did not provide a portable mechanism for getting that information. We could have addressed this by providing builds for other platform-specific target frameworks, such as .NET Framework for Windows, or Xamarin for MacOS, but this had enough implementation difficulties (the SDK is currently built with msbuild and it is not possible to build for all of those target frameworks at once with that tool), and there had been so little demand (basically none) for desktop platform support from customers, that we didn't prioritize it.

Future versions of this SDK will likely have a target framework of .NET 6.0, and it may be that that runtime does have a portable API for detecting connectivity state— we haven't looked into it yet. But at present it's not an option.

eli-darkly commented 2 years ago

As for your other suggestion of a SetOnline method— I'm not sure what you are trying to accomplish that isn't already possible. If what you had in mind was that your application code would implement its own method of detecting connectivity state (presumably using platform-specific APIs that aren't available in .NET Standard), and on detecting a change, it would call this method to tell the SDK to stop trying to use the network, or to start trying again. But there already is a method that does exactly that: SetOffline.

And, regarding your third option, I can't think what you could accomplish by implementing your own IConnectivityStateManager that could not be accomplished by simply calling SetOffline.

As far as I know, the only difference between the SDK suspending network activity because SetOffline was called, and the SDK suspending network activity because (on a mobile platform) it detected that the network was unavailable, is that client.DataSourceStatusProvider.Status.State will report a slightly different value (DataSourceState.SetOffline as opposed to DataSourceState.NetworkUnavailable).

raksanyi commented 2 years ago

Thanks for your input. I've tried subscribing to DataSourceStatusProvider.StatusChanged but it never fired no matter how the network changed, even if I was explicitly calling SetOffline(false) whenever the network came back. My use-case is that I want to initialize the LdClient and call AllFlags() initially, and then call AllFlags() to be up to date with all the flags whenever the network is available again.

My expectation would be if I call SetOffline(false) then StateChanged will fire, so I can update the local cache with AllFlags(). Could you show me an example on how to get all flags at once to update the local LdClient cache?

eli-darkly commented 2 years ago

I've tried subscribing to DataSourceStatusProvider.StatusChanged but it never fired no matter how the network changed, even if I was explicitly calling SetOffline(false) whenever the network came back.

Hmm... that may be a bug. My understanding of this logic was that it would fire for any transition in the state, regardless of what caused it. I'll try to reproduce this.

However, on a different point: in this SDK, I'm not sure there is much value in calling AllFlags() to "cache" the flag values. Calling one of the Variation methods does not cause any network requests to happen and it doesn't cause the flags to be re-evaluated from scratch; it's just returning one of the values that was previously received from LD. In other words, they are already cached.

raksanyi commented 2 years ago

So what you're saying is if I call SetOffline(false), the SDK will try to reconnect and if it succeeds, makes a request to the LD servers and caches the updated values? In other words, do I have to do anything to get fresh data once I call SetOffline(false)?

I would want a clear image on from where did the SDK provide me a flag. If I setup persistence with .Persistence(Components.Persistence()) in .NET Standard that will be IsolatedStorage. From this point can you confirm this is how the SDK evaluates flags? For example by calling ldClient.BoolVariation("flagKey"):

  1. If the underlying datastore has the flag in the in-memory cache, return that value
  2. If it's not in the in-memory cache, try to load it from IsolatedStorage
  3. If IsolatedStorage fails, make a new request to the LD servers to get the value
eli-darkly commented 2 years ago

So what you're saying is if I call SetOffline(false), the SDK will try to reconnect and if it succeeds, makes a request to the LD servers and caches the updated values?

That's right.

Any time it is in a state of either "I'm aware that the network isn't available, because this is a phone and the phone OS has told me the network isn't available", or "the application has told me to stay offline by calling SetOffline(true)", or both, it will not try to connect or reconnect. Any time it transitions to a state where neither of those things is true, it tries to connect, just as it would if you had started it in such a state. And any successful connection results in it getting and storing the last data. That's what the API documentation means when it says this:

If you set it to false when it was previously true, and the network is available, the SDK will attempt to connect to LaunchDarkly.

And in that case, SetOffline does not return (unless it times out), and SetOfflineAsync's task does not complete, until the connection has been made and the new data has been obtained, so you know that any calls to the Variation methods after that point will be using the new data.

eli-darkly commented 2 years ago

I would want a clear image on from where did the SDK provide me a flag. If I setup persistence with .Persistence(Components.Persistence()) in .NET Standard that will be IsolatedStorage. From this point can you confirm > this is how the SDK evaluates flags? For example by calling ldClient.BoolVariation("flagKey"): If the underlying datastore has the flag in the in-memory cache, return that value If it's not in the in-memory cache, try to load it from IsolatedStorage If IsolatedStorage fails, make a new request to the LD servers to get the value

No, that's not correct. Here is what happens:

raksanyi commented 2 years ago

So in the end all I need to do is just to call SetOffline(false) and I'll get up to date data all the time. Thanks for the explanation, this clears up a few things!

eli-darkly commented 2 years ago

That's how it should work, yes. I'll still look into the possible issue of the DataSourceStatusProvider.StatusChanged event not firing, but the only reason you should need to pay any attention to that event is if you want to do other kinds of things based on the fact that the SDK is or isn't currently connected; you don't need an event listener just to re-request flags, because that part happens transparently.

raksanyi commented 2 years ago

Any updates on the StatusChanged event? I'm getting very inconsistent behaviour. The other issue I have is that LdClient.Init(...) will create the ldclient instance and call Start(...) on it, which will trigger the StateChanged event if it's successful, but I can't subscribe to the event only after Init has finished and I have an actual object, so initially I won't get the Valid state change.

eli-darkly commented 2 years ago

@raksanyi I'm sorry that I haven't looked into it yet. I had not made it a high priority because it sounded like you did not actually need to rely on the StatusChanged event after all to do what you wanted to do, but maybe I misread your comments.

eli-darkly commented 2 years ago

I also don't quite understand why you need to see the initial Valid state change. I mean, if you've just initialized a client, you can look at client.DataSourceStatusProvider.Status to see what the status is right away.

raksanyi commented 2 years ago

client.DataSourceStatusProvider.Status helped checking if ldclient is online, thank you. I've been experimenting with subscribing to DataSourceStatusProvider.StatusChanged and I found that it's not triggering if I only call SetOffline(false, timeout). When internet is reachable, I call SetOffline(false, timeout), but when it's not available I found that I have to explicitly call SetOffline(true, timeout) and then it's going to fire the StatusChanged event. I believe this is because of this check here: https://github.com/launchdarkly/dotnet-client-sdk/blob/master/src/LaunchDarkly.ClientSdk/Internal/DataSources/ConnectionManager.cs#L119-L122. Is this the intended behaviour?