Skip to content

Commit

Permalink
Prepare v8.0.0-preview1 release (#67)
Browse files Browse the repository at this point in the history
* Reduce memory allocation in async hot paths (where sync completion is likely)

* Back IConfigCatLogger.LogLevel with a volatile field as it may be accessed from multiple threads

* Revise XML documentation to improve consistency and wording (with special regard to terminology)

* Enable Source Link and deterministic build

* Get rid of redundant release notes in CHANGELOG.md

* Clean up usings

* Remove unused code

* Reduce more memory allocation in async paths where sync completion is possible

* Fix bug causing constant refresh in the case of flag overrides using simplified config

* Slightly change flag override behavior so an unsupported value doesn't prevent other overrides + add tests

* Fix finalizer-related tests

* Update version
  • Loading branch information
adams85 authored May 26, 2023
1 parent 0bda27f commit 775aa17
Show file tree
Hide file tree
Showing 60 changed files with 453 additions and 525 deletions.
206 changes: 1 addition & 205 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,205 +1 @@
### 7.2.0

- Introduced a new local dictionary override factory method:
```csharp
FlagOverrides LocalDictionary(IDictionary<string, object> dictionary, bool watchChanges, OverrideBehaviour overrideBehaviour)
```
Where the `watchChanges` parameter indicates whether the SDK should rebuild the overrides upon each read to keep track of the source dictionary's changes.
- Fix config fetcher-related error logging to include exception in the log if any.

### 7.1.0

- Add new evaluation methods `GetAllValueDetails`/`GetAllValueDetailsAsync`.
- Fix logging in `ConfigServiceBase.SetOnline`.
- Correct behavior of `GetAllXXX` methods so `FlagEvaluated` event is also raised in case of error.
- Correct reporting of "Config JSON is not present" errors and log them with error level also in the case of `GetAllXXX` methods.
- Change implementation of `HttpConfigFetcher.FetchAsync` to execute only one fetch operation at a time.
- Make `ProjectConfig` equality comparison consistent with other SDKs (treats `ProjectConfig` instances with the same ETag equal regardless of actual content).
- Make HTTP response handling consistent with other SDKs.
- Make `HttpConfigFetcher`-related error message consistent with other SDKs.

### 7.0.0
- Deprecate `ConfigCatClient` constructors in favor of the new static factory method `Get`,
which provides single client instances per SDK key.
- Add convenience method `DisposeAll` for disposing all open clients at once.
- Implement default user feature.
- Implement offline mode feature.
- Improve LazyLoad and AutoPoll refresh logic by taking the cache timestamp into account
to fetch the config only if cached config is unavailable or stale.
- Add new evaluation methods `GetValueDetail`/`GetValueDetailsAsync`,
which provide more detailed information about the evaluation result.
- Add hooks (events), which provide notifications of the client's actions.
- Additional minor code quality and performance improvements.
- Update samples to .NET 6.

### 6.5.3
- Use logger wrapper everywhere internally. #39
- Improved evaluation logging. #38

### 6.5.2
- Consolidate percentage rule evaluation logs.

### 6.5.1
- Add net461 to the target frameworks list to force the usage of `System.Text.Json` rather than `Newtonsoft.Json`.

### 6.5.0
- Replace `FileSystemWatcher` with file polling in local file override data source.

### 6.4.12
- Fix various local file override data source issues.

### 6.4.9
- Move the PollingMode option to public scope.

### 6.4.8
- Readd `System.Text.RegularExpressions` version `4.3.1` due to SNYK security report.

### 6.4.7
- Remove unused `System.Text.RegularExpressions` dependency.

### 6.4.6
- Fix the wait time calculation in auto-polling mode.

### 6.4.3
- Fix README links displayed on the NuGet package page.

### 6.4.0
- **Introduced a new configuration API replacing the builder pattern**:

```cs
ConfigCatClientBuilder
.Initialize(SDKKEY)
.WithLogger(consoleLogger)
.WithAutoPoll()
.WithMaxInitWaitTimeSeconds(5)
.WithPollIntervalSeconds(60)
.Create();
```

Will look like this:
```cs
new ConfigCatClient(options =>
{
options.SdkKey = SDKKEY;
options.PollingMode = PollingModes.AutoPoll(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(5));
options.Logger = consoleLogger;
});
```

The old API is still available it's just marked with the `[Obsolete]` attribute.

- **`GetAllValues()` and `GetAllValuesAsync()`**:
These methods are now evaluating all feature flags and settings into an `IDictionary<string, object>`.

- **FlagOverrides**:
It's now possible to feed the SDK with local feature flag and setting values.
- **Dictionary**
```cs
var dict = new Dictionary<string, object>
{
{"enabledFeature", true},
{"intSetting", 5},
};

using var client = new ConfigCatClient(options =>
{
options.SdkKey = "localhost";
options.FlagOverrides = FlagOverrides.LocalDictionary(dict,
OverrideBehaviour.LocalOnly);
});
```
- **File**
```cs
using var client = new ConfigCatClient(options =>
{
options.SdkKey = "localhost";
options.FlagOverrides = FlagOverrides.LocalFile("path/to/file",
autoReload: false,
overrideBehaviour: OverrideBehaviour.LocalOnly);
});
```
Three behaviours available: `LocalOnly`, `LocalOverRemote`, and `RemoteOverLocal`.
With `LocalOnly` the SDK switches into a complete offline state, and only the override values are served.
`LocalOverRemote` and `RemoteOverLocal` merge the local and remote feature flag values respecting one or another in case of key duplications.

- **Changes in JSON handling**:
In respect of [#30](https://github.com/configcat/.net-sdk/issues/30) `System.Text.Json` is favored over `Newtonsoft.Json` in frameworks newer than `net45`. `System.Text.Json` is not available for `net45` so that target remains using `Newtonsoft.Json`.
- **`net5.0` and `net6.0` target frameworks**.

- **HttpTimeout configuration option**.

- **Solution for** [#26](https://github.com/configcat/.net-sdk/issues/26).
To prevent possible deadlocks the following changes were applied:
- Created a synchronous extension for the existing fully async `IConfigCache`. In the future we will replace that interface with the new one (`IConfigCatCache`) that has now the sync API and inherits the async API from `IConfigCache`. `IConfigCache` was marked with `[Obsolete]` to maintain backward compatibility. `InMemoryConfigCache` now implements both sync and async APIs through `IConfigCatCache`.
- Extended the config services (`AutoPoll`, `LazyLoad`, `ManualPoll`) with synchronous branches that are using the new cache's sync / async methods in respect of sync and async customer calls.
- Extended the `HttpConfigFetcher` with a synchronous `Fetch` that uses the `HttpClient`'s `Send()` method where it's available (`net5.0` and above). Below `net5.0` the synchronous path falls back to a functionality that queues the HTTP request to a thread pool thread and waits for its completion. This solution prevents deadlocks however, it puts more load on the thread pool.

- **CI Changes**:
- Introduced new [GitHub actions for Linux and macOS builds](https://github.com/configcat/.net-sdk/actions/workflows/linux-macOS-CI.yml).
- The [sonarcloud analysis](https://github.com/configcat/.net-sdk/actions/workflows/sonar-analysis.yml) is moved to a separate Action from the appveyor task.
- Removed codecov completely, it will be replaced by the coverage data from sonarcloud.

### 6.2.1
- Reducing the number of json deserializations between `GetValue` calls.
### 6.1.20
- Bugfix: The SDK's json serialization behavior is not depending on the `JsonConvert.DefaultSettings` anymore.
### 6.1.0
- Bugfix ([#17](https://github.com/configcat/.net-sdk/issues/17))
### 6.0.0
- Addressing global data handling and processing trends via Data Governance feature. Customers can control the geographic location where their config JSONs get published to. [See the docs](https://configcat.com/docs/advanced/data-governance/).
We are introducing a new DataGovernance initialization parameter. Set this parameter to be in sync with the Data Governance preference on the [Dashboard](https://app.configcat.com/organization/data-governance).
#### Breaking changes:
### 7.1.0
- [API] Remove `BeforeClientDispose` hook
### 7.0.0
- [API] Add new methods to the `IConfigCatClient` interface.
- [API] Change `ProjectConfig` to reference type with value equality (record).
- [Behavior] Slightly changes the behavior of ProjectConfig.TimeStamp (only updated when communication with the CDN servers succeeds, regardless of the returned status code.)
### 6.0.0
- Custom cache implementations should implement the new cache interface using key parameter in the get/set methods.
### 5.3.0
- VariationID, bugfix ([#11](https://github.com/configcat/.net-sdk/issues/11))
### 5.2.0
- Bugfix (config fetch, caching)
### 5.1.0
- Remove semver nuget packages
### 5.0.0
- Breaking change: Renamed `API Key` to `SDK Key`.
### 4.0.0
- Supporting sensitive text comparators.
### 3.2.0
- Minor fix in info level logging
### 3.1.0
- Added new semantic version tests
### 3.0.0
- Support new types (number, semver), detailed log entries, compressed http communication
### 2.5.0
- Support custom HttpClientHandler
### 2.4.0
- Add GetAllKeys() function
### 2.3.0
- BaseUrl override oppurtunity
- IConfigCache override oppurtunity
### 2.3.0
- BaseUrl override oppurtunity
- IConfigCache override oppurtunity
### 2.2.1
- Bugfix (logger level)
### 2.2.0
- Namespace unification
### 2.1.0
- Rollout handling v2
### 2.0.1
- Bugfix
### 2.0.0
- Implement rollout feature
### 1.0.7
- Implement LazyLoad, AutoPoll, ManualPoll feature
### 1.0.6
- Finalize logging
### 1.0.5
- Implement tracing, add clear cache ability to client
### 1.0.4
- Initial release
Please check the [Github Releases](https://github.com/configcat/.net-sdk/releases) page for the changelog of the ConfigCat SDK for .NET.
11 changes: 5 additions & 6 deletions DEPLOY.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# Steps to Deploy
1. Run tests
2. Set version in `appveyor.yml` (e.g: from `build_version: 6.5.0` to `build_version: 6.5.1`)
3. Update release notes in CHANGELOG.md
4. Open a PR
5. When the PR is merged to master, start a deploy to NuGet.org
3. Open a PR
4. When the PR is merged to master, start a deploy to NuGet.org

Create a new deployment on https://ci.appveyor.com/project/configcat/net-sdk/deployments
6. Make sure new package is available via Nuget.org: https://www.nuget.org/packages/ConfigCat.Client
7. Update and test sample apps with the new SDK version.
5. Make sure new package is available via Nuget.org: https://www.nuget.org/packages/ConfigCat.Client
6. Update and test sample apps with the new SDK version.

*Usually it takes a few minutes to propagate.*
8. Add release notes: https://github.com/configcat/.net-sdk/releases
7. Add release notes: https://github.com/configcat/.net-sdk/releases
9 changes: 5 additions & 4 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
environment:
build_version: 7.2.0
build_version: 8.0.0
version: $(build_version)-{build}
image: Visual Studio 2022
configuration: Release
Expand All @@ -11,10 +11,10 @@ dotnet_csproj:
patch: true
file: '**\*.csproj'
version: $(build_version)
package_version: $(build_version)
package_version: $(build_version)-preview1
assembly_version: $(build_version)
file_version: $(build_version)
informational_version: $(build_version)
informational_version: $(build_version)-preview1
install:
- cmd: dotnet tool install -g InheritDocTool
build_script:
Expand All @@ -24,9 +24,10 @@ build_script:
after_build:
- cmd: echo __PACK__
- inheritdoc -o
- dotnet pack -c %configuration% /p:IncludeSymbols=true /p:PackageOutputPath=..\..\artifacts /p:SymbolPackageFormat=snupkg src\ConfigCatClient\ConfigCatClient.csproj
- dotnet pack -c %configuration% /p:PackageOutputPath=..\..\artifacts /p:ContinuousIntegrationBuild=true src\ConfigCatClient\ConfigCatClient.csproj
test_script:
- dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -f net45 -c %configuration% --no-build
- dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -f net461 -c %configuration% --no-build
- dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -f netcoreapp3.1 -c %configuration% --no-build
- dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -f net5.0 -c %configuration% --no-build
- dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -f net6.0 -c %configuration% --no-build
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ public ConfigCatToMSLoggerAdapter(ILogger<ConfigCat.Client.ConfigCatClient> logg
}

// Allow all log levels here and let MS logger do log level filtering (see appsettings.json)
public ConfigCat.Client.LogLevel LogLevel { get; set; } = ConfigCat.Client.LogLevel.Debug;
public ConfigCat.Client.LogLevel LogLevel
{
get => ConfigCat.Client.LogLevel.Debug;
set { }
}

public void Log(ConfigCat.Client.LogLevel level, ConfigCat.Client.LogEventId eventId, ref ConfigCat.Client.FormattableLogMessage message, Exception? exception = null)
{
Expand Down
11 changes: 9 additions & 2 deletions samples/FileLoggerSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ class Program
{
class MyFileLogger : IConfigCatLogger
{
private readonly string filePath;
private static readonly object SyncObj = new object();

public LogLevel LogLevel { get; set; }
private readonly string filePath;

private volatile LogLevel logLevel;

public LogLevel LogLevel
{
get => this.logLevel;
set => this.logLevel = value;
}

public MyFileLogger(string filePath, LogLevel logLevel)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
using System;
using System.CodeDom;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using ConfigCat.Client.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace ConfigCat.Client.Tests;

Expand Down
3 changes: 0 additions & 3 deletions src/ConfigCat.Client.Tests/BasicConfigEvaluatorTests.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using ConfigCat.Client.Evaluation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace ConfigCat.Client.Tests;

Expand Down
4 changes: 2 additions & 2 deletions src/ConfigCat.Client.Tests/ConfigCacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,14 @@ public async Task ConfigCache_Works(bool isExternal, bool isAsync)

Task<ProjectConfig> ReadCacheAsync()
{
return isAsync ? configCache.GetAsync(cacheKey) : Task.FromResult(configCache.Get(cacheKey));
return isAsync ? configCache.GetAsync(cacheKey).AsTask() : Task.FromResult(configCache.Get(cacheKey));
}

Task WriteCacheAsync(ProjectConfig config)
{
if (isAsync)
{
return configCache.SetAsync(cacheKey, config);
return configCache.SetAsync(cacheKey, config).AsTask();
}
else
{
Expand Down
2 changes: 0 additions & 2 deletions src/ConfigCat.Client.Tests/ConfigCatClientCacheTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using ConfigCat.Client.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Expand Down
14 changes: 10 additions & 4 deletions src/ConfigCat.Client.Tests/ConfigCatClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,9 @@ public void DisposeAll_CachedInstancesRemoved()

instanceCount1 = ConfigCatClient.Instances.GetAliveCount();

GC.KeepAlive(client1);
GC.KeepAlive(client2);

ConfigCatClient.DisposeAll();

var instanceCount2 = ConfigCatClient.Instances.GetAliveCount();
Expand Down Expand Up @@ -1284,6 +1287,9 @@ static void CreateClients(out int instanceCount)
var client2 = ConfigCatClient.Get("test2", options => options.PollingMode = PollingModes.ManualPoll);

instanceCount = ConfigCatClient.Instances.GetAliveCount();

GC.KeepAlive(client1);
GC.KeepAlive(client2);
}

// Act
Expand Down Expand Up @@ -1801,14 +1807,14 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}

public Task<ProjectConfig> GetConfigAsync(CancellationToken cancellationToken = default)
public ValueTask<ProjectConfig> GetConfigAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult(ProjectConfig.Empty);
return new ValueTask<ProjectConfig>(ProjectConfig.Empty);
}

public override Task<RefreshResult> RefreshConfigAsync(CancellationToken cancellationToken = default)
public override ValueTask<RefreshResult> RefreshConfigAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult(RefreshConfig());
return new ValueTask<RefreshResult>(RefreshConfig());
}

public ProjectConfig GetConfig()
Expand Down
Loading

0 comments on commit 775aa17

Please sign in to comment.