-
Notifications
You must be signed in to change notification settings - Fork 245
Add header support for caching #2650
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
base: main
Are you sure you want to change the base?
Conversation
/azp run |
Azure Pipelines successfully started running 6 pipeline(s). |
/azp run |
Azure Pipelines successfully started running 6 pipeline(s). |
/azp run |
Azure Pipelines successfully started running 6 pipeline(s). |
/azp run |
Azure Pipelines successfully started running 6 pipeline(s). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds support for handling caching directives via the HTTP Cache-Control header by incorporating new properties and logic into the SQL query processing and cache service layers. It introduces new cache getter and setter methods in the cache service, adds cache control handling in the SQL query engine, and updates the SQL query structure and REST request context to capture and validate the Cache-Control header.
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
File | Description |
---|---|
src/Core/Services/Cache/DabCacheService.cs | Added TryGet and Set methods to retrieve/store JSON responses in the cache based on a derived cache key. |
src/Core/Resolvers/SqlQueryEngine.cs | Updated the query execution flow to handle cache control options and to throw exceptions when only-if-cached is used with a cache miss. |
src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs | Introduced CacheControlOption property and constants, with logic to extract and validate Cache-Control header values. |
src/Core/Models/RestRequestContexts/RestRequestContext.cs | Added a new property to capture the Cache-Control header from REST requests. |
/azp run |
Azure Pipelines successfully started running 6 pipeline(s). |
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Aniruddh Munde <[email protected]>
/azp run |
Azure Pipelines successfully started running 6 pipeline(s). |
@@ -255,6 +271,7 @@ public SqlQueryStructure( | |||
: this(sqlMetadataProvider, | |||
authorizationResolver, | |||
gQLFilterParser, | |||
httpRequestHeaders: httpContext?.Request.Headers, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can httpContext be null here? If not, do we need httpContext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it can be null here. For GraphQL read operation we intentionally leave this null, and then use that fact to sort out when to process authorization policies. This flow was causing a bunch of tests to fail when I was forcing an httpContext to always get passed through, which is why now I just grab the Headers, and allow the context to remain null when its passed in as such.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it could be null
only for GraphQL read, we need not worried here, right? Since the constructor is actually for Rest only:
public SqlQueryStructure(
RestRequestContext context,
ISqlMetadataProvider sqlMetadataProvider,
also the argument to the constructor itself is not nullable
**HttpContext httpContext)**
switch (structure.CacheControlOption) | ||
{ | ||
// Do not get result from cache even if it exists, still cache result. | ||
case SqlQueryStructure.CACHE_CONTROL_NO_CACHE: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add some automated tests for these control options?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Waiting on consistently using nullable or not nullable httpContext
.
And a test case.
/azp run |
Azure Pipelines successfully started running 6 pipeline(s). |
@@ -102,6 +102,10 @@ public class SqlQueryStructure : BaseSqlQueryStructure | |||
|
|||
public const string CACHE_CONTROL_ONLY_IF_CACHED = "only-if-cached"; | |||
|
|||
public HashSet<string> cacheControlHeaderOptions = new( | |||
new[] { CACHE_CONTROL, CACHE_CONTROL_NO_STORE, CACHE_CONTROL_NO_CACHE, CACHE_CONTROL_ONLY_IF_CACHED }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is CACHE_CONTROL
i.e. Cache-Control
itself a valid value of this option? IMO this should not be in the HashSet since you would be giving out an exception for this value if its actually an invalid value
If its valid, what does it mean?
dataSourceName: queryMetadata.DataSource); | ||
_cache.Set<JsonElement?>(queryMetadata, cacheEntryTtl: runtimeConfig.GetEntityCacheEntryTtl(entityName: structure.EntityName), result); | ||
return ParseResultIntoJsonDocument(result); | ||
// Do not store result even if valid, stil get from cache if available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add a new line before the comment
args: null, | ||
dataSourceName: queryMetadata.DataSource); | ||
return ParseResultIntoJsonDocument(result); | ||
// Only return query response if it exists in cache, return gateway timeout otherwise. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add new line before comment
statusCode: System.Net.HttpStatusCode.GatewayTimeout, | ||
subStatusCode: DataApiBuilderException.SubStatusCodes.ItemNotFound); | ||
return ParseResultIntoJsonDocument(result); | ||
default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add new line before default
result = await _cache.GetOrSetAsync<JsonElement>(queryExecutor, queryMetadata, cacheEntryTtl: runtimeConfig.GetEntityCacheEntryTtl(entityName: structure.EntityName)); | ||
return ParseResultIntoJsonDocument(result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am confused by this, shouldn't there be two different cases, one if CacheControlOption
is null and one for CACHE_CONTROL
?
/// </summary> | ||
/// <typeparam name="JsonElement">The type of value in the cache</typeparam> | ||
/// <param name="queryMetadata">Metadata used to create a cache key or fetch a response from the database.</param> | ||
/// <param name="cacheEntryTtl">Number of seconds the cache entry should be valid before eviction.</param> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't there be a section for cacheValue
?
Why make this change?
Closes #2604
What is this change?
Inside of the
SqlQueryStructure
, there is a private constructor, that all of the public constructors will call. This private constructor offers a single point where all of the queries will bottleneck, and therefore we add the cache control information to the query structure at this point. Then when we are in theSqlQueryEngine
, we can check for this cache control information and handle the query appropriately.The cache control options can be found
here: #2253
and here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control#request_directives
But for reference, they are
no-cache
,no-store
,only-if-cached
, which will mean we do not get from the cache, we do not store in the cache, and if there is a cache miss we return gateway timeout, respectively.How was this tested?
Run against test suite.
Sample Request(s)
To test you need to include the relevant cache headers in the request, "no-cache", "no-store", or "only-if-cached"