Skip to content

Commit

Permalink
Remove main_rt
Browse files Browse the repository at this point in the history
Add `Executable::builder`
  • Loading branch information
bryn committed Jun 14, 2022
1 parent 6b5151e commit 2f84563
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 125 deletions.
31 changes: 24 additions & 7 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,36 @@ This is a breaking change since slightly invalid input might have validated befo

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

### Entry point improvements ([PR #1227](https://github.com/apollographql/router/pull/1227)) ([PR #1234](https://github.com/apollographql/router/pull/1234))
`ApolloRouterBuilder` has been migrated to `buildstructor` for consistency with other code.
Calls to `ApolloRouterBuilder::default()` should be migrated to `ApolloRouter::builder`.
`FederatedServerHandle` has been renamed to `ApolloRouterHandle`.
### Entry point improvements ([PR #1227](https://github.com/apollographql/router/pull/1227)) ([PR #1234](https://github.com/apollographql/router/pull/1234)) ([PR #1239](https://github.com/apollographql/router/pull/1239))

The interfaces around the entry point have been improved for naming consistency and to enable reuse when customization is required.

Most users will continue to use:
```rust
apollo_router::main()
```

However, if you want to specify your own tokio runtime and or provide some extra customization to configuration/schema/shutdown then you may use `Executable::builder()` to override behavior.

```rust
use apollo_router::Executable;
Executable::builder()
.runtime(runtime) // Optional
.router_builder_fn(|configuration, schema| ...) // Optional
.start()?
```

Migration tips:
* Calls to `ApolloRouterBuilder::default()` should be migrated to `ApolloRouter::builder`.
* `FederatedServerHandle` has been renamed to `ApolloRouterHandle`.
* The ability to supply your own `RouterServiceFactory` has been removed.
* `StateListener`. This made the internal state machine unnecessarily complex. `listen_address()` remains on `ApolloRouterHandle`.
* `FederatedServerHandle#shutdown()` has been removed. Instead, dropping `ApolloRouterHandle` will cause the router to shutdown.
* `FederatedServerHandle#ready()` has been renamed to `FederatedServerHandle#listen_address()`, it will return the address when the router is ready to serve requests.
* `FederatedServerHandle::shutdown()` has been removed. Instead, dropping `ApolloRouterHandle` will cause the router to shutdown.
* `FederatedServerHandle::ready()` has been renamed to `FederatedServerHandle::listen_address()`, it will return the address when the router is ready to serve requests.
* `FederatedServerError` has been renamed to `ApolloRouterError`.
* `main_rt` should be migrated to `Executable::builder()`

By [@bryncooke](https://github.com/bryncooke) in https://github.com/apollographql/router/pull/1227 https://github.com/apollographql/router/pull/1234
By [@bryncooke](https://github.com/bryncooke) in https://github.com/apollographql/router/pull/1227 https://github.com/apollographql/router/pull/1234 https://github.com/apollographql/router/pull/1239

## 🚀 Features
### Add trace logs for parsing recursion consumption ([PR #1222](https://github.com/apollographql/router/pull/1222))
Expand Down
239 changes: 135 additions & 104 deletions apollo-router/src/executable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ impl fmt::Display for ProjectDir {

/// This is the main router entrypoint.
///
/// It effectively builds a tokio runtime and runs `rt_main()`.
///
/// Refer to the examples if you would like how to run your own router with plugins.
pub fn main() -> Result<()> {
let mut builder = tokio::runtime::Builder::new_multi_thread();
Expand All @@ -135,104 +133,136 @@ pub fn main() -> Result<()> {
builder.worker_threads(nb);
}
let runtime = builder.build()?;
runtime.block_on(rt_main())
Executable::builder().runtime(runtime).start()
}

/// If you already have a tokio runtime, you can spawn the router like this:
///
/// ```no_run
/// #[tokio::main]
/// async fn main() -> anyhow::Result<()> {
/// apollo_router::rt_main().await
/// }
/// ```
pub async fn rt_main() -> Result<()> {
let opt = Opt::parse();

if opt.version {
println!("{}", std::env!("CARGO_PKG_VERSION"));
return Ok(());
/// Entry point into creating a router executable.
pub struct Executable {}

#[buildstructor::buildstructor]
impl Executable {
/// Build an executable which can be blockingly started.
/// You may optionally supply a tokio `runtime` and `router_builder_fn` to override building of the router.
///
/// ```no_run
/// use apollo_router::{ApolloRouter, Executable, ShutdownKind};
/// # use anyhow::Result;
/// # fn main()->Result<()> {
/// # let runtime = tokio::runtime::Runtime::new().unwrap();
/// Executable::builder()
/// .runtime(runtime)
/// .router_builder_fn(|configuration, schema| ApolloRouter::builder()
/// .configuration(configuration)
/// .schema(schema)
/// .shutdown(ShutdownKind::CtrlC)
/// .build())
/// .start()
/// # }
/// ```
/// Note that if you do not specify a runtime you must be in the context of an existing tokio runtime.
///
#[builder(entry = "builder", exit = "start")]
pub fn build(
runtime: Option<tokio::runtime::Runtime>,
router_builder_fn: Option<fn(ConfigurationKind, SchemaKind) -> ApolloRouter>,
) -> Result<()> {
match runtime {
None => tokio::runtime::Handle::current().block_on(Executable::run(router_builder_fn)),
Some(runtime) => runtime.block_on(Executable::run(router_builder_fn)),
}
}

copy_args_to_env();
async fn run(
router_builder_fn: Option<fn(ConfigurationKind, SchemaKind) -> ApolloRouter>,
) -> Result<()> {
let opt = Opt::parse();

if opt.schema {
let schema = generate_config_schema();
println!("{}", serde_json::to_string_pretty(&schema)?);
return Ok(());
}
if opt.version {
println!("{}", std::env!("CARGO_PKG_VERSION"));
return Ok(());
}

// This is more complex than I'd like it to be. Really, we just want to pass
// a FmtSubscriber to set_global_subscriber(), but we can't because of the
// generic nature of FmtSubscriber. See: https://github.com/tokio-rs/tracing/issues/380
// for more details.
let builder = tracing_subscriber::fmt::fmt().with_env_filter(
EnvFilter::try_new(&opt.log_level).context("could not parse log configuration")?,
);

let subscriber: RouterSubscriber = if atty::is(atty::Stream::Stdout) {
RouterSubscriber::TextSubscriber(builder.finish())
} else {
RouterSubscriber::JsonSubscriber(builder.json().finish())
};

set_global_subscriber(subscriber)?;

GLOBAL_ENV_FILTER.set(opt.log_level).unwrap();

let current_directory = std::env::current_dir()?;

let configuration = opt
.config_path
.as_ref()
.map(|path| {
let path = if path.is_relative() {
current_directory.join(path)
} else {
path.to_path_buf()
};

ConfigurationKind::File {
path,
watch: opt.hot_reload,
delay: None,
}
})
.unwrap_or_else(|| ConfigurationKind::Instance(Configuration::builder().build().boxed()));
let apollo_router_msg = format!("Apollo Router v{} // (c) Apollo Graph, Inc. // Licensed as ELv2 (https://go.apollo.dev/elv2)", std::env!("CARGO_PKG_VERSION"));
let schema = match (opt.supergraph_path, opt.apollo_key) {
(Some(supergraph_path), _) => {
tracing::info!("{apollo_router_msg}");
setup_panic_handler();

let supergraph_path = if supergraph_path.is_relative() {
current_directory.join(supergraph_path)
} else {
supergraph_path
};
SchemaKind::File {
path: supergraph_path,
watch: opt.hot_reload,
delay: None,
}
copy_args_to_env();

if opt.schema {
let schema = generate_config_schema();
println!("{}", serde_json::to_string_pretty(&schema)?);
return Ok(());
}
(None, Some(apollo_key)) => {
tracing::info!("{apollo_router_msg}");
let apollo_graph_ref = opt.apollo_graph_ref.ok_or_else(||anyhow!("cannot fetch the supergraph from Apollo Studio without setting the APOLLO_GRAPH_REF environment variable"))?;
if opt.apollo_uplink_poll_interval < Duration::from_secs(10) {
return Err(anyhow!("Apollo poll interval must be at least 10s"));

// This is more complex than I'd like it to be. Really, we just want to pass
// a FmtSubscriber to set_global_subscriber(), but we can't because of the
// generic nature of FmtSubscriber. See: https://github.com/tokio-rs/tracing/issues/380
// for more details.
let builder = tracing_subscriber::fmt::fmt().with_env_filter(
EnvFilter::try_new(&opt.log_level).context("could not parse log configuration")?,
);

let subscriber: RouterSubscriber = if atty::is(atty::Stream::Stdout) {
RouterSubscriber::TextSubscriber(builder.finish())
} else {
RouterSubscriber::JsonSubscriber(builder.json().finish())
};

set_global_subscriber(subscriber)?;

GLOBAL_ENV_FILTER.set(opt.log_level).unwrap();

let current_directory = std::env::current_dir()?;

let configuration = opt
.config_path
.as_ref()
.map(|path| {
let path = if path.is_relative() {
current_directory.join(path)
} else {
path.to_path_buf()
};

ConfigurationKind::File {
path,
watch: opt.hot_reload,
delay: None,
}
})
.unwrap_or_else(|| {
ConfigurationKind::Instance(Configuration::builder().build().boxed())
});
let apollo_router_msg = format!("Apollo Router v{} // (c) Apollo Graph, Inc. // Licensed as ELv2 (https://go.apollo.dev/elv2)", std::env!("CARGO_PKG_VERSION"));
let schema = match (opt.supergraph_path, opt.apollo_key) {
(Some(supergraph_path), _) => {
tracing::info!("{apollo_router_msg}");
setup_panic_handler();

let supergraph_path = if supergraph_path.is_relative() {
current_directory.join(supergraph_path)
} else {
supergraph_path
};
SchemaKind::File {
path: supergraph_path,
watch: opt.hot_reload,
delay: None,
}
}
(None, Some(apollo_key)) => {
tracing::info!("{apollo_router_msg}");
let apollo_graph_ref = opt.apollo_graph_ref.ok_or_else(||anyhow!("cannot fetch the supergraph from Apollo Studio without setting the APOLLO_GRAPH_REF environment variable"))?;
if opt.apollo_uplink_poll_interval < Duration::from_secs(10) {
return Err(anyhow!("Apollo poll interval must be at least 10s"));
}

SchemaKind::Registry {
apollo_key,
apollo_graph_ref,
url: opt.apollo_uplink_endpoints,
poll_interval: opt.apollo_uplink_poll_interval,
SchemaKind::Registry {
apollo_key,
apollo_graph_ref,
url: opt.apollo_uplink_endpoints,
poll_interval: opt.apollo_uplink_poll_interval,
}
}
}
_ => {
return Err(anyhow!(
r#"{apollo_router_msg}
_ => {
return Err(anyhow!(
r#"{apollo_router_msg}
⚠️ The Apollo Router requires a composed supergraph schema at startup. ⚠️
Expand Down Expand Up @@ -261,22 +291,23 @@ pub async fn rt_main() -> Result<()> {
$ ./router --supergraph starstuff.graphql
"#
));
));
}
};

let router = router_builder_fn.unwrap_or(|configuration, schema| {
ApolloRouter::builder()
.configuration(configuration)
.schema(schema)
.shutdown(ShutdownKind::CtrlC)
.build()
})(configuration, schema);
if let Err(err) = router.serve().await {
tracing::error!("{}", err);
return Err(err.into());
}
};

let router = ApolloRouter::builder()
.configuration(configuration)
.schema(schema)
.shutdown(ShutdownKind::CtrlC)
.build();

if let Err(err) = router.serve().await {
tracing::error!("{}", err);
return Err(err.into());
Ok(())
}

Ok(())
}

fn setup_panic_handler() {
Expand Down
2 changes: 1 addition & 1 deletion apollo-router/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ mod traits;
pub use cache::*;
pub use context::*;
pub use error::*;
pub use executable::{main, rt_main};
pub use executable::{main, Executable};
pub use introspection::*;
pub use json_ext::*;
pub use layers::*;
Expand Down
23 changes: 10 additions & 13 deletions apollo-router/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::axum_http_server_factory::AxumHttpServerFactory;
use crate::configuration::Configuration;
use crate::configuration::{validate_configuration, ListenAddr};
use crate::reload::Error as ReloadError;
use crate::router_factory::{RouterServiceFactory, YamlRouterServiceFactory};
use crate::router_factory::YamlRouterServiceFactory;
use crate::state_machine::StateMachine;
use derivative::Derivative;
use derive_more::{Display, From};
Expand Down Expand Up @@ -359,10 +359,7 @@ impl ShutdownKind {
/// };
/// ```
///
pub struct ApolloRouter<RF>
where
RF: RouterServiceFactory,
{
pub struct ApolloRouter {
/// The Configuration that the server will use. This can be static or a stream for hot reloading.
pub(crate) configuration: ConfigurationKind,

Expand All @@ -372,17 +369,17 @@ where
/// A future that when resolved will shut down the server.
pub(crate) shutdown: ShutdownKind,

pub(crate) router_factory: RF,
pub(crate) router_factory: YamlRouterServiceFactory,
}

#[buildstructor::buildstructor]
impl ApolloRouter<YamlRouterServiceFactory> {
impl ApolloRouter {
#[builder]
pub fn new(
configuration: ConfigurationKind,
schema: SchemaKind,
shutdown: Option<ShutdownKind>,
) -> ApolloRouter<YamlRouterServiceFactory> {
) -> ApolloRouter {
ApolloRouter {
configuration,
schema,
Expand Down Expand Up @@ -447,10 +444,7 @@ impl Future for RouterHandle {
}
}

impl<RF> ApolloRouter<RF>
where
RF: RouterServiceFactory,
{
impl ApolloRouter {
/// Start the federated server on a separate thread.
///
/// Dropping the server handle will shutdown the server.
Expand All @@ -473,7 +467,10 @@ where
.map(|r| match r {
Ok(Ok(ok)) => Ok(ok),
Ok(Err(err)) => Err(err),
Err(_err) => Err(ApolloRouterError::StartupError),
Err(err) => {
tracing::error!("{}", err);
Err(ApolloRouterError::StartupError)
}
})
.boxed();

Expand Down

0 comments on commit 2f84563

Please sign in to comment.