Cache doesn't get updated in [Case 2]
First, a few sentences of what I am trying to do:
I want to use "Local Evaluation"
Each time I want to get flags for users, I would like to hit the cache always.
There will be a cron job that will call an API endpoint in my code, which will update the cache with new data every X interval. (so end users don't experience lag)
Here is what I did
If I have a index.php file:
<?php
$startTime = microtime(true);
$featureFlags = new FeatureFlags();
$userFeatureFlags = $featureFlags->forUser();
$endTime = microtime(true);
$executionTime = number_format($endTime - $startTime, 6);
var_dump($executionTime);
echo "<br>";
var_dump($userFeatureFlags);
die();
And a FeatureFlags class that looks like this:
<?php
use Flagsmith\Flagsmith;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Psr16Cache;
class FeatureFlags {
private Flagsmith $flagsmith;
function __construct() {
$this->flagsmith = (new Flagsmith('SERVER_API_KEY', null, null, 10))
->withCache(new Psr16Cache(new FilesystemAdapter('featureFlagsCache')));
$this->flagsmith->updateEnvironment();
}
function forUser() {
$allIdentityFlags = $this->flagsmith->getIdentityFlags("1", (object) [
'instanceType' => "SomeThing"
])->flags;
return $allIdentityFlags;
}
}
- I did install
composer require flagsmith/flagsmith-php-client guzzlehttp/guzzle symfony/cache
Case 1 (Works, but has latency due to a blocking request to Flagsmith if cache is stale)
when we call ->updateEnvironment() after the new Flagsmith
if the cache expired, a request is made to Flagsmith and the response will update the cache successfully.
function __construct() {
$this->flagsmith = (new Flagsmith('SERVER_API_KEY', null, null, 10))
->withCache(new Psr16Cache(new FilesystemAdapter('featureFlagsCache')));
$this->flagsmith->updateEnvironment();
}
This is fine, but after the cache expires, the next user visiting the page will experience a slower response.
I want to remove the blocking API call to Flagsmith, by introducing a cron job that will update the cache. Lets see tha Case 2.
Case 2 (doesn't work, but no latency because the API Flagsmith was moved to a cron job)
I have created an API endpoint POST /invalidate-cache, where I do:
// this is what the /invalidate-cache call does
$featureFlags = new FeatureFlags();
$featureFlags->flagsmith->updateEnvironment();
A cron process will call that API every 60 seconds.
<?php
use Flagsmith\Flagsmith;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Psr16Cache;
class FeatureFlags {
public Flagsmith $flagsmith;
function __construct() {
- $this->flagsmith = (new Flagsmith('SERVER_API_KEY', null, null, 10))
+ $this->flagsmith = (new Flagsmith('SERVER_API_KEY', null, null, 1)) // need to provide environmentTtl for localEvaluation to be `true`, but I reduced it to 1 second
->withCache(new Psr16Cache(new FilesystemAdapter('featureFlagsCache')));
- $this->flagsmith->updateEnvironment();
}
function forUser() {
$allIdentityFlags = $this->flagsmith->getIdentityFlags("1", (object) [
'instanceType' => "SomeThing"
])->flags;
return $allIdentityFlags;
}
}
I have noticed that this doesn't work, and by that I mean:
- On the Flagsmith dashboard, there is a flag
"myFlag"disabled. - user_1 lands on the page, because there was no cache, FlagSmith API will be called and the response will be put in the cache with the value
"myFlag" disabled - user_2 lands on the page, because there is a cache now, the response for him will be fast. And the cache will contain
"myFlag" disabled. - Admin changes the flag in the dashboard and now
"myFlag"isenabled. - user_3 lands on the page, because there is cache now, the response for him will be fast. and the cache will contain
"myFlag" disabled - cron job call
POST /invalidate-cachebecause we specified1forenvironmentTtlthe php should make a call to the Flagsmith api and put the response in the cache, and the cache should have the value"myFlag" enabled - user_3 lands on the page, because there is cache now, the response for him will be fast. and the cache:
Expected:
should contain
"myFlag" enabledActual: it contains"myFlag" disabled
Question:
If I have:
class FeatureFlags {
function __construct() {
$this->flagsmith = (new Flagsmith('SERVER_API_KEY', null, null, 1))
->withCache(new Psr16Cache(new FilesystemAdapter('featureFlagsCache')));
}
}
and later, in some API endpoint, call:
$featureFlags = new FeatureFlags();
$featureFlags->flagsmith->updateEnvironment();
Given that environmentTtl will have a value of 1
That should cause the cache to be considered stale immediately.
so next time we call flagsmith->updateEnvironment(); from an API (like POST /invalidate-cache),
I would expect the Flagsmith API to be called and that the response to be put in cache, so the cache will contain fresh data...
function forUser() {
$allIdentityFlags = $this->flagsmith->getIdentityFlags("1", (object) [
'instanceType' => "SomeThing"
])->flags;
return $allIdentityFlags;
}
It doesn't function like that currently, the cache, for unknown reasons, still contains old data.
flagsmith-php-client version: v4.4.0
After tying out the php-flagsmith-client. I did experience a quite a few issues with it...
enableLocalEvaluation will only be enabled if I provide environmentTtl is provided through the constructor new Flagsmith("KEY", null, null, 100) but not when you do (new Flagsmith("KEY"))->->withEnvironmentTtl(100)... because $this->enableLocalEvaluation is only set in the constructor.
class Flagsmith
{
public function __construct(
string $apiKey = null,
string $host = null,
object $customHeaders = null,
int $environmentTtl = null,
...
) {
$this->enableLocalEvaluation = !is_null($environmentTtl);
Overall, php-flagsmith-client did cause me some issues, nothing that cannot be fixed, but would you suggest me to use a different flagsmith client, like Java for example, that has fewer issues?
I am also not crossing the possibility that my code is to blame :D, my implementation is above, and I did follow the docs. If you see something off, please do say.
Hi @predrag-nikolic-kombinat-dev , this does seem like odd behaviour (and I think is somewhat linked to the unclear documentation as mentioned in #99).
From looking into the implementation, I am surprised that in case 1 you are seeing any blocking behaviour as I cannot see any logic that would trigger a new request to the API, even after the environmentTtl has expired. From what I can tell, the environmentTtl is only used for the request to get the environment document here, but then the response from that request is used to build the environment model which should then be cached against the client object, seemingly without any expiry time set.
Regarding case 2, I certainly would expect that to work as you have described.
Could you provide a full, minimal example of a PHP project (perhaps using a gist) to reproduce the issues that you're having and we can investigate further? The snippets you have provided are useful, but don't constitute a reproducible example at this point.
hello @matthewelwell,
Here are the minimal examples: php-flagsmith-repo_case_1.zip php-flagsmith-repo_case_2.zip
Setup
For both example the setup is:
- unizip them
- run
composer install - update
const SERVER_API_KEY = "XXX";with your Server API key - run
leaf serve - visit
http://localhost:5500/http://localhost:5500/will dispay the execution time, and the fetched flags
for the case 2, there is an additional GET http://localhost:5500/invalidate-cache (for test purposes this is a GET request, because it is easier to copy paste in the browser url bar)
, I am surprised that in case 1 you are seeing any blocking behaviour as I cannot see any logic that would trigger a new request to the API, even after the environmentTtl has expired.
For case 1, when you refresh the page, each 10 seconds you will notice that the request will take longer. For me, I was not suprised to see a slower request there, but do keep me updated with your findings.
Both examples do provide the behavior I explained in this issue. But if you have any additional questions or need more info, ask. :)
Thanks very much for providing the examples - we will look into these and get back to you.