Skip to content

Commit

Permalink
Remove Feature Flag Service in favor of OpenFeature + flagd (#1388)
Browse files Browse the repository at this point in the history
* replace featureflag service with flagd

* remove ff from gha

* update changelog

* fix sanity checker

* add python

* fix sanity

* add flagd provider to python

* Update src/adservice/build.gradle

Co-authored-by: Justin Abrahms <[email protected]>

* cleanup ad service eval

* fix adservice

* fixup gomod

* add otel hooks for .net flags

* add otel hooks for productcatalog

* Remove trailing space

* add stub for python otel hook

* remove unneeded key from flags

---------

Co-authored-by: Justin Abrahms <[email protected]>
Co-authored-by: Juliano Costa <[email protected]>
  • Loading branch information
3 people authored Mar 7, 2024
1 parent c8b8e08 commit ce4ad93
Show file tree
Hide file tree
Showing 87 changed files with 1,435 additions and 3,110 deletions.
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ FEATURE_FLAG_SERVICE_HOST=feature-flag-service
FEATURE_FLAG_GRPC_SERVICE_PORT=50053
FEATURE_FLAG_GRPC_SERVICE_ADDR=featureflagservice:${FEATURE_FLAG_GRPC_SERVICE_PORT}

# flagd
FLAGD_HOST=flagd

# Frontend
FRONTEND_PORT=8080
FRONTEND_ADDR=frontend:${FRONTEND_PORT}
Expand Down
12 changes: 0 additions & 12 deletions .github/workflows/build-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,6 @@ jobs:
tag_suffix: emailservice
context: ./src/emailservice
setup-qemu: true
- file: ./src/ffspostgres/Dockerfile
tag_suffix: ffspostgres
context: ./
setup-qemu: true
# NOTE:
# https:/open-telemetry/opentelemetry-demo/issues/956
# Until dedicated ARM runners are available for GHA we cannot upgrade
# OTP/Elixir versions. Please do not change the OTP/Elixir versions.
- file: ./src/featureflagservice/Dockerfile
tag_suffix: featureflagservice
context: ./
setup-qemu: true
- file: ./src/frontend/Dockerfile
tag_suffix: frontend
context: ./
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ the release.

## Unreleased

* [featureflag] deprecate in favor of flagd
([#1338](https:/open-telemetry/opentelemetry-demo/pull/1388))
* [checkoutservice] add producer interceptor for tracing
([#1400](https:/open-telemetry/opentelemetry-demo/pull/1400))
* [chore] increase memory for Collector and Jaeger
([#1396](https:/open-telemetry/opentelemetry-demo/pull/1396))
* [chore] fix Make targets for restart and redeploy
Expand Down
83 changes: 17 additions & 66 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ services:
- "${AD_SERVICE_PORT}"
environment:
- AD_SERVICE_PORT
- FEATURE_FLAG_GRPC_SERVICE_ADDR
- FLAGD_HOST
- OTEL_EXPORTER_OTLP_ENDPOINT=http://${OTEL_COLLECTOR_HOST}:${OTEL_COLLECTOR_PORT_HTTP}
- OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE
- OTEL_RESOURCE_ATTRIBUTES
Expand Down Expand Up @@ -91,7 +91,7 @@ services:
- "${CART_SERVICE_PORT}"
environment:
- CART_SERVICE_PORT
- FEATURE_FLAG_GRPC_SERVICE_ADDR
- FLAGD_HOST
- REDIS_ADDR
- OTEL_EXPORTER_OTLP_ENDPOINT
- OTEL_RESOURCE_ATTRIBUTES
Expand Down Expand Up @@ -203,36 +203,20 @@ services:
condition: service_started
logging: *logging

# Feature Flag service
featureflagservice:
image: ${IMAGE_NAME}:${DEMO_VERSION}-featureflagservice
container_name: feature-flag-service
build:
context: ./
dockerfile: ./src/featureflagservice/Dockerfile
cache_from:
- ${IMAGE_NAME}:${IMAGE_VERSION}-featureflagservice
deploy:
resources:
limits:
memory: 175M
restart: unless-stopped
flagd:
image: ghcr.io/open-feature/flagd:latest
container_name: flagd
command: [
"start",
"--uri",
"file:./etc/flagd/demo.flagd.json"
]
ports:
- "${FEATURE_FLAG_SERVICE_PORT}" # Feature Flag Service UI
- "${FEATURE_FLAG_GRPC_SERVICE_PORT}" # Feature Flag Service gRPC API
environment:
- FEATURE_FLAG_SERVICE_PORT
- FEATURE_FLAG_GRPC_SERVICE_PORT
- OTEL_EXPORTER_OTLP_ENDPOINT
- OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc
- OTEL_SERVICE_NAME=featureflagservice
- DATABASE_URL=ecto://ffs:ffs@ffspostgres:5432/ffs
healthcheck:
test: ["CMD", "curl", "-H", "baggage: synthetic_request=true", "-f", "http://localhost:${FEATURE_FLAG_SERVICE_PORT}"]
depends_on:
ffspostgres:
condition: service_healthy
logging: *logging
- 8013
volumes:
- ./src/flagd:/etc/flagd
logging:
*logging

# Fraud Detection service
frauddetectionservice:
Expand Down Expand Up @@ -333,8 +317,6 @@ services:
environment:
- FRONTEND_PORT
- FRONTEND_HOST
- FEATURE_FLAG_SERVICE_PORT
- FEATURE_FLAG_SERVICE_HOST
- LOCUST_WEB_HOST
- LOCUST_WEB_PORT
- GRAFANA_SERVICE_PORT
Expand All @@ -349,8 +331,6 @@ services:
depends_on:
frontend:
condition: service_started
featureflagservice:
condition: service_started
loadgenerator:
condition: service_started
jaeger:
Expand Down Expand Up @@ -436,7 +416,7 @@ services:
- "${PRODUCT_CATALOG_SERVICE_PORT}"
environment:
- PRODUCT_CATALOG_SERVICE_PORT
- FEATURE_FLAG_GRPC_SERVICE_ADDR
- FLAGD_HOST
- OTEL_EXPORTER_OTLP_ENDPOINT
- OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE
- OTEL_RESOURCE_ATTRIBUTES
Expand Down Expand Up @@ -493,16 +473,14 @@ services:
environment:
- RECOMMENDATION_SERVICE_PORT
- PRODUCT_CATALOG_SERVICE_ADDR
- FEATURE_FLAG_GRPC_SERVICE_ADDR
- FLAGD_HOST
- OTEL_PYTHON_LOG_CORRELATION=true
- OTEL_EXPORTER_OTLP_ENDPOINT
- OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE
- OTEL_RESOURCE_ATTRIBUTES
- OTEL_SERVICE_NAME=recommendationservice
- PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
depends_on:
featureflagservice:
condition: service_started
productcatalogservice:
condition: service_started
otelcol:
Expand Down Expand Up @@ -539,33 +517,6 @@ services:
# ******************
# Dependent Services
# ******************
# Postgres used by Feature Flag service
ffspostgres:
image: ${IMAGE_NAME}:${DEMO_VERSION}-ffspostgres
container_name: ffs-postgres
build:
context: ./
dockerfile: ./src/ffspostgres/Dockerfile
cache_from:
- ${IMAGE_NAME}:${IMAGE_VERSION}-ffspostgres
deploy:
resources:
limits:
memory: 120M
restart: unless-stopped
environment:
- POSTGRES_USER=ffs
- POSTGRES_DB=ffs
- POSTGRES_PASSWORD=ffs
volumes:
- ./src/ffspostgres/update-scripts/99-ffs_update.sql:/docker-entrypoint-initdb.d/99-ffs_update.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -d ffs -U ffs"]
interval: 10s
timeout: 5s
retries: 5
logging: *logging

# Kafka used by Checkout, Accounting, and Fraud Detection services
kafka:
image: ${IMAGE_NAME}:${DEMO_VERSION}-kafka
Expand Down
4 changes: 3 additions & 1 deletion src/adservice/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ dependencies {
"io.opentelemetry:opentelemetry-api",
"io.opentelemetry:opentelemetry-sdk",
"io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations",
"org.apache.logging.log4j:log4j-core:2.21.1"
"org.apache.logging.log4j:log4j-core:2.21.1",
"dev.openfeature.contrib.providers:flagd:0.7.0",
'dev.openfeature:sdk:1.7.4'

runtimeOnly "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}",
"com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}",
Expand Down
61 changes: 26 additions & 35 deletions src/adservice/src/main/java/oteldemo/AdService.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,14 @@
import oteldemo.Demo.Ad;
import oteldemo.Demo.AdRequest;
import oteldemo.Demo.AdResponse;
import oteldemo.Demo.GetFlagResponse;
import oteldemo.FeatureFlagServiceGrpc.FeatureFlagServiceBlockingStub;
import dev.openfeature.contrib.providers.flagd.FlagdOptions;
import dev.openfeature.contrib.providers.flagd.FlagdProvider;
import dev.openfeature.sdk.Client;
import dev.openfeature.sdk.EvaluationContext;
import dev.openfeature.sdk.MutableContext;
import dev.openfeature.sdk.OpenFeatureAPI;
import java.util.UUID;


public final class AdService {

Expand Down Expand Up @@ -72,18 +78,19 @@ private void start() throws IOException {
"environment vars: AD_SERVICE_PORT must not be null")));
healthMgr = new HealthStatusManager();

String featureFlagServiceAddr =
Optional.ofNullable(System.getenv("FEATURE_FLAG_GRPC_SERVICE_ADDR")).orElse("");
FeatureFlagServiceBlockingStub featureFlagServiceStub = null;
if (!featureFlagServiceAddr.isEmpty()) {
featureFlagServiceStub =
oteldemo.FeatureFlagServiceGrpc.newBlockingStub(
ManagedChannelBuilder.forTarget(featureFlagServiceAddr).usePlaintext().build());
}
// Create a flagd instance with OpenTelemetry
FlagdOptions options =
FlagdOptions.builder()
.withGlobalTelemetry(true)
.build();

FlagdProvider flagdProvider = new FlagdProvider(options);
// Set flagd as the OpenFeature Provider
OpenFeatureAPI.getInstance().setProvider(flagdProvider);

server =
ServerBuilder.forPort(port)
.addService(new AdServiceImpl(featureFlagServiceStub))
.addService(new AdServiceImpl())
.addService(healthMgr.getHealthService())
.build()
.start();
Expand Down Expand Up @@ -119,14 +126,8 @@ private enum AdResponseType {
}

private static class AdServiceImpl extends oteldemo.AdServiceGrpc.AdServiceImplBase {

private static final String ADSERVICE_FAIL_FEATURE_FLAG = "adServiceFailure";

private final FeatureFlagServiceBlockingStub featureFlagServiceStub;

private AdServiceImpl(FeatureFlagServiceBlockingStub featureFlagServiceStub) {
this.featureFlagServiceStub = featureFlagServiceStub;
}

private AdServiceImpl() {}

/**
* Retrieves ads based on context provided in the request {@code AdRequest}.
Expand Down Expand Up @@ -177,7 +178,6 @@ public void getAds(AdRequest req, StreamObserver<AdResponse> responseObserver) {
adRequestTypeKey, adRequestType.name(), adResponseTypeKey, adResponseType.name()));

if (checkAdFailure()) {
logger.warn(ADSERVICE_FAIL_FEATURE_FLAG + " fail feature flag enabled");
throw new StatusRuntimeException(Status.RESOURCE_EXHAUSTED);
}

Expand All @@ -194,21 +194,12 @@ public void getAds(AdRequest req, StreamObserver<AdResponse> responseObserver) {
}

boolean checkAdFailure() {
if (featureFlagServiceStub == null) {
return false;
}

// Flip a coin and fail 1/10th of the time if feature flag is enabled
if (random.nextInt(10) != 1) {
return false;
}

GetFlagResponse response =
featureFlagServiceStub.getFlag(
oteldemo.Demo.GetFlagRequest.newBuilder()
.setName(ADSERVICE_FAIL_FEATURE_FLAG)
.build());
return response.getFlag().getEnabled();
Client client = OpenFeatureAPI.getInstance().getClient();
// TODO: Plumb the actual session ID from the frontend via baggage?
UUID uuid = UUID.randomUUID();
client.setEvaluationContext(new MutableContext().add("session", uuid.toString()));
Boolean boolValue = client.getBooleanValue("adServiceFailure", false);
return boolValue;
}
}

Expand Down
23 changes: 16 additions & 7 deletions src/cartservice/src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System;

using cartservice.cartstore;
using cartservice.featureflags;
using cartservice.services;

using Microsoft.AspNetCore.Builder;
Expand All @@ -18,6 +17,9 @@
using OpenTelemetry.ResourceDetectors.Host;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using OpenFeature;
using OpenFeature.Contrib.Providers.Flagd;
using OpenFeature.Contrib.Hooks.Otel;

var builder = WebApplication.CreateBuilder(args);
string redisAddress = builder.Configuration["REDIS_ADDR"];
Expand All @@ -38,13 +40,20 @@
return store;
});

builder.Services.AddSingleton<FeatureFlagHelper>();
builder.Services.AddSingleton(x => new CartService(x.GetRequiredService<ICartStore>(),
new RedisCartStore(x.GetRequiredService<ILogger<RedisCartStore>>(), "badhost:1234"),
x.GetRequiredService<FeatureFlagHelper>()));
builder.Services.AddSingleton<IFeatureClient>(x => {
var flagdProvider = new FlagdProvider();
Api.Instance.SetProviderAsync(flagdProvider).GetAwaiter().GetResult();
var client = Api.Instance.GetClient();
return client;
});

builder.Services.AddSingleton(x =>
new CartService(
x.GetRequiredService<ICartStore>(),
new RedisCartStore(x.GetRequiredService<ILogger<RedisCartStore>>(), "badhost:1234"),
x.GetRequiredService<IFeatureClient>()
));

// see https://opentelemetry.io/docs/instrumentation/net/getting-started/

Action<ResourceBuilder> appResourceBuilder =
resource => resource
Expand All @@ -65,7 +74,7 @@
.AddRuntimeInstrumentation()
.AddAspNetCoreInstrumentation()
.AddOtlpExporter());

OpenFeature.Api.Instance.AddHooks(new TracingHook());
builder.Services.AddGrpc();
builder.Services.AddGrpcHealthChecks()
.AddCheck("Sample", () => HealthCheckResult.Healthy());
Expand Down
3 changes: 3 additions & 0 deletions src/cartservice/src/cartservice.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
<PackageReference Include="OpenTelemetry.ResourceDetectors.Container" Version="1.0.0-beta.6" />
<PackageReference Include="OpenTelemetry.ResourceDetectors.Host" Version="0.1.0-alpha.2" />
<PackageReference Include="StackExchange.Redis" Version="2.7.17" />
<PackageReference Include="OpenFeature.Contrib.Providers.Flagd" Version="0.1.8" />
<PackageReference Include="OpenFeature.Contrib.Hooks.Otel" Version="0.1.4" />
<PackageReference Include="OpenFeature" Version="1.4.1" />
</ItemGroup>

<ItemGroup>
Expand Down
38 changes: 0 additions & 38 deletions src/cartservice/src/featureflags/FeatureFlagHelper.cs

This file was deleted.

Loading

0 comments on commit ce4ad93

Please sign in to comment.