Skip to content

Commit

Permalink
in memory and Redis cache configuration (#2155)
Browse files Browse the repository at this point in the history
Fix #2075

This implements @BrynCooke's suggestion from #2075. I did not add the
option for introspection because it did not feel very useful, and would
be a breaking change for the configuration format. If we really need to
cache introspection responses, that could be done as part of the whole
response caching feature.

Example configuration:

```yaml
supergraph:
  apq:
    experimental_cache:
      in_memory:
        limit: 512
      redis:
        urls: ["redis://..."]
  query_planning:
    experimental_cache:
      in_memory:
        limit: 512
      redis:
        urls: ["redis://..."]
```
  • Loading branch information
Geoffroy Couprie authored and garypen committed Nov 30, 2022
1 parent 7193bbf commit a4c5255
Show file tree
Hide file tree
Showing 11 changed files with 261 additions and 87 deletions.
24 changes: 23 additions & 1 deletion NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ There are situations where comments and whitespace are not preserved. This may b

By [@bryncooke](https://github.com/bryncooke) in https://github.com/apollographql/router/pull/2116, https://github.com/apollographql/router/pull/2162

### *Experimental* subgraph request retry ([Issue #338](https://github.com/apollographql/router/issues/338), [Issue #1956](https://github.com/apollographql/router/issues/1956))
### *Experimental* 🥼 subgraph request retry ([Issue #338](https://github.com/apollographql/router/issues/338), [Issue #1956](https://github.com/apollographql/router/issues/1956))

Implements subgraph request retries, using Finagle's retry buckets algorithm:
- it defines a minimal number of retries per second (`min_per_sec`, default is 10 retries per second), to
Expand Down Expand Up @@ -216,6 +216,28 @@ traffic_shaping:
By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/2006 and https://github.com/apollographql/router/pull/2160
### *Experimental* 🥼 Caching configuration ([Issue #2075](https://github.com/apollographql/router/issues/2075))
Split Redis cache configuration for APQ and query planning:
```yaml
supergraph:
apq:
experimental_cache:
in_memory:
limit: 512
redis:
urls: ["redis://..."]
query_planning:
experimental_cache:
in_memory:
limit: 512
redis:
urls: ["redis://..."]
```
By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/2155
## 🐛 Fixes
### fix build_docker_image.sh script when using default repo ([PR #2163](https://github.com/apollographql/router/pull/2163))
Expand Down
7 changes: 6 additions & 1 deletion apollo-router/src/axum_factory/axum_http_server_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,12 @@ impl HttpServerFactory for AxumHttpServerFactory {
RF: SupergraphServiceFactory,
{
Box::pin(async move {
let apq = APQLayer::with_cache(DeduplicatingCache::new().await);
let apq = APQLayer::with_cache(
DeduplicatingCache::from_configuration(
&configuration.supergraph.apq.experimental_cache,
)
.await,
);

let all_routers =
make_axum_router(service_factory, &configuration, extra_endpoints, apq)?;
Expand Down
11 changes: 11 additions & 0 deletions apollo-router/src/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ where
}
}

pub(crate) async fn from_configuration(config: &crate::configuration::Cache) -> Self {
Self::with_capacity(
config.in_memory.limit,
#[cfg(feature = "experimental_cache")]
config.redis.as_ref().map(|c| c.urls.clone()),
#[cfg(not(feature = "experimental_cache"))]
None,
)
.await
}

pub(crate) async fn get(&self, key: &K) -> Entry<K, V> {
// waiting on a value from the cache is a potentially long(millisecond scale) task that
// can involve a network call to an external database. To reduce the waiting time, we
Expand Down
110 changes: 57 additions & 53 deletions apollo-router/src/configuration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use serde_json::Map;
use serde_json::Value;
use thiserror::Error;

use crate::cache::DEFAULT_CACHE_CAPACITY;
use crate::configuration::schema::Mode;
use crate::executable::APOLLO_ROUTER_DEV_ENV;
use crate::plugin::plugins;
Expand Down Expand Up @@ -485,16 +486,19 @@ pub(crate) struct Supergraph {
#[serde(default = "default_defer_support")]
pub(crate) preview_defer_support: bool,

#[cfg(feature = "experimental_cache")]
/// URLs of Redis cache used for query planning
pub(crate) cache_redis_urls: Option<Vec<String>>,
/// Configures automatic persisted queries
#[serde(default)]
pub(crate) apq: Apq,

/// Query planning options
#[serde(default)]
pub(crate) query_planning: QueryPlanning,
}

fn default_defer_support() -> bool {
true
}

#[cfg(feature = "experimental_cache")]
#[buildstructor::buildstructor]
impl Supergraph {
#[builder]
Expand All @@ -503,19 +507,20 @@ impl Supergraph {
path: Option<String>,
introspection: Option<bool>,
preview_defer_support: Option<bool>,
cache_redis_urls: Option<Vec<String>>,
apq: Option<Apq>,
query_planning: Option<QueryPlanning>,
) -> Self {
Self {
listen: listen.unwrap_or_else(default_graphql_listen),
path: path.unwrap_or_else(default_graphql_path),
introspection: introspection.unwrap_or_else(default_graphql_introspection),
preview_defer_support: preview_defer_support.unwrap_or_else(default_defer_support),
cache_redis_urls,
apq: apq.unwrap_or_default(),
query_planning: query_planning.unwrap_or_default(),
}
}
}

#[cfg(feature = "experimental_cache")]
#[cfg(test)]
#[buildstructor::buildstructor]
impl Supergraph {
Expand All @@ -525,75 +530,74 @@ impl Supergraph {
path: Option<String>,
introspection: Option<bool>,
preview_defer_support: Option<bool>,
cache_redis_urls: Option<Vec<String>>,
apq: Option<Apq>,
query_planning: Option<QueryPlanning>,
) -> Self {
Self {
listen: listen.unwrap_or_else(test_listen),
path: path.unwrap_or_else(default_graphql_path),
introspection: introspection.unwrap_or_else(default_graphql_introspection),
preview_defer_support: preview_defer_support.unwrap_or_else(default_defer_support),
cache_redis_urls,
apq: apq.unwrap_or_default(),
query_planning: query_planning.unwrap_or_default(),
}
}
}

#[cfg(not(feature = "experimental_cache"))]
#[buildstructor::buildstructor]
impl Supergraph {
#[builder]
pub(crate) fn new(
listen: Option<ListenAddr>,
path: Option<String>,
introspection: Option<bool>,
preview_defer_support: Option<bool>,
) -> Self {
Self {
listen: listen.unwrap_or_else(default_graphql_listen),
path: path.unwrap_or_else(default_graphql_path),
introspection: introspection.unwrap_or_else(default_graphql_introspection),
preview_defer_support: preview_defer_support.unwrap_or_else(default_defer_support),
}
impl Default for Supergraph {
fn default() -> Self {
Self::builder().build()
}
}

#[cfg(not(feature = "experimental_cache"))]
#[cfg(test)]
#[buildstructor::buildstructor]
impl Supergraph {
#[builder]
pub(crate) fn fake_new(
listen: Option<ListenAddr>,
path: Option<String>,
introspection: Option<bool>,
preview_defer_support: Option<bool>,
) -> Self {
Self {
listen: listen.unwrap_or_else(test_listen),
path: path.unwrap_or_else(default_graphql_path),
introspection: introspection.unwrap_or_else(default_graphql_introspection),
preview_defer_support: preview_defer_support.unwrap_or_else(default_defer_support),
}
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub(crate) struct Apq {
pub(crate) experimental_cache: Cache,
}

impl Supergraph {
#[derive(Debug, Clone, Default, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub(crate) struct QueryPlanning {
pub(crate) experimental_cache: Cache,
}

#[derive(Debug, Clone, Default, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]

pub(crate) struct Cache {
/// Configures the in memory cache (always active)
pub(crate) in_memory: InMemoryCache,
#[cfg(feature = "experimental_cache")]
pub(crate) fn cache(&self) -> Option<Vec<String>> {
self.cache_redis_urls.clone()
}
/// Configures and activates the Redis cache
pub(crate) redis: Option<RedisCache>,
}

#[cfg(not(feature = "experimental_cache"))]
pub(crate) fn cache(&self) -> Option<Vec<String>> {
None
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
/// In memory cache configuration
pub(crate) struct InMemoryCache {
/// Number of entries in the Least Recently Used cache
pub(crate) limit: usize,
}

impl Default for Supergraph {
impl Default for InMemoryCache {
fn default() -> Self {
Self::builder().build()
Self {
limit: DEFAULT_CACHE_CAPACITY,
}
}
}

#[cfg(feature = "experimental_cache")]
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
/// Redis cache configuration
pub(crate) struct RedisCache {
/// List of URLs to the Redis cluster
pub(crate) urls: Vec<String>,
}

/// Configuration options pertaining to the sandbox page.
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,10 +605,66 @@ expression: "&schema"
"listen": "127.0.0.1:4000",
"path": "/",
"introspection": false,
"preview_defer_support": true
"preview_defer_support": true,
"apq": {
"experimental_cache": {
"in_memory": {
"limit": 512
}
}
},
"query_planning": {
"experimental_cache": {
"in_memory": {
"limit": 512
}
}
}
},
"type": "object",
"properties": {
"apq": {
"description": "Configures automatic persisted queries",
"default": {
"experimental_cache": {
"in_memory": {
"limit": 512
}
}
},
"type": "object",
"required": [
"experimental_cache"
],
"properties": {
"experimental_cache": {
"type": "object",
"required": [
"in_memory"
],
"properties": {
"in_memory": {
"description": "Configures the in memory cache (always active)",
"type": "object",
"required": [
"limit"
],
"properties": {
"limit": {
"description": "Number of entries in the Least Recently Used cache",
"type": "integer",
"format": "uint",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"introspection": {
"description": "Enable introspection Default: false",
"default": false,
Expand Down Expand Up @@ -636,6 +692,48 @@ expression: "&schema"
"preview_defer_support": {
"default": true,
"type": "boolean"
},
"query_planning": {
"description": "Query planning options",
"default": {
"experimental_cache": {
"in_memory": {
"limit": 512
}
}
},
"type": "object",
"required": [
"experimental_cache"
],
"properties": {
"experimental_cache": {
"type": "object",
"required": [
"in_memory"
],
"properties": {
"in_memory": {
"description": "Configures the in memory cache (always active)",
"type": "object",
"required": [
"limit"
],
"properties": {
"limit": {
"description": "Number of entries in the Least Recently Used cache",
"type": "integer",
"format": "uint",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
Expand Down
Loading

0 comments on commit a4c5255

Please sign in to comment.