diff --git a/docs/concepts/metadata.md b/docs/concepts/metadata.md new file mode 100644 index 000000000..32b6dd5b2 --- /dev/null +++ b/docs/concepts/metadata.md @@ -0,0 +1,177 @@ +# Metadata + +Metadata in flagd provides contextual information about flags and flag sets. +It enables rich observability, logical separation, and debugging capabilities. + +## Overview + +Flagd supports metadata at two levels: + +- **Flag Set-Level Metadata**: Applied to entire flag configurations +- **Flag-Level Metadata**: Applied to individual flags + +## Metadata Inheritance + +Flagd uses a hierarchical metadata system where flags inherit metadata from their containing flag set, with the ability to override specific values at the flag level. + +### Flag Set-Level Metadata + +The most common pattern is defining metadata at the configuration level, where all flags inherit it: + +```json +{ + "metadata": { + "flagSetId": "payment-service", + "team": "payments", + "version": "v1.2.0", + "environment": "production" + }, + "flags": { + "checkout-flow": { + "state": "ENABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "on" + // Inherits all set-level metadata + }, + "payment-gateway": { + "state": "DISABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "off" + // Also inherits all set-level metadata + } + } +} +``` + +### Flag-Level Overrides + +Individual flags can override inherited metadata or add flag-specific metadata: + +```json +{ + "metadata": { + "flagSetId": "payment-service", + "team": "payments", + "version": "v1.2.0" + }, + "flags": { + "standard-feature": { + "state": "ENABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "on" + // Inherits: flagSetId="payment-service", team="payments", version="v1.2.0" + }, + "experimental-feature": { + "metadata": { + "flagSetId": "experiments", // Override: different flag set + "owner": "research-team", // Addition: flag-specific metadata + "experimental": true // Addition: flag-specific metadata + // Still inherits: team="payments", version="v1.2.0" + }, + "state": "DISABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "off" + } + } +} +``` + +### Inheritance Behavior + +1. **Default Inheritance**: Flags inherit all set-level metadata +2. **Selective Override**: Flag-level metadata overrides specific inherited values +3. **Additive Enhancement**: Flag-level metadata can add new keys not present at set level +4. **Preserved Inheritance**: Non-overridden set-level metadata remains inherited + +## Metadata Reflection + +Metadata reflection provides transparency by echoing selector and configuration information back in API responses. This enables debugging, auditing, and verification of flag targeting. + +### Selector Reflection + +When making requests with selectors, flagd "reflects" the parsed selector information in the "top-level" `metadata` field: + +**Request:** + +```bash +curl -H "Flagd-Selector: flagSetId=payment-service" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +**Response includes reflected metadata:** + +```json +{ + "flags": { + "checkout-flow": { + "key": "checkout-flow", + "value": true, + "variant": "on", + "metadata": { + "flagSetId": "payment-service", + "team": "payments" + } + } + }, + "metadata": { + "flagSetId": "payment-service" // Reflected from selector + } +} +``` + +### Configuration Reflection + +Flag evaluation responses include the complete merged metadata for each flag: + +```json +{ + "key": "experimental-feature", + "value": false, + "variant": "off", + "metadata": { + "flagSetId": "experiments", // Overridden at flag level + "owner": "research-team", // Added at flag level + "experimental": true, // Added at flag level + "team": "payments", // Inherited from set level + "version": "v1.2.0" // Inherited from set level + } +} +``` + +## Common Metadata Fields + +### Standard Fields + +Some metadata fields are defined in the flag-definition schema for common use-cases: + +- **`flagSetId`**: Logical grouping identifier for selectors +- **`version`**: Configuration or flag version + +### Custom Fields + +You can define any custom metadata fields relevant to your use case: + +```json +{ + "metadata": { + "flagSetId": "user-service", + "version": "v34", + "costCenter": "engineering", + "compliance": "pci-dss", + "lastReviewed": "2024-01-15", + "approver": "team-lead" + } +} +``` + +## Use Cases + +**Debugging**: Metadata reflection shows which selectors were used and how inheritance resolved, making it easier to troubleshoot flag targeting issues. + +**Governance**: Track team ownership, compliance requirements, and approval workflows through custom metadata fields. + +**Environment Management**: Use metadata for version tracking, environment identification, and change management across deployments. + +**Multi-Tenancy**: Isolate tenants through flag sets and maintain tenant-specific configurations and governance. + +**Observability**: Metadata attributes can be used in telemetry spans and metrics, providing operational visibility into flag usage patterns and configuration context. diff --git a/docs/concepts/selectors.md b/docs/concepts/selectors.md new file mode 100644 index 000000000..df79d9d43 --- /dev/null +++ b/docs/concepts/selectors.md @@ -0,0 +1,126 @@ +# Selectors + +Selectors are query expressions that allow you to filter flag configurations from flagd's sync service. They enable providers to request only specific subsets of flags instead of receiving all flags, making flagd more efficient and flexible for complex deployments. + +## Overview + +In flagd, **selectors** provide a way to query flags based on different criteria. This is particularly powerful because flagd decouples **flag sources** from **flag sets**, allowing for more granular control over which flags are synchronized and evaluated. + +### Key Concepts + +- **Flag Source**: Where flag configuration data comes from (file, HTTP endpoint, gRPC service, etc.) +- **Flag Set**: A logical grouping of flags identified by a `flagSetId` +- **Selector**: A query expression that filters flags by source, flag set, or other criteria +- **Flag Set Metadata**: The selector information is "reflected" back in response metadata for transparency + +## Source vs Flag Set Decoupling + +### Before: Tight Coupling + +Historically, each source provided exactly one flag set, and providers had to target specific sources: + +```yaml +# Old approach - targeting a specific source +selector: "my-flag-source.json" +``` + +### After: Flexible Flag Sets + +Now, sources and flag sets are decoupled. A single source can contain multiple flag sets, and flag sets can span multiple sources: + +```yaml +# New approach - targeting a logical flag set +selector: "flagSetId=project-42" +``` + +## Flag Set Configuration + +Flag sets are typically configured at the top level of a flag configuration, with all flags in that configuration inheriting the same `flagSetId`. This is the recommended approach for most use cases. + +### Set-Level Configuration + +The most common pattern is to set the `flagSetId` at the configuration level, where all flags inherit it: + +```json +{ + "metadata": { + "flagSetId": "payment-service", + "version": "v1.2.0" + }, + "flags": { + "new-checkout-flow": { + "state": "ENABLED", + "variants": { + "on": true, + "off": false + }, + "defaultVariant": "on" + }, + "stripe-integration": { + "state": "DISABLED", + "variants": { "on": true, "off": false }, + "defaultVariant": "off" + } + } +} +``` + +In this example, both `new-checkout-flow` and `stripe-integration` flags belong to the `payment-service` flag set. + +### Metadata Integration + +Selectors work closely with flagd's metadata system. For advanced patterns like flag-level overrides of `flagSetId` or complex metadata inheritance, see the [Metadata concepts](metadata.md) section. + +## Metadata Reflection + +When you make a request with a selector, flagd "reflects" the selector information back in the response metadata for transparency and debugging. For complete details on metadata selector reflection, inheritance, and configuration patterns, see the [Metadata concepts](metadata.md) section. + +## Use Cases + +### Multi-Tenant Applications + +```yaml +# Tenant A's flags +selector: "flagSetId=tenant-a" + +# Tenant B's flags +selector: "flagSetId=tenant-b" +``` + +### Environment Separation + +```yaml +# Development environment +selector: "flagSetId=dev-features" + +# Production environment +selector: "flagSetId=prod-features" +``` + +### Feature Team Isolation + +```yaml +# Payment team's flags +selector: "flagSetId=payments" + +# User interface team's flags +selector: "flagSetId=ui-components" +``` + +### Legacy Source-Based Selection + +```yaml +# Still supported for backward compatibility +selector: "source=legacy-config.json" +``` + +## Best Practices + +1. **Use Flag Sets for Logical Grouping**: Prefer `flagSetId` over `source` for new deployments +2. **Plan Your Flag Set Strategy**: Design flag sets around logical boundaries (teams, features, environments) +3. **Leverage Metadata**: Use metadata for debugging and auditing +4. **Document Your Schema**: Clearly document your flag set naming conventions for your team + +## Migration Considerations + +The selector enhancement maintains full backward compatibility. See the [migration guide](../guides/migrating-to-flag-sets.md) for detailed guidance on transitioning from source-based to flag-set-based selection patterns. diff --git a/docs/guides/migrating-to-flag-sets.md b/docs/guides/migrating-to-flag-sets.md new file mode 100644 index 000000000..90ec10a67 --- /dev/null +++ b/docs/guides/migrating-to-flag-sets.md @@ -0,0 +1,150 @@ +# Migrating to Flag Sets + +This guide helps you transition from source-based selector patterns to flag-set-based patterns, taking advantage of flagd's enhanced selector capabilities while maintaining backward compatibility. + +## Understanding the Change + +### Before: Source-Based Selection + +In the traditional approach, providers targeted specific sources: + +```yaml +# Provider configuration targeting a source file +selector: "config/my-flags.json" +``` + +This created tight coupling between providers and sources: + +- Providers had to know which source contained their flags +- Moving flags between sources required provider reconfiguration +- One source could only serve one logical set of flags + +### After: Flag Set-Based Selection + +With flag sets, providers target logical groupings of flags: + +```yaml +# Provider configuration targeting a flag set +selector: "flagSetId=my-application" +``` + +This provides flexibility: + +- Providers are decoupled from sources +- Sources can contain multiple flag sets +- Flag sets can span multiple sources +- No breaking changes - old selectors still work + +## Migration Process + +### Step 1: Add Flag Set IDs to Configurations + +Add `flagSetId` to your flag configurations at the set level: + +**Before:** + +```json +{ + "flags": { + "feature-a": { + "state": "ENABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "on" + } + } +} +``` + +**After:** + +```json +{ + "metadata": { + "flagSetId": "my-application" + }, + "flags": { + "feature-a": { + "state": "ENABLED", + "variants": {"on": true, "off": false}, + "defaultVariant": "on" + } + } +} +``` + +### Step 2: Update Provider Configurations + +Change provider selectors from source-based to flag set-based: + +```java +// Before +new FlagdProvider(FlagdOptions.builder() + .selector("config/app-flags.json").build()); + +// After +new FlagdProvider(FlagdOptions.builder() + .selector("flagSetId=my-application").build()); +``` + +### Step 3: Verify Migration + +Test that selectors work correctly and check metadata reflection: + +```bash +curl -H "Flagd-Selector: flagSetId=my-application" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +## Flag Set Organization Patterns + +**By Application/Service:** + +```yaml +flagSetId: "user-service" # All user-related flags +flagSetId: "payment-service" # All payment-related flags +``` + +**By Environment:** + +```yaml +flagSetId: "development" # Dev-specific flags +flagSetId: "production" # Production flags +``` + +**By Team:** + +```yaml +flagSetId: "frontend-team" # Frontend features +flagSetId: "backend-team" # Backend features +``` + +Choose the pattern that best matches your deployment and organizational structure. + +## Common Issues + +**No flags returned**: Check that `flagSetId` in selector matches flag configuration exactly + +**Wrong flags returned**: Look for flag-level `flagSetId` overrides or header/body selector conflicts + +**Selector ignored**: Verify selector syntax is correct (`flagSetId=value`, not `flagSetId:value`) + +## Best Practices + +- **Group logically**: Organize flags by service, environment, or team +- **Name consistently**: Use clear, descriptive flag set names +- **Test first**: Validate migration in non-production environments +- **Use metadata reflection**: Check reflected metadata for debugging + +## FAQ + +**Q: Do I have to migrate?** +A: No, source-based selectors continue to work. Migration is optional but recommended. + +**Q: Can flag sets span multiple sources?** +A: Yes, multiple sources can contribute flags to the same flag set. + +## Additional Resources + +- [Selector Concepts](../concepts/selectors.md) - Understanding selectors and flag sets +- [Selector Syntax Reference](../reference/selector-syntax.md) - Complete syntax documentation +- [ADR: Decouple Flag Source and Set](../architecture-decisions/decouple-flag-source-and-set.md) - Technical decision rationale diff --git a/docs/reference/selector-syntax.md b/docs/reference/selector-syntax.md new file mode 100644 index 000000000..82b18d3e9 --- /dev/null +++ b/docs/reference/selector-syntax.md @@ -0,0 +1,191 @@ +# Selector Syntax Reference + +This document provides the complete technical specification for flagd selector syntax, including supported operators, precedence rules, and metadata reflection behavior. + +## Syntax Overview + +Selectors use a simple key-value syntax to filter flags. Currently, selectors support single key-value pairs with plans to expand to more complex queries in the future. + +### Basic Syntax + +```text += +``` + +### Backward Compatibility Syntax + +```text + +``` + +When no `=` is present, the value is treated as a source selector for backward compatibility. + +## Supported Keys + +### `flagSetId` + +Selects flags belonging to a specific flag set. + +**Syntax:** + +```text +flagSetId= +``` + +**Examples:** + +```text +flagSetId=project-42 +flagSetId=dev-environment +flagSetId=team-payments +``` + +**Special Case - Empty Flag Set:** + +```text +flagSetId= +``` + +Selects flags that don't belong to any named flag set (equivalent to the "null" flag set). + +### `source` + +Selects flags from a specific source. + +**Syntax:** + +```text +source= +``` + +**Examples:** + +```text +source=config/flags.json +source=http://flag-server/config +source=./local-flags.yaml +``` + +## Selector Precedence + +When selectors are provided in multiple locations, flagd uses the following precedence order (highest to lowest): + +1. **gRPC Header**: `Flagd-Selector` header in gRPC metadata +2. **HTTP Header**: `Flagd-Selector` header in HTTP requests +3. **Request Body**: `selector` field in protobuf/JSON request body + +### Example: Header Precedence + +```bash +# gRPC request with both header and body selector +# Header takes precedence +grpcurl -H "Flagd-Selector: flagSetId=production" \ + -d '{"selector": "flagSetId=development"}' \ + localhost:8013 flagd.sync.v1.FlagSyncService/FetchAllFlags + +# Result: Uses "flagSetId=production" from header +``` + +## Metadata Reflection + +Flagd reflects selector information back in response metadata, providing transparency about query execution. For complete details on metadata selector reflection, inheritance patterns, and configuration examples, see the [Metadata concepts](../concepts/metadata.md) section. + +## Examples + +### Flag Set Selection + +```bash +# Select flags from the "payments" flag set +curl -H "Flagd-Selector: flagSetId=payments" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +### Source Selection (Legacy) + +```bash +# Select flags from a specific source (backward compatibility) +curl -H "Flagd-Selector: source=config/prod-flags.json" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +### Empty Flag Set Selection + +```bash +# Select flags that don't belong to any named flag set +curl -H "Flagd-Selector: flagSetId=" \ + http://localhost:8014/ofrep/v1/evaluate/flags +``` + +### Provider SDK Usage + +#### Go Provider + +```go +import "github.com/open-feature/go-sdk-contrib/providers/flagd" + +provider := flagd.NewProvider( + flagd.WithHost("localhost"), + flagd.WithPort(8013), + flagd.WithSelector("flagSetId=user-service"), +) +``` + +#### Java Provider + +```java +FlagdProvider provider = new FlagdProvider( + FlagdOptions.builder() + .host("localhost") + .port(8013) + .selector("flagSetId=payment-service") + .build() +); +``` + +#### JavaScript Provider + +```javascript +const provider = new FlagdProvider({ + host: 'localhost', + port: 8013, + selector: 'flagSetId=frontend-features' +}); +``` + +## Future Enhancements + +The selector syntax is designed to be extensible. Future versions may support: + +- **Multiple Criteria**: `flagSetId=app1,source=prod` +- **Complex Queries**: `flagSetId=app1 OR flagSetId=app2` +- **Filter Expressions**: `metadata.environment=production` +- **Kubernetes-Style Selectors**: `app=frontend,tier=web` + +> **Note**: The current implementation supports single key-value pairs only. Complex selectors are planned for future releases. + +## API Reference + +### gRPC Services + +**Sync Service:** + +- `SyncFlags(SyncFlagsRequest)`: Supports selector in header and request body +- `FetchAllFlags(FetchAllFlagsRequest)`: Supports selector in header and request body + +**Evaluation Service:** + +- `ResolveBoolean(ResolveBooleanRequest)`: Supports selector in header +- `ResolveString(ResolveStringRequest)`: Supports selector in header +- `ResolveInt(ResolveIntRequest)`: Supports selector in header +- `ResolveFloat(ResolveFloatRequest)`: Supports selector in header +- `ResolveObject(ResolveObjectRequest)`: Supports selector in header +- `ResolveAll(ResolveAllRequest)`: Supports selector in header + +### HTTP/OFREP Services + +**OFREP Endpoints:** + +- `POST /ofrep/v1/evaluate/flags/{key}`: Supports selector in header +- `POST /ofrep/v1/evaluate/flags`: Supports selector in header + +All HTTP endpoints support the `Flagd-Selector` header for selector specification. diff --git a/docs/reference/specifications/providers.md b/docs/reference/specifications/providers.md index 101570ea7..2d276bda4 100644 --- a/docs/reference/specifications/providers.md +++ b/docs/reference/specifications/providers.md @@ -106,7 +106,7 @@ We always rely on the [integrated functionality of GRPC for reconnection](https: We are configuring the underlying reconnection mechanism whenever we can, based on our configuration. (not all GRPC implementations support this) | language/property | min connect timeout | max backoff | initial backoff | jitter | multiplier | -|-------------------|-----------------------------------|--------------------------|--------------------------|--------|------------| +| ----------------- | --------------------------------- | ------------------------ | ------------------------ | ------ | ---------- | | GRPC property | grpc.initial_reconnect_backoff_ms | max_reconnect_backoff_ms | min_reconnect_backoff_ms | 0.2 | 1.6 | | Flagd property | deadlineMs | retryBackoffMaxMs | retryBackoffMs | 0.2 | 1.6 | | --- | --- | --- | --- | --- | --- | @@ -262,28 +262,28 @@ precedence. Below are the supported configuration parameters (note that not all apply to both resolver modes): -| Option name | Environment variable name | Explanation | Type & Values | Default | Compatible resolver | -| --------------------- | ------------------------------ | ---------------------------------------------------------------------- | ---------------------------- | ----------------------------- | ----------------------- | -| resolver | FLAGD_RESOLVER | mode of operation | String - `rpc`, `in-process` | rpc | rpc & in-process | -| host | FLAGD_HOST | remote host | String | localhost | rpc & in-process | -| port | FLAGD_PORT | remote port | int | 8013 (rpc), 8015 (in-process) | rpc & in-process | -| targetUri | FLAGD_TARGET_URI | alternative to host/port, supporting custom name resolution | string | null | rpc & in-process | -| tls | FLAGD_TLS | connection encryption | boolean | false | rpc & in-process | -| socketPath | FLAGD_SOCKET_PATH | alternative to host port, unix socket | String | null | rpc & in-process | -| certPath | FLAGD_SERVER_CERT_PATH | tls cert path | String | null | rpc & in-process | -| deadlineMs | FLAGD_DEADLINE_MS | deadline for unary calls, and timeout for initialization | int | 500 | rpc & in-process & file | -| streamDeadlineMs | FLAGD_STREAM_DEADLINE_MS | deadline for streaming calls, useful as an application-layer keepalive | int | 600000 | rpc & in-process | -| retryBackoffMs | FLAGD_RETRY_BACKOFF_MS | initial backoff for stream retry | int | 1000 | rpc & in-process | -| retryBackoffMaxMs | FLAGD_RETRY_BACKOFF_MAX_MS | maximum backoff for stream retry | int | 120000 | rpc & in-process | -| retryGracePeriod | FLAGD_RETRY_GRACE_PERIOD | period in seconds before provider moves from STALE to ERROR state | int | 5 | rpc & in-process & file | -| keepAliveTime | FLAGD_KEEP_ALIVE_TIME_MS | http 2 keepalive | long | 0 | rpc & in-process | -| cache | FLAGD_CACHE | enable cache of static flags | String - `lru`, `disabled` | lru | rpc | -| maxCacheSize | FLAGD_MAX_CACHE_SIZE | max size of static flag cache | int | 1000 | rpc | -| selector | FLAGD_SOURCE_SELECTOR | selects a single sync source to retrieve flags from only that source | string | null | in-process | -| providerId | FLAGD_PROVIDER_ID | A unique identifier for flagd(grpc client) initiating the request. | string | null | in-process | -| offlineFlagSourcePath | FLAGD_OFFLINE_FLAG_SOURCE_PATH | offline, file-based flag definitions, overrides host/port/targetUri | string | null | file | -| offlinePollIntervalMs | FLAGD_OFFLINE_POLL_MS | poll interval for reading offlineFlagSourcePath | int | 5000 | file | -| contextEnricher | - | sync-metadata to evaluation context mapping function | function | identity function | in-process | +| Option name | Environment variable name | Explanation | Type & Values | Default | Compatible resolver | +| --------------------- | ------------------------------ | ------------------------------------------------------------------------------------ | ---------------------------- | ----------------------------- | ----------------------- | +| resolver | FLAGD_RESOLVER | mode of operation | String - `rpc`, `in-process` | rpc | rpc & in-process | +| host | FLAGD_HOST | remote host | String | localhost | rpc & in-process | +| port | FLAGD_PORT | remote port | int | 8013 (rpc), 8015 (in-process) | rpc & in-process | +| targetUri | FLAGD_TARGET_URI | alternative to host/port, supporting custom name resolution | string | null | rpc & in-process | +| tls | FLAGD_TLS | connection encryption | boolean | false | rpc & in-process | +| socketPath | FLAGD_SOCKET_PATH | alternative to host port, unix socket | String | null | rpc & in-process | +| certPath | FLAGD_SERVER_CERT_PATH | tls cert path | String | null | rpc & in-process | +| deadlineMs | FLAGD_DEADLINE_MS | deadline for unary calls, and timeout for initialization | int | 500 | rpc & in-process & file | +| streamDeadlineMs | FLAGD_STREAM_DEADLINE_MS | deadline for streaming calls, useful as an application-layer keepalive | int | 600000 | rpc & in-process | +| retryBackoffMs | FLAGD_RETRY_BACKOFF_MS | initial backoff for stream retry | int | 1000 | rpc & in-process | +| retryBackoffMaxMs | FLAGD_RETRY_BACKOFF_MAX_MS | maximum backoff for stream retry | int | 120000 | rpc & in-process | +| retryGracePeriod | FLAGD_RETRY_GRACE_PERIOD | period in seconds before provider moves from STALE to ERROR state | int | 5 | rpc & in-process & file | +| keepAliveTime | FLAGD_KEEP_ALIVE_TIME_MS | http 2 keepalive | long | 0 | rpc & in-process | +| cache | FLAGD_CACHE | enable cache of static flags | String - `lru`, `disabled` | lru | rpc | +| maxCacheSize | FLAGD_MAX_CACHE_SIZE | max size of static flag cache | int | 1000 | rpc | +| selector | FLAGD_SOURCE_SELECTOR | Selector expression to filter flags (e.g., `flagSetId=my-app`, `source=config.json`) | string | null | in-process | +| providerId | FLAGD_PROVIDER_ID | A unique identifier for flagd(grpc client) initiating the request. | string | null | in-process | +| offlineFlagSourcePath | FLAGD_OFFLINE_FLAG_SOURCE_PATH | offline, file-based flag definitions, overrides host/port/targetUri | string | null | file | +| offlinePollIntervalMs | FLAGD_OFFLINE_POLL_MS | poll interval for reading offlineFlagSourcePath | int | 5000 | file | +| contextEnricher | - | sync-metadata to evaluation context mapping function | function | identity function | in-process | ### Custom Name Resolution @@ -298,8 +298,103 @@ envoy://localhost:9211/flagd-sync.service The custom name resolver provider in this case will use the endpoint name i.e. `flagd-sync.service` as [authority](https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/nameresolve/ExampleNameResolver.java#L55-L61) and connect to `localhost:9211`. -### Metadata +### Selector Configuration + +Providers support selector configuration to filter which flags are synchronized or evaluated. This enables more granular control in multi-tenant or multi-environment deployments. + +#### Selector Syntax + +Providers accept selector expressions using the following syntax: + +- **Flag Set Selection**: `flagSetId=` - Target flags belonging to a specific flag set +- **Source Selection**: `source=` - Target flags from a specific source (legacy) +- **Backward Compatibility**: `` - Treated as source selection + +#### Selector Precedence + +When selectors are provided in multiple locations, the following precedence applies: + +1. **Request Header**: `Flagd-Selector` header (RPC and OFREP requests) +2. **Provider Configuration**: `selector` option in provider constructor + +#### Usage Examples + +**Flag Set-Based Selection (Recommended):** + +```javascript +const provider = new FlagdProvider({ + host: 'localhost', + port: 8013, + selector: 'flagSetId=user-service' +}); +``` + +**Source-Based Selection (Legacy):** + +```javascript +const provider = new FlagdProvider({ + host: 'localhost', + port: 8013, + selector: 'source=config/app-flags.json' +}); +``` + +**Header-Based Selection:** + +```bash +# gRPC request with selector header +grpcurl -H "Flagd-Selector: flagSetId=payment-service" \ + localhost:8013 flagd.evaluation.v1.Service/ResolveBoolean + +# OFREP request with selector header +curl -H "Flagd-Selector: flagSetId=frontend-features" \ + http://localhost:8014/ofrep/v1/evaluate/flags/my-flag +``` + +### Metadata and Metadata Reflection + +#### Flag Metadata When a flag is resolved, the returned [metadata](./flag-definitions.md#metadata) is a merged representation of the metadata defined on the flag set, and on the flag, with the flag metadata taking priority. Flag metadata is returned on a "best effort" basis when flags are resolved: disabled, missing or erroneous flags return the metadata of the associated flag set whenever possible. This is particularly important for debugging purposes and error metrics. + +#### Selector Metadata "Reflection" + +Flagd "reflects" selector information back in response metadata, providing transparency about query execution. This helps with debugging selector expressions and understanding which flags were actually queried. + +**Example - gRPC Response:** + +```protobuf +// Request with selector header: "Flagd-Selector: flagSetId=payment-service" +message ResolveBooleanResponse { + bool value = 1; + string reason = 2; + string variant = 3; + google.protobuf.Struct metadata = 4; // Contains reflected selector info +} +``` + +**Example - OFREP Response:** + +```json +{ + "value": true, + "reason": "TARGETING_MATCH", + "variant": "on", + "metadata": { + "flagSetId": "payment-service", + "team": "payments", + "version": "1.2.0" + } +} +``` + +#### Debugging with Metadata + +Use reflected metadata to: + +- **Verify Selector Parsing**: Confirm your selector was interpreted correctly +- **Debug Empty Results**: Check if selectors are filtering flags as expected +- **Audit Access Patterns**: Log selector metadata for compliance and monitoring +- **Troubleshoot Configuration**: Identify selector precedence issues diff --git a/docs/reference/sync-configuration.md b/docs/reference/sync-configuration.md index 8cf679efd..1156f9446 100644 --- a/docs/reference/sync-configuration.md +++ b/docs/reference/sync-configuration.md @@ -47,17 +47,17 @@ The flagd accepts a string argument, which should be a JSON representation of an Alternatively, these configurations can be passed to flagd via config file, specified using the `--config` flag. -| Field | Type | Note | -| ----------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| uri | required `string` | Flag configuration source of the sync | -| provider | required `string` | Provider type - `file`, `fsnotify`, `fileinfo`, `kubernetes`, `http`, `grpc`, `gcs` or `azblob` | -| authHeader | optional `string` | Used for http sync; set this to include the complete `Authorization` header value for any authentication scheme (e.g., "Bearer token_here", "Basic base64_credentials", etc.). | -| interval | optional `uint32` | Used for http, gcs and azblob syncs; requests will be made at this interval. Defaults to 5 seconds. | -| tls | optional `boolean` | Enable/Disable secure TLS connectivity. Currently used only by gRPC sync. Default (ex: if unset) is false, which will use an insecure connection | -| providerID | optional `string` | Value binds to grpc connection's providerID field. gRPC server implementations may use this to identify connecting flagd instance | -| selector | optional `string` | Value binds to grpc connection's selector field. gRPC server implementations may use this to filter flag configurations | -| certPath | optional `string` | Used for grpcs sync when TLS certificate is needed. If not provided, system certificates will be used for TLS connection | -| maxMsgSize | optional `int` | Used for gRPC sync to set max receive message size (in bytes) e.g. 5242880 for 5MB. If not provided, the default is [4MB](https://pkg.go.dev/google.golang.org#grpc#MaxCallRecvMsgSize) | +| Field | Type | Note | +| ---------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| uri | required `string` | Flag configuration source of the sync | +| provider | required `string` | Provider type - `file`, `fsnotify`, `fileinfo`, `kubernetes`, `http`, `grpc`, `gcs` or `azblob` | +| authHeader | optional `string` | Used for http sync; set this to include the complete `Authorization` header value for any authentication scheme (e.g., "Bearer token_here", "Basic base64_credentials", etc.). | +| interval | optional `uint32` | Used for http, gcs and azblob syncs; requests will be made at this interval. Defaults to 5 seconds. | +| tls | optional `boolean` | Enable/Disable secure TLS connectivity. Currently used only by gRPC sync. Default (ex: if unset) is false, which will use an insecure connection | +| providerID | optional `string` | Value binds to grpc connection's providerID field. gRPC server implementations may use this to identify connecting flagd instance | +| selector | optional `string` | Selector expression to filter flag configurations. Supports `source=` and `flagSetId=` syntax. See [selector syntax](selector-syntax.md) for details. | +| certPath | optional `string` | Used for grpcs sync when TLS certificate is needed. If not provided, system certificates will be used for TLS connection | +| maxMsgSize | optional `int` | Used for gRPC sync to set max receive message size (in bytes) e.g. 5242880 for 5MB. If not provided, the default is [4MB](https://pkg.go.dev/google.golang.org#grpc#MaxCallRecvMsgSize) | The `uri` field values **do not** follow the [URI patterns](#uri-patterns). The provider type is instead derived from the `provider` field. Only exception is the remote provider where `http(s)://` is expected by default. Incorrect @@ -100,7 +100,7 @@ Startup command: {"uri":"grpc-source:8080","provider":"grpc"}, {"uri":"my-flag-source:8080","provider":"grpc", "maxMsgSize": 5242880}, {"uri":"envoy://localhost:9211/test.service", "provider":"grpc"}, - {"uri":"my-flag-source:8080","provider":"grpc", "certPath": "/certs/ca.cert", "tls": true, "providerID": "flagd-weatherapp-sidecar", "selector": "source=database,app=weatherapp"}, + {"uri":"my-flag-source:8080","provider":"grpc", "certPath": "/certs/ca.cert", "tls": true, "providerID": "flagd-weatherapp-sidecar", "selector": "flagSetId=weatherapp"}, {"uri":"gs://my-bucket/my-flag.json","provider":"gcs"}, {"uri":"azblob://my-container/my-flag.json","provider":"azblob"}]' ``` @@ -132,7 +132,7 @@ sources: certPath: /certs/ca.cert tls: true providerID: flagd-weatherapp-sidecar - selector: "source=database,app=weatherapp" + selector: "flagSetId=weatherapp" - uri: gs://my-bucket/my-flag.json provider: gcs - uri: azblob://my-container/my-flags.json @@ -186,3 +186,25 @@ the reload of the secrets from the filesystem every `ReloadDelayS` seconds. "tokenURL": "http://localhost:8180/sso/oauth2/token" }}]' ``` + +## Selector Configuration + +Selectors allow you to filter flag configurations from sync sources. Add the `selector` field to source configurations: + +```yaml +sources: + - uri: grpc://flag-server:8080 + provider: grpc + selector: "flagSetId=payment-service" # Flag set selection + - uri: grpc://flag-server:8080 + provider: grpc + selector: "source=legacy-flags" # Source selection (legacy) +``` + +### Selector Precedence + +1. **Request Headers**: `Flagd-Selector` header (highest priority) +2. **Request Body**: `selector` field in request +3. **Configuration**: `selector` field in source configuration (lowest priority) + +For complete selector syntax, patterns, and examples, see the [Selectors concepts](../concepts/selectors.md) and [Selector Syntax Reference](selector-syntax.md). diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 6537d81f1..4ab0cdd2c 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -71,3 +71,45 @@ You may need to explicitly allow HTTP2 or gRPC in your platform if you're using !!! note HTTP2 _is not_ strictly for the flag [evaluation gRPC service](./reference/specifications/protos.md#schemav1schemaproto), which is exposed both as a gRPC service and a RESTful HTTP/1.1 service, thanks to the [connect protocol](https://connectrpc.com/docs/protocol/). + +--- + +## Selector Issues + +### No Flags Returned with Selector + +**Problem**: Provider returns no flags when using a selector. + +**Debugging Steps:** + +- Verify `flagSetId` in selector matches flag configuration exactly +- Check selector syntax: `flagSetId=my-app` (not `flagSetId:my-app`) +- Test without selector to confirm flags exist + +### Wrong Flags Returned + +**Problem**: Selector returns unexpected flags. + +**Debugging Steps:** + +- Check for flag-level `flagSetId` overrides in individual flags +- Verify header precedence: `Flagd-Selector` header overrides request body +- Use metadata reflection to see what selector was actually applied + +### Selector Ignored + +**Problem**: Selector appears to be ignored, all flags returned. + +**Debugging Steps:** + +- Verify selector syntax is correct (`key=value` format) +- Check if provider configuration has a selector that overrides requests +- Ensure selector value is not empty (`flagSetId=` returns all flags without flagSetId) + +**Debug with metadata reflection:** + +```bash +curl -H "Flagd-Selector: flagSetId=my-app" \ + http://localhost:8014/ofrep/v1/evaluate/flags +# Check response metadata to see parsed selector +``` diff --git a/mkdocs.yml b/mkdocs.yml index 641f133f8..68de9503b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -78,7 +78,11 @@ nav: - 'Concepts': - 'Feature Flagging': 'concepts/feature-flagging.md' - 'Syncs': 'concepts/syncs.md' + - 'Selectors': 'concepts/selectors.md' + - 'Metadata': 'concepts/metadata.md' - 'Architecture': 'architecture.md' + - 'Guides': + - 'Migrating to Flag Sets': 'guides/migrating-to-flag-sets.md' - 'OpenFeature Providers': - 'providers/index.md' - 'Go': 'providers/go.md' @@ -95,6 +99,7 @@ nav: - 'Start': 'reference/flagd-cli/flagd_start.md' - 'Version': 'reference/flagd-cli/flagd_version.md' - 'Sync Configuration': 'reference/sync-configuration.md' + - 'Selector Syntax': 'reference/selector-syntax.md' - 'gRPC sync service': 'reference/grpc-sync-service.md' - 'OFREP service': 'reference/flagd-ofrep.md' - 'Flag Definitions':