-
Notifications
You must be signed in to change notification settings - Fork 8
chore: adds composite datasource #187
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
base: main
Are you sure you want to change the base?
Conversation
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/CompositeSource.cs
Show resolved
Hide resolved
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/CompositeSource.cs
Show resolved
Hide resolved
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/CompositeSource.cs
Outdated
Show resolved
Hide resolved
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/CompositeSource.cs
Outdated
Show resolved
Hide resolved
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/CompositeSource.cs
Show resolved
Hide resolved
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/CompositeSource.cs
Show resolved
Hide resolved
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/CompositeSource.cs
Outdated
Show resolved
Hide resolved
|
|
||
| // Start the source asynchronously and complete the task when it finishes. | ||
| // We do this outside the lock to avoid blocking the queue. | ||
| dataSourceToStart.Start().ContinueWith(task => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ContinueWith is maybe a little scary. I think we may need to specify which scheduler to use, versus depending on its default behavior. I also don't know if it may be important to make sure it doesn't attach. Basically internal to the SDK we want the behavior of ConfigureAwait(false), even if we aren't interacting with the promise that way.
Task.Run on an async method that does await may be more understandable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this?
// Start the source asynchronously and complete the task when it finishes.
// We do this outside the lock to avoid blocking the queue.
_ = Task.Run(async () =>
{
try
{
var result = await dataSourceToStart.Start();
tcs.TrySetResult(result);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Committed the Task.Run solution, let me know if that isn't what you were thinking of.
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/DataSourceUpdatesSanitizer.cs
Show resolved
Hide resolved
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/FanOutDataSourceUpdates.cs
Show resolved
Hide resolved
|
|
||
| // report state Off | ||
| _sanitizedUpdateSink.UpdateStatus(DataSourceState.Off, null); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Disposed composite source can create new data sources
The Dispose() method clears _pendingActions and resets state, but there's a race window where an already-dequeued action can execute after Dispose() completes. Since Dispose() calls _sourcesList.Reset() and sets _currentDataSource to null, a subsequent GoToNext() or GoToFirst() action that was dequeued before disposal can invoke TryFindNextUnderLock(), which will create a new data source. This new data source will never be disposed, causing a resource leak, and will trigger status updates after Off was already reported, causing state inconsistency. A disposed flag could prevent TryFindNextUnderLock() from creating new sources post-disposal.
Additional Locations (1)
pkgs/sdk/server/src/Internal/DataSources/CompositeDataSource/CompositeSource.cs
Show resolved
Hide resolved
| { | ||
| try | ||
| { | ||
| var result = await dataSourceToStart.Start(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| var result = await dataSourceToStart.Start(); | |
| var result = await dataSourceToStart.Start().ConfigureAwait(false); |
The ConfigureAwait(false) means we don't care about the synchronization context we run on. It can be important if the code runs in something like if it was running in an HTTP context in ASP, or in an environment with winforms. The rule of thumb is basically to always do this, unless you know you need the same synchronization context, which is hardly ever the case.
Though in the client-side SDK it could be, if you were trying to do UI thread things being called from the UI thread. Which removes the need to like schedule something onto that thread, if you know the task will return on that thread.
I think this method specifically wouldn't matter, because you can never get to the entrypoint from caller code. Generally we should probably always use this setting. The reason I didn't want ContinueWith is because achieving the exact same thing is more complicated.
kinyoklion
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check comment for ConfigureAwait feedback.
Requirements
I have added test coverage for new or changed functionality
I have followed the repository's pull request submission guidelines
I have validated my changes against all supported platform versions
This may not be possible until more FDv2 logic is available.
Related issues
SDK-1636
Describe the solution you've provided
Adds composite datasource that can be set up with tuples of datasource factories and action appliers. Action appliers transform DataSourceUpdates invocations into actions that change the composite data source.
This PR does not include the definitions of how initializers fallback to synchronizers, that will come in the next PR.
Note
Introduce
CompositeSourcethat can switch among multiple sources via factories and action appliers, with fan-out, sanitization, disableable update proxies, and comprehensive tests.CompositeSource(IDataSource,ICompositeSourceActionable) to dynamically switch among sources with queued, serialized actions (start/next/first/blacklist/dispose).DataSourceUpdatesSanitizer(dedupes status, mapsOff→Interrupted).FanOutDataSourceUpdates(fan-out to multipleIDataSourceUpdates).DisableableDataSourceUpdatesTracker(wrappable, disableable update proxies).SourcesList<T>(iterable/removable source list with optional circular iteration).ISourceFactory,IActionApplier,IActionApplierFactory,ICompositeSourceActionable.CompositeSourceTest: verifies fallback onInterrupted, blacklisting onOff, and blocking updates from disabled sources.SourcesListTest: verifies replace/remove/cycling behavior.Written by Cursor Bugbot for commit 73cbd40. This will update automatically on new commits. Configure here.