Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for connectivity change in .NET Standard #59

Open
raksanyi opened this issue Mar 9, 2022 · 13 comments
Open

Support for connectivity change in .NET Standard #59

raksanyi opened this issue Mar 9, 2022 · 13 comments

Comments

@raksanyi
Copy link

raksanyi commented Mar 9, 2022

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:

  • Subscribe to a connectivity changed event (for this I suppose the Connectivity needs to be implemented for netstandard)
  • Have a "SetOnline" method on the ILdClient interface (which would call SetNetworkEnabled internally)
  • Have the option to add my custom IConnectivityStateManager implementation through the ConfigurationBuilder (for this, I imagine a few interfaces and classes would need to be changed to not be internal) so that I can manage and implement my own ConnectionChanged event

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
Copy link
Contributor

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
Copy link
Contributor

eli-darkly commented Mar 9, 2022

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
Copy link
Author

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
Copy link
Contributor

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
Copy link
Author

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
Copy link
Contributor

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
Copy link
Contributor

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:

  • When you start the SDK, it checks persistent storage and if data is present there, it loads it into memory at that point. (Note, you do not need to call .Persistence(Components.Persistence()) for this to be enabled: it is enabled by default, that configuration method is for if you want to change its behavior or disable it.)
  • Also at start time, unless you have to told it to stay offline or the network is unavailable, it makes an HTTP request to LD. If successful, it stores the flags in memory and also in persistent storage.
  • The HTTP request is normally a long-lived streaming connection, so if any flags are changed, LD sends an update message over that connection. The SDK stores the update in memory, and updates persistent storage at the same time.
  • If the streaming connection ever breaks and then is re-established, LD always sends a full set of the current flags at that point in case any updates might have been missed. Again, LD updates both memory and persistent storage at that point.
  • Calls to the Variation methods, or to AllFlags, always use the in-memory values. They never cause an on-demand request to LD, and they never cause a fresh read from persistent storage.

@raksanyi
Copy link
Author

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
Copy link
Contributor

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
Copy link
Author

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
Copy link
Contributor

@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
Copy link
Contributor

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
Copy link
Author

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?

@cwaldren-ld cwaldren-ld transferred this issue from launchdarkly/dotnet-client-sdk Dec 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants