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
<PackageReference Include="RussiaRunning.FeatureFlags.Client" Version="1.1.0" />
<PackageVersion Include="RussiaRunning.FeatureFlags.Client" Version="1.1.0" />
<PackageReference Include="RussiaRunning.FeatureFlags.Client" />
paket add RussiaRunning.FeatureFlags.Client --version 1.1.0
#r "nuget: RussiaRunning.FeatureFlags.Client, 1.1.0"
#:package RussiaRunning.FeatureFlags.Client@1.1.0
#addin nuget:?package=RussiaRunning.FeatureFlags.Client&version=1.1.0
#tool nuget:?package=RussiaRunning.FeatureFlags.Client&version=1.1.0
RussiaRunning.FeatureFlags.Client
Клиентская библиотека для работы с публичным REST API фича-флагов
через API и интеграцию с ASP.NET Core 9+.
v1.1.0 — отказоустойчивость и скорость. Сбой сервера флагов больше никогда не валит бизнес-операцию: жёсткий таймаут, retry с джиттером, stale-while-revalidate, single-flight и настраиваемый fail-safe режим.
Содержание
- Что нового в v1.1.0
- Конфигурация
- Варианты подключения
- Использование
- Поведение при сбое сервера флагов
- Все опции клиента
Что нового в 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 | Versions 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. |
-
net9.0
- Microsoft.Extensions.Caching.Memory (>= 9.0.11)
- Microsoft.Extensions.Http (>= 9.0.11)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.11)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.