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

Async support #28

Open
rakkum opened this issue Oct 23, 2020 · 1 comment
Open

Async support #28

rakkum opened this issue Oct 23, 2020 · 1 comment
Labels

Comments

@rakkum
Copy link

rakkum commented Oct 23, 2020

Is your feature request related to a problem? Please describe.
Since there are connections that are being established, the SDK should support async operations. Currently the documentation says that the constructor can block up to 10 seconds. That doesn't sound like good design. For the other operations it is not clear how long the flag evaluation might block if at all. Since its not async I would assume no blocking since its designed for server side operations.

Describe the solution you'd like
All operations and methods that might be blocking due to network, file, etc calls should make async calls available.

Describe alternatives you've considered
Not much that can be done there except requests being blocked. They also cannot be cancelled when initiated.

@eli-darkly
Copy link
Contributor

Hi. We've had requests for this before, ever since async/await became a common pattern in .NET, and we're definitely considering it as part of a future rewrite. There are some reasons why that's not a straightforward change to make, but first, I'll clarify what the current behavior is:

  • The maximum length of time that the constructor can block for is up to you; that's what the StartWaitTime configuration property is for. Ten seconds is the default value. If you don't want to block at all, but instead will just start it initializing in the background and check later for whether it has finished initializing, you can set that to zero.
  • The constructor is meant to only be called once during the lifetime of the application, as the SDK client is a long-lived component that maintains a lot of state. So that particular blocking condition is not something that would keep happening while servicing requests.
  • If you are using the default in-memory data store, then flag evaluation does not involve any I/O and does not block on any external resource.
  • If you are using a database store, such as Redis, then flag evaluation can block if a flag is requested that has not recently been read from the store; there is a read-through cache with a configurable TTL.
  • The LdClient.Close method will block until any pending analytics events have been delivered; that's deliberate, to give you a way to ensure that events have been sent if you're about to shut down. Like the constructor, that's an action that's normally only done once during the lifetime of the app.
  • No other SDK methods can block on any external resource.

Providing an async version of the constructor is probably feasible without major changes, but making flag evaluation async is a little more problematic. Because there are several points within the evaluation logic where another piece of data might need to be accessed on an as-needed basis (a prerequisite flag, or a user segment), there is no way to isolate the evaluation logic from the potential I/O operations ("potential" because, again, there's no actual I/O unless you're using a database). So the whole thing would need to be rewritten as async, and to support existing customers who are using it synchronously, we would need to wrap it in some "start a task and then block on the task" logic— which is a performance drag we'd prefer to avoid. There are various ways we could rearrange the code to address this, but some of them would require unsupporting some features that we currently support.

As for "no blocking since it's designed for server-side operations"— this is the design that all of our server-side SDKs have always used, except for Node.js where all I/O is by definition asynchronous. The in-memory caching layer works as follows: if a flag has expired from the cache and has to be reread from the database, all requests for the flag are coalesced so that the flag is only read once no matter how many tasks are evaluating it. All subsequent requests for that flag go to the cache until the TTL expires again.

@louis-launchdarkly louis-launchdarkly added the enhancement New feature or request label Nov 9, 2021
@tanderson-ld tanderson-ld transferred this issue from launchdarkly/dotnet-server-sdk Oct 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants