-
I’m observing an issue where fetching feature flags initially sends back the default value rather than the intended rollout value. This can affect real-time logic that depends on precise feature values at the start of the session. Sharing code where i was able to reproduce this issue: const {
EdgeFeatureHubConfig,
Readyness,
FHLog,
StrategyAttributeCountryName
} = require('featurehub-javascript-node-sdk');
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
FHLog.fhLog.trace = (...args) => console.log(args);
async function init() {
const fhConfig = new EdgeFeatureHubConfig(process.env.FEATUREHUB_EDGE_URL, process.env.FEATUREHUB_CLIENT_API_KEY);
fhConfig.addReadinessListener((readyness, firstTimeReady) => {
if (firstTimeReady) {
console.log("Connected to feature hub");
}
if (readyness === Readyness.NotReady) {
console.log("Connection is not ready");
}
if (readyness === Readyness.Failed) {
console.log("Unable to connect to feature hub");
}
});
fhConfig.init();
let fhClient = fhConfig.newContext();
app.get('/healthz', (req, res) => {
console.log(fhConfig.readyness);
if (fhConfig.readyness === Readyness.Ready) {
res.status(200).json({ done: 'ok' });
} else {
res.status(500).json({ done: 'not ready' });
}
});
app.get("/", async (req, res) => {
await fhClient.country(StrategyAttributeCountryName.Iceland).build();
console.log(
fhConfig.readyness,
fhClient.feature('booleanFlag').getBoolean(),
fhClient.feature('StringFlag').getString()
);
if (fhClient.feature('booleanFlag').getBoolean()) {
res.status(200).json({ "hello": "hello" });
} else {
res.status(401).json({ "hello": "hello" });
}
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}
init(); |
Beta Was this translation helpful? Give feedback.
Replies: 8 comments 2 replies
-
So if you have a string value set with a default of "blue" and strategy of "yellow" (for country Iceland) and once the app is healthy and has state, your app prints "blue"? We also need to know which version of featurehub plz :-) |
Beta Was this translation helpful? Give feedback.
-
Yes i use /healthz to check if the feature hub is ready. Then when i hit / i get blue instead of yellow first time then it switched to yellow on further tries. |
Beta Was this translation helpful? Give feedback.
-
Hi there, @IrinaSouth is taking a look at this |
Beta Was this translation helpful? Give feedback.
-
Hi @barath121 I tried to replicate but all appears to work for me. I have used your code exactly, just added some prints:
And when I loaded http://localhost:3000/ it gave me the following on first time: And the strategy that I have set in featurehub on the "StringFlag" feature looks like this: You will also see some log from the sdk like this that might confused you, which will show default value and strategies array, so the strategy value won't be displayed:
|
Beta Was this translation helpful? Give feedback.
-
Updated code:
|
Beta Was this translation helpful? Give feedback.
-
@IrinaSouth I am trying to use the code again but still facing similar issue. const {
EdgeFeatureHubConfig,
FeatureHubPollingClient,
Readyness,
FHLog,
featurehubMiddleware
} = require('featurehub-javascript-node-sdk');
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
FHLog.fhLog.trace = (...args) => console.log(args);
async function init() {
const fhConfig = new EdgeFeatureHubConfig(process.env.FEATUREHUB_EDGE_URL, process.env.FEATUREHUB_CLIENT_API_KEY);
let fhClient = fhConfig.newContext();
// fhConfig.addReadinessListener((ready, firstTime) => {
// }, true);
fhConfig.init();
app.use(async (req,res,next)=>{
req.featureContext = await fhClient.attributeValue('platform_version', req.headers.platform_version).build();
next();
})
app.get('/healthz', (req, res) => {
console.log(fhConfig.readyness);
if (fhConfig.readyness === Readyness.Ready) {
res.status(200).json({ done: 'ok' });
} else {
res.status(500).json({ done: 'not ready' });
}
});
app.get("/", async (req, res) => {
if (fhConfig.readyness !== Readyness.Ready) {
console.log('Feature flags not ready yet');
return res.status(503).json({ error: 'Feature flags not ready yet' });
}
let featureFlag;
const responseObject = {
key1: 1,
key2: 2,
key3: 3
}
featureFlag = req.featureContext.feature('enable_key4').getBoolean();
if(featureFlag){
responseObject["key4"] = 4;
}
res.status(200).json(responseObject);
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}
init(); Python: from fastapi import FastAPI, Request, HTTPException
from featurehub_sdk.featurehub_config import FeatureHubConfig
import os
import uvicorn
app = FastAPI()
FEATUREHUB_EDGE_URL = os.getenv("FEATUREHUB_EDGE_URL")
FEATUREHUB_CLIENT_API_KEY = os.getenv("FEATUREHUB_CLIENT_API_KEY")
fh_config = FeatureHubConfig(FEATUREHUB_EDGE_URL, [FEATUREHUB_CLIENT_API_KEY])
fh_config.use_polling_edge_service()
@app.on_event("startup")
async def startup_event():
await fh_config.init()
@app.middleware("http")
async def feature_context_middleware(request: Request, call_next):
platform_version = request.headers.get("platform_version")
print(platform_version)
context = await fh_config.new_context().attribute_values("platform_version", platform_version).build()
request.state.feature_context = context
response = await call_next(request)
return response
@app.get("/healthz")
async def healthz():
print(fh_config.repository().is_ready())
if fh_config.repository().is_ready():
return {"done": "ok"}
else:
raise HTTPException(status_code=500, detail="not ready")
@app.get("/")
async def root(request: Request):
response_object = {
"key1": 1,
"key2": 2,
"key3": 3
}
feature_flag = request.state.feature_context.feature("enable_key4").get_flag
print(feature_flag)
if feature_flag:
response_object["key4"] = 4
return response_object
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", 3000))) |
Beta Was this translation helpful? Give feedback.
-
Heya - your node code isn't right unfortunately. You cannot have a single context for the entire application, you need to have a context per request, you are mixing browser style coding (where there is a single context) with node style (which is where there is one per request). For node you need to be making sure you are using a client evaluated key, and in your middleware, you need to ask for a new context - so app.use(() => req.featureContext = fhClient.newContext().attributeValue......build()); If you don't do that, you are changing the global config and it will replace the repository and go back to being blank. |
Beta Was this translation helpful? Give feedback.
-
@IrinaSouth Thanks this works |
Beta Was this translation helpful? Give feedback.
Heya - your node code isn't right unfortunately. You cannot have a single context for the entire application, you need to have a context per request, you are mixing browser style coding (where there is a single context) with node style (which is where there is one per request).
For node you need to be making sure you are using a client evaluated key, and in your middleware, you need to ask for a new context - so app.use(() => req.featureContext = fhClient.newContext().attributeValue......build());
If you don't do that, you are changing the global config and it will replace the repository and go back to being blank.