Skip to content

Commit

Permalink
[adservice] add adServiceManualGC featureflag (#1463)
Browse files Browse the repository at this point in the history
* [adservice] add adServiceManualGC featureflag

* update changelog

* [adservice] address PR comment

---------

Co-authored-by: Pierre Tessier <[email protected]>
  • Loading branch information
EislM0203 and puckpuck authored Mar 25, 2024
1 parent 73bdd52 commit 5c6b801
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ the release.
([#1448](https:/open-telemetry/opentelemetry-demo/pull/1448))
* [cartservice] update .NET to .NET 8.0.3
([#1460](https:/open-telemetry/opentelemetry-demo/pull/1460))
* [adservice] add adServiceManualGC feature flag
([#1463](https:/open-telemetry/opentelemetry-demo/pull/1463))
* [frontendproxy] remove deprecated start_child_span option
([#1469](https:/open-telemetry/opentelemetry-demo/pull/1469))
* [currency] fix metric name
Expand Down
22 changes: 19 additions & 3 deletions src/adservice/src/main/java/oteldemo/AdService.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import oteldemo.Demo.Ad;
import oteldemo.Demo.AdRequest;
import oteldemo.Demo.AdResponse;
import oteldemo.problempattern.GarbageCollectionTrigger;
import dev.openfeature.contrib.providers.flagd.FlagdOptions;
import dev.openfeature.contrib.providers.flagd.FlagdProvider;
import dev.openfeature.sdk.Client;
Expand Down Expand Up @@ -127,6 +128,9 @@ private enum AdResponseType {

private static class AdServiceImpl extends oteldemo.AdServiceGrpc.AdServiceImplBase {

private static final String ADSERVICE_FAILURE = "adServiceFailure";
private static final String ADSERVICE_MANUAL_GC_FEATURE_FLAG = "adServiceManualGc";

private AdServiceImpl() {}

/**
Expand Down Expand Up @@ -177,10 +181,16 @@ public void getAds(AdRequest req, StreamObserver<AdResponse> responseObserver) {
Attributes.of(
adRequestTypeKey, adRequestType.name(), adResponseTypeKey, adResponseType.name()));

if (checkAdFailure()) {
if (getFeatureFlagEnabled(ADSERVICE_FAILURE)) {
throw new StatusRuntimeException(Status.RESOURCE_EXHAUSTED);
}

if (getFeatureFlagEnabled(ADSERVICE_MANUAL_GC_FEATURE_FLAG)) {
logger.warn("Feature Flag " + ADSERVICE_MANUAL_GC_FEATURE_FLAG + " enabled, performing a manual gc now");
GarbageCollectionTrigger gct = new GarbageCollectionTrigger();
gct.doExecute();
}

AdResponse reply = AdResponse.newBuilder().addAllAds(allAds).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
Expand All @@ -193,12 +203,18 @@ public void getAds(AdRequest req, StreamObserver<AdResponse> responseObserver) {
}
}

boolean checkAdFailure() {
/**
* Retrieves the status of a feature flag from the Feature Flag service.
*
* @param ff The name of the feature flag to retrieve.
* @return {@code true} if the feature flag is enabled, {@code false} otherwise or in case of errors.
*/
boolean getFeatureFlagEnabled(String ff) {
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);
Boolean boolValue = client.getBooleanValue(ff, false);
return boolValue;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package oteldemo.problempattern;

import java.lang.management.ManagementFactory;
import java.util.concurrent.TimeUnit;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
* The GarbageCollectionTrigger class is responsible for triggering manual garbage collection
* at specified intervals to simulate memory pressure and measure the impact on performance.
*/
public class GarbageCollectionTrigger {
private static final Logger logger = LogManager.getLogger(GarbageCollectionTrigger.class.getName());

private final long gc_delay;
private final int finalize_delay;
private final int maxObjects;

private long lastGC = 0;

private final MemoryUtils memUtils;

/**
* Constructs a new GarbageCollectionTrigger with default values.
*/
public GarbageCollectionTrigger() {
memUtils = new MemoryUtils(ManagementFactory.getMemoryMXBean());
gc_delay = TimeUnit.SECONDS.toMillis(10);
finalize_delay = 500;
maxObjects = 500000;
}

/**
* Triggers manual garbage collection at specified intervals and measures the impact on performance.
* It creates Entry objects to fill up memory and initiates garbage collection.
*/
public void doExecute() {
if (System.currentTimeMillis() - lastGC > gc_delay) {
logger.info("Triggering a manual garbage collection, next one in " + (gc_delay/1000) + " seconds.");
// clear old data, we want to clear old Entry objects, because their finalization is expensive
System.gc();

long total = 0;
for (int i = 0; i < 10; i++) {
while (memUtils.getHeapUsage() < 0.9 && memUtils.getObjectPendingFinalizationCount() < maxObjects) {
new Entry();
}
long start = System.currentTimeMillis();
System.gc();
total += System.currentTimeMillis() - start;
}
logger.info("The artificially triggered GCs took: " + total + " ms");
lastGC = System.currentTimeMillis();
}

}

/**
* The Entry class represents objects created for the purpose of triggering garbage collection.
*/
private class Entry {
/**
* Overrides the finalize method to introduce a delay, simulating finalization during garbage collection.
*
* @throws Throwable If an exception occurs during finalization.
*/
@SuppressWarnings("removal")
@Override
protected void finalize() throws Throwable {
TimeUnit.MILLISECONDS.sleep(finalize_delay);
super.finalize();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package oteldemo.problempattern;

import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;


/**
* This class provides JVM heap related utility methods.
*/
public class MemoryUtils {

private static final Logger logger = LogManager.getLogger(MemoryUtils.class.getName());

private static final long NO_HEAP_LIMIT = -1;

private final MemoryMXBean memoryBean;

/**
* @param memoryBean defines which {@link MemoryMXBean} is to use
*/
public MemoryUtils(MemoryMXBean memoryBean) {
this.memoryBean = memoryBean;
}


/**
* @return The current heap usage as a decimal number between 0.0 and 1.0.
* That is, if the returned value is 0.85, 85% of the max heap is used.
*
* If no max heap is set, the method returns -1.0.
*/
public double getHeapUsage() {
MemoryUsage heapProps = memoryBean.getHeapMemoryUsage();
long heapUsed = heapProps.getUsed();
long heapMax = heapProps.getMax();

if (heapMax == NO_HEAP_LIMIT) {
if (logger.isDebugEnabled()) {
logger.debug("No maximum heap is set");
}
return NO_HEAP_LIMIT;
}


double heapUsage = (double) heapUsed / heapMax;
if (logger.isDebugEnabled()) {
logger.debug("Current heap usage is {0} percent" + (heapUsage * 100));
}
return heapUsage;
}

/**
* see {@link MemoryMXBean#getObjectPendingFinalizationCount()}
*/
public int getObjectPendingFinalizationCount() {
return memoryBean.getObjectPendingFinalizationCount();
}
}
9 changes: 9 additions & 0 deletions src/flagd/demo.flagd.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@
},
"defaultVariant": "off"
},
"adServiceManualGc": {
"description": "Triggers full manual garbage collections in the ad service",
"state": "ENABLED",
"variants": {
"on": true,
"off": false
},
"defaultVariant": "off"
},
"adServiceFailure": {
"description": "Fail ad service",
"state": "ENABLED",
Expand Down

0 comments on commit 5c6b801

Please sign in to comment.