RussiaRunning.FeatureFlags.Client 1.1.0

dotnet add package RussiaRunning.FeatureFlags.Client --version 1.1.0
                    
NuGet\Install-Package RussiaRunning.FeatureFlags.Client -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RussiaRunning.FeatureFlags.Client" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RussiaRunning.FeatureFlags.Client" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="RussiaRunning.FeatureFlags.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RussiaRunning.FeatureFlags.Client --version 1.1.0
                    
#r "nuget: RussiaRunning.FeatureFlags.Client, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RussiaRunning.FeatureFlags.Client@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RussiaRunning.FeatureFlags.Client&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=RussiaRunning.FeatureFlags.Client&version=1.1.0
                    
Install as a Cake Tool

RussiaRunning.FeatureFlags.Client

Клиентская библиотека для работы с публичным REST API фича-флагов через API и интеграцию с ASP.NET Core 9+.

v1.1.0 — отказоустойчивость и скорость. Сбой сервера флагов больше никогда не валит бизнес-операцию: жёсткий таймаут, retry с джиттером, stale-while-revalidate, single-flight и настраиваемый fail-safe режим.

Содержание


Что нового в v1.1.0

Возможность Описание
Жёсткий per-request таймаут RequestTimeout (по умолчанию 2 с) применяется через CancellationTokenSource.CancelAfter. Сервер флагов не сможет «подвесить» вызывающую операцию ни при каких обстоятельствах.
Fail-safe режим По умолчанию FailureMode = ReturnStale. При недоступности сервера клиент возвращает устаревшее значение из кэша, а если его нет — null. FeatureGate сам подставит ваш @default.
Stale-while-revalidate После истечения свежести (CacheTtl) клиент мгновенно отдаёт старое значение и запускает фоновое обновление. Никаких холодных сетевых ожиданий на горячем пути.
Negative cache «Не найдено / null» тоже кэшируется (NegativeCacheTtl) — несконфигурированные флаги не штурмуют сервер.
Single-flight Параллельные обращения к одному ключу дожидаются одного сетевого запроса, а не N.
Retry с джиттером До MaxRetryAttempts повторов с экспоненциальным backoff на сетевые ошибки и 5xx / 408 / 429.
Стабильный batch-кэш Ключи в batch-кэше сортируются — порядок не ломает переиспользование.
Логирование Сбои логируются как Warning (с stale) / Error (если FailureMode = Throw).

Публичные интерфейсы IFeatureFlagsClient, IFeatureGate, IFeatureFlagsCache не изменились — апдейт 1.0.1 → 1.1.0 не требует правок кода.


Конфигурация

Минимальный набор:

{
  "FeatureFlags": {
    "BaseUrl": "https://flags.myhost.com",
    "EnvironmentKey": "prod",
    "DefaultTenantKey": "def_tenant_key"
  }
}

Полный пример с тонкой настройкой отказоустойчивости:

{
  "FeatureFlags": {
    "BaseUrl": "https://flags.myhost.com",
    "EnvironmentKey": "prod",
    "DefaultTenantKey": "def_tenant_key",

    "EnableCaching": true,
    "CacheTtl": "00:00:30",
    "StaleTtl": "00:10:00",
    "NegativeCacheTtl": "00:00:30",

    "RequestTimeout": "00:00:02",
    "MaxRetryAttempts": 2,
    "RetryBaseDelay": "00:00:00.100",

    "FailureMode": "ReturnStale",
    "EnableStaleWhileRevalidate": true
  }
}

Варианты подключения

Вариант 1 — через appsettings.json

using RussiaRunning.FeatureFlags.Client.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddFeatureFlagsClient(builder.Configuration.GetSection("FeatureFlags"));

var app = builder.Build();
app.Run();

Вариант 2 — программная настройка

builder.Services.AddFeatureFlagsClient(opts =>
{
    opts.BaseUrl          = "https://flags.myhost.com";
    opts.EnvironmentKey   = "staging";
    opts.DefaultTenantKey = "global";

    opts.RequestTimeout   = TimeSpan.FromSeconds(2);
    opts.CacheTtl         = TimeSpan.FromSeconds(30);
    opts.StaleTtl         = TimeSpan.FromMinutes(10);
    opts.FailureMode      = FeatureFlagsFailureMode.ReturnStale;
});

Вариант 3 — с собственным кэшем (например, Redis)

builder.Services.AddStackExchangeRedisCache(o => o.Configuration = "localhost:6379");

builder.Services.AddFeatureFlagsClient(
    builder.Configuration.GetSection("FeatureFlags"),
    cacheFactory: sp => new DistributedCacheAdapter(sp.GetRequiredService<IDistributedCache>())
);

Использование

public class CheckoutController(IFeatureGate flags) : ControllerBase
{
    [HttpGet("/checkout")]
    public async Task<IActionResult> Checkout(CancellationToken ct)
    {
        var enabled = await flags.IsEnabledAsync("checkout.enabled", @default: false, ct: ct);
        if (!enabled)
            return Forbid();

        var limit  = await flags.GetIntAsync("checkout.maxItems", 10, ct: ct);
        var theme  = await flags.GetStringAsync("ui.theme", "light", ct: ct);
        var config = await flags.GetValueAsync<CheckoutConfig>("checkout.config", tenantKey: "store-42", ct: ct);

        return Ok(new { enabled, limit, theme, config });
    }

    public record CheckoutConfig(string Mode, int MaxItems);
}

При недоступности сервера флагов этот код всё ещё работает — каждый вызов вернёт @default без исключений и без зависаний на минуту.


Поведение при сбое сервера флагов

FeatureFlagsFailureMode управляет поведением клиента после исчерпания retry-попыток:

Режим Поведение
ReturnStale (по умолчанию) Если в кэше есть устаревшая копия — вернуть её; иначе null / пустой batch. Исключений наружу нет.
ReturnDefault Всегда null / пустой batch при сбое. Исключений наружу нет.
Throw Старое поведение: пробросить исключение наверх. Использовать только для отладки/тестов.

Stale-копия живёт CacheTtl + StaleTtl (по умолчанию 30с + 10мин = ~10мин). Это «окно живучести» при полной недоступности сервера флагов.

Поток обработки одного вызова EvaluateAsync:

┌─ cache HIT (свежий) ─────────► return value
│
├─ cache HIT (stale)  ─────────► return value + fire-and-forget refresh
│
└─ cache MISS ────► single-flight ────► HTTP (timeout 2с, retry x2)
                                              │
                                       success ► cache + return
                                              │
                                       fail    ► FailureMode:
                                                  ReturnStale → stale | null
                                                  ReturnDefault → null
                                                  Throw → throw

Все опции клиента

Опция По умолчанию Назначение
BaseUrl — Базовый URL сервиса флагов. Обязательно.
EnvironmentKey — Ключ окружения (dev/staging/prod). Обязательно.
DefaultTenantKey null Дефолтный тенант, если не передан явно в вызове.
EnableCaching true Включить кэширование ответов.
CacheTtl 30 с Время свежести значения в кэше.
StaleTtl 10 мин Сколько ещё хранить устаревшее значение для stale-while-revalidate / ReturnStale.
NegativeCacheTtl 30 с TTL для «не найдено / null».
RequestTimeout 2 с Жёсткий таймаут одного HTTP-запроса.
MaxRetryAttempts 2 Сколько раз повторить запрос при сетевой ошибке / 5xx / 408 / 429. 0 — отключить.
RetryBaseDelay 100 мс Базовая задержка между ретраями (экспонента + джиттер 50%).
FailureMode ReturnStale Что делать после исчерпания retry.
EnableStaleWhileRevalidate true Возвращать stale-значение немедленно с фоновым обновлением.
Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 135 5/18/2026
1.0.1 156 1/30/2026
1.0.0 330 11/12/2025