Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 All @@ -213,6 +213,28 @@ traffic_shaping:

By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/2006

### *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

### Improve errors when subgraph returns non-GraphQL response with a non-2xx status code ([Issue #2117](https://github.com/apollographql/router/issues/2117))
Expand Down
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,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub(crate) experimental_cache: Cache,
#[serde(rename = "experimental_cache")]
pub(crate) cache: Cache,

It will be one line change if we stabilize it

}

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

Choose a reason for hiding this comment

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

Why do we need 2 different structs instead of one ?

}

#[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