Improve description and fix some test errors

This commit is contained in:
tyranron 2021-09-20 19:12:30 +02:00 committed by Christian Legnitto
parent 9bd9727cb2
commit dfda435bc7
2 changed files with 26 additions and 21 deletions

View file

@ -59,8 +59,7 @@ struct User {
// Assign Database as the context type for User // Assign Database as the context type for User
#[graphql_object(context = Database)] #[graphql_object(context = Database)]
impl User { impl User {
// Inject the context by specifying an argument // Inject the context by specifying an argument with the context type.
// with the context type.
// Note: // Note:
// - the type must be a reference // - the type must be a reference
// - the name of the argument SHOULD be `context` // - the name of the argument SHOULD be `context`
@ -86,20 +85,26 @@ impl User {
You only get an immutable reference to the context, so if you want to affect You only get an immutable reference to the context, so if you want to affect
change to the execution, you'll need to use [interior change to the execution, you'll need to use [interior
mutability](https://doc.rust-lang.org/book/first-edition/mutability.html#interior-vs-exterior-mutability) mutability](https://doc.rust-lang.org/book/first-edition/mutability.html#interior-vs-exterior-mutability)
using e.g. `RwLock` or `RefCell`. \ using e.g. `RwLock` or `RefCell`.
If you are using async runtime like `tokio` for mutable references you will need to use a corresponding async version of RwLock:
## Dealing with mutable references
Context cannot be specified by a mutable reference, because concurrent fields resolving may be performed. If you have something in your context that requires access by mutable reference, then you need to leverage the [interior mutability][1] for that.
For example, when using async runtime with [work stealing][2] (like `tokio`), which obviously requires thread safety in addition, you will need to use a corresponding async version of `RwLock`:
```rust ```rust
# extern crate juniper; # extern crate juniper;
# use std::collections::HashMap; # use std::collections::HashMap;
# use juniper::graphql_object; # use juniper::graphql_object;
# use tokio::sync::RwLock; use tokio::sync::RwLock;
#
// This struct represents our context.
struct Database { struct Database {
requested_count: HashMap<i32, i32>, requested_count: HashMap<i32, i32>,
} }
// Mark the Database as a valid context type for Juniper
impl juniper::Context for Database {} impl juniper::Context for Database {}
struct User { struct User {
@ -108,21 +113,15 @@ struct User {
times_requested: i32, times_requested: i32,
} }
// Assign Database as the context type for User and envelope it in RwLock
#[graphql_object(context = RwLock<Database>)] #[graphql_object(context = RwLock<Database>)]
impl User { impl User {
// Inject the context by specifying an argument async fn times_requested<'db>(&self, context: &'db RwLock<Database>) -> Vec<&'db User> {
// with the context type.
// Note:
// - the type must be a reference
// - the name of the argument SHOULD be `context`
fn times_requested<'db>(&self, context: &'db RwLock<Database>) -> Vec<&'db User> {
// Acquire a mutable reference and await if async RwLock is used, // Acquire a mutable reference and await if async RwLock is used,
// which is neccessary if context consists async operations // which is necessary if context consists async operations like
// like querying remote databases // querying remote databases.
// If context is immutable use .read() on RwLock // If context is immutable use .read() on RwLock.
let mut context = context.write().await; let mut context = context.write().await;
// Preform a mutable operation // Preform a mutable operation.
context.requested_count.entry(self.id).and_modify(|e| { *e += 1 }).or_insert(1) context.requested_count.entry(self.id).and_modify(|e| { *e += 1 }).or_insert(1)
} }
@ -137,4 +136,10 @@ impl User {
# #
# fn main() { } # fn main() { }
``` ```
Replace `tokio::sync::RwLock` with `std::sync::RwLock` if async runtime is not intended Replace `tokio::sync::RwLock` with `std::sync::RwLock` (or similar) if you don't intend to use async resolving.
[1]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html
[2]: https://en.wikipedia.org/wiki/Work_stealing

View file

@ -16,7 +16,7 @@ iron = "0.5"
mount = "0.4" mount = "0.4"
skeptic = "0.13" skeptic = "0.13"
serde_json = "1.0" serde_json = "1.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] } tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync"] }
uuid = "0.8" uuid = "0.8"
[build-dependencies] [build-dependencies]