Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new CacheStrategy & CacheMiddleware classes #3

Merged
merged 30 commits into from
Sep 26, 2024

Conversation

jblz
Copy link
Member

@jblz jblz commented Aug 20, 2024

  • Add POST to the request methods that we can cache, but default to not caching them
  • Add a method for queries to declare a TTL via get_cache_ttl method
  • Add debug logging to fetch & cache
  • Add mechanism to bypass cache via remote_data_blocks_bypass_cache filter

This isn't perfect yet, but it gives us a jumping-off point for fine-tuning our caching strategy.

Cold Cache Warm Cache
Screenshot 2024-08-21 at 9 32 47 AM Screenshot 2024-08-21 at 9 35 25 AM

@jblz jblz force-pushed the add/guzzle-caching-strategy branch 2 times, most recently from addcd13 to 22d148b Compare August 21, 2024 13:29
@jblz jblz marked this pull request as ready for review August 21, 2024 13:36
@jblz jblz changed the title Add new CacheStrategy class Add new CacheStrategy & CacheMiddleware classes Aug 21, 2024
}

protected function should_bypass_cache( RequestInterface $request ) {
$bypass = $request->getHeader( self::HEADER_BYPASS_CACHE );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can inspect the request here to e.g. put tighter scrutiny on POST requests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. We discussed inspecting the GraphQL operation for mutations and setting those to not cache.

Additionally, we discussed using a method on e.g. the QueryContext that we leverage to bypass the cache.

use Psr\Http\Message\RequestInterface;
use RemoteDataBlocks\Logging\LoggerManager;

class CacheStrategy extends GreedyCacheStrategy {
Copy link
Contributor

@mhsdef mhsdef Aug 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth s/CacheStrategy/CustomCacheStrategy?

Just thinking, in other context, like when instantiated, some sort of qualifier like that might convey more meaning. And RemoteDataBlocksCacheStrategy feels a tad long even for me (though I'm open to it!).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the PHP namespace will suffice for now to indicate these are bespoke & not from the composer lib. If / when we have more than one implementation of the strategy / middleware classes, that's probably a good time to be more specific about the naming, IMO.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up prefixing these with Rdb -- feel free to push a change if anyone has a preference.

Copy link
Contributor

@mhsdef mhsdef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, didn't test.

It would be nice to have unit tests on the cache strategy class. Maybe take Cursor for a spin on that if you haven't tried it yet? I suspect it'd do nicely with a class that's so direct as that one.

$uri_string = $uri->getScheme() . '://' . $uri->getHost() . $uri->getPath();
$method = $request->getMethod();
$body = $request->getBody()->getContents();
return $method . ' ' . $uri_string . ' ' . $body;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to log the content of the $body?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to log the content of the $body?

In production, we'd not want to do this for sure. Since we're just logging to the debug log, it's probably not all that critical. At least for now as we debug the middleware and strategy.

$logger = LoggerManager::instance();

if ( $this->should_bypass_cache( $request ) ) {
$logger->debug( 'Bypassing cache: ' . self::getRequestString( $request ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have any prefix in debugging messages which will make the search easier?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pass a string to LoggerManager::instance(), it accepts a namespace

Copy link
Member Author

@jblz jblz Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chriszarate Seems like the Logger constructor / create fn does, but not the static LoggerManager::instance fn:

public static function instance(): Logger {
if ( null === self::$instance ) {
self::$instance = Logger::create( self::$log_namespace );
}
return self::$instance;
}

...unless I'm missing something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to not just use the Logger::create fn here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right. I guess we already have a namespace and don't really need one? Or we want multi-level namespaces?

Copy link
Member Author

@jblz jblz Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @mehmoodak's suggestion was for multi-level namespaces to be able to more-easily identify that the debug log came from e.g. the HTTP Client module (similar to how the debug npm package allows multi-level prefixing / filtering.

We could drill in via Query Monitor like this:

Screenshot 2024-08-23 at 5 19 40 PM

I think I'll file it to an issue for an enhancement outside this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#12

@mehmoodak
Copy link
Member

+1 on @mhsdef suggestion of testing.

Copy link
Member Author

@jblz jblz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to tests for these. I wanted to make sure folks agreed w/ the direction before writing them. I'll see how feasible it is.

use Psr\Http\Message\RequestInterface;
use RemoteDataBlocks\Logging\LoggerManager;

class CacheStrategy extends GreedyCacheStrategy {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the PHP namespace will suffice for now to indicate these are bespoke & not from the composer lib. If / when we have more than one implementation of the strategy / middleware classes, that's probably a good time to be more specific about the naming, IMO.

$uri_string = $uri->getScheme() . '://' . $uri->getHost() . $uri->getPath();
$method = $request->getMethod();
$body = $request->getBody()->getContents();
return $method . ' ' . $uri_string . ' ' . $body;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to log the content of the $body?

In production, we'd not want to do this for sure. Since we're just logging to the debug log, it's probably not all that critical. At least for now as we debug the middleware and strategy.

}

protected function should_bypass_cache( RequestInterface $request ) {
$bypass = $request->getHeader( self::HEADER_BYPASS_CACHE );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. We discussed inspecting the GraphQL operation for mutations and setting those to not cache.

Additionally, we discussed using a method on e.g. the QueryContext that we leverage to bypass the cache.

$logger = LoggerManager::instance();

if ( $this->should_bypass_cache( $request ) ) {
$logger->debug( 'Bypassing cache: ' . self::getRequestString( $request ) );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

@jblz jblz force-pushed the add/guzzle-caching-strategy branch from 5495e0a to 8ae5f3b Compare August 27, 2024 21:38
@jblz jblz requested a review from mhsdef August 27, 2024 21:38
@@ -11,6 +11,8 @@
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\RequestException;
use Kevinrob\GuzzleCache\Storage\VolatileRuntimeStorage;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this Kevinrob person has some good adjectives, A++

/**
* @var array<string, bool>
*/
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase, SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are needed to comply with the parent class' declaration

@jblz jblz requested a review from chriszarate September 25, 2024 15:55
@jblz
Copy link
Member Author

jblz commented Sep 25, 2024

@chriszarate want to give this another once over?

Copy link
Member

@chriszarate chriszarate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, just had one suggestion (to improve my own code) :)

@jblz jblz merged commit cb3fb16 into trunk Sep 26, 2024
10 checks passed
@jblz jblz deleted the add/guzzle-caching-strategy branch September 26, 2024 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants