Skip to content

Commit

Permalink
Document resource limiting (#510)
Browse files Browse the repository at this point in the history
* Document resource limiting

* Formatting

* More formatting

* fmt

* Doc comment wording

Co-authored-by: David <[email protected]>

* Cross-reference docs between servers and utils

* Fix grumbles

* More elaborate description

* Update http-server/Cargo.toml

Co-authored-by: Niklas Adolfsson <[email protected]>

* Apply suggestions from code review

Co-authored-by: David <[email protected]>

* Allow for unlimited resources if cap is 0

* fmt

* Update ws-server/Cargo.toml

Co-authored-by: Niklas Adolfsson <[email protected]>

* Clarify 8 resources, 0s in costs/limits, and runtime errors

Co-authored-by: David <[email protected]>
Co-authored-by: Niklas Adolfsson <[email protected]>
  • Loading branch information
3 people authored Oct 12, 2021
1 parent 6fb61dc commit 75045f4
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 17 deletions.
1 change: 1 addition & 0 deletions http-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ unicase = "2.6.0"
[dev-dependencies]
env_logger = "0.9"
jsonrpsee-test-utils = { path = "../test-utils" }
jsonrpsee = { path = "../jsonrpsee", features = ["full"] }
7 changes: 5 additions & 2 deletions http-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,11 @@ impl Builder {
self
}

/// Register a new resource kind. Errors if `label` is already registered, or if number of
/// registered resources would exceed 8.
/// Register a new resource kind. Errors if `label` is already registered, or if the number of
/// registered resources on this server instance would exceed 8.
///
/// See the module documentation for [`resurce_limiting`](../jsonrpsee_utils/server/resource_limiting/index.html#resource-limiting)
/// for details.
pub fn register_resource(mut self, label: &'static str, capacity: u16, default: u16) -> Result<Self, Error> {
self.resources.register(label, capacity, default)?;

Expand Down
15 changes: 6 additions & 9 deletions proc-macros/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
// DEALINGS IN THE SOFTWARE.

use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree};
use std::fmt;
use std::{fmt, iter};
use syn::parse::{Parse, ParseStream, Parser};
use syn::punctuated::Punctuated;
use syn::{spanned::Spanned, Attribute, Error, Token};
Expand All @@ -51,15 +51,14 @@ impl Parse for Argument {
fn parse(input: ParseStream) -> syn::Result<Self> {
let label = input.parse()?;

let mut tokens = TokenStream2::new();
let mut scope = 0usize;

// Need to read to till either the end of the stream,
// or the nearest comma token that's not contained
// inside angle brackets.
loop {
let tokens = iter::from_fn(move || {
if scope == 0 && input.peek(Token![,]) {
break;
return None;
}

if input.peek(Token![<]) {
Expand All @@ -68,11 +67,9 @@ impl Parse for Argument {
scope = scope.saturating_sub(1);
}

match input.parse::<TokenTree>() {
Ok(token) => tokens.extend([token]),
Err(_) => break,
}
}
input.parse::<TokenTree>().ok()
})
.collect();

Ok(Argument { label, tokens })
}
Expand Down
2 changes: 1 addition & 1 deletion utils/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

/// Helpers.
pub mod helpers;
/// Resource limiting helpers
/// Resource limiting. Create generic "resources" and configure their limits to ensure servers are not overloaded.
pub mod resource_limiting;
/// JSON-RPC "modules" group sets of methods that belong together and handles method/subscription registration.
pub mod rpc_module;
91 changes: 91 additions & 0 deletions utils/src/server/resource_limiting.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,94 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the
// Software without restriction, including without
// limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice
// shall be included in all copies or substantial portions
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! # Resource Limiting
//!
//! This module handles limiting the capacity of the server to respond to requests.
//!
//! `jsonrpsee` is agnostic about the types of resources available on the server, and the units used are arbitrary.
//! The units are used to model the availability of a resource, be it something mundane like CPU or Memory,
//! or more exotic things like remote API access to a 3rd party service, or use of some external hardware
//! that's under the control of the server.
//!
//! To get the most out of this feature, we suggest benchmarking individual methods to see how many resources they
//! consume, in particular anything critical that is expected to result in a lot of stress on the server,
//! and then defining your units such that the limits (`capacity`) can be adjusted for different hardware configurations.
//!
//! Up to 8 resources can be defined using the [`WsServerBuilder::register_resource`](../../../jsonrpsee_ws_server/struct.WsServerBuilder.html#method.register_resource)
//! or [`HttpServerBuilder::register_resource`](../../../jsonrpsee_ws_server/struct.WsServerBuilder.html#method.register_resource) method
//! for the WebSocket and HTTP server respectively.
//!
//! Each method will claim the specified number of units (or the default) for the duration of its execution.
//! Any method execution that would cause the total sum of claimed resource units to exceed
//! the `capacity` of that resource will be denied execution, immediately returning JSON-RPC error object with code `-32604`.
//!
//! Setting the execution cost to `0` equates to the method effectively not being limited by a given resource. Likewise setting the
//! `capacity` to `0` disables any limiting for a given resource.
//!
//! To specify a different than default number of units a method should use, use the `resources` argument in the
//! `#[method]` attribute:
//!
//! ```
//! # use jsonrpsee::{types::RpcResult, proc_macros::rpc};
//! #
//! #[rpc(server)]
//! pub trait Rpc {
//! #[method(name = "my_expensive_method", resources("cpu" = 5, "mem" = 2))]
//! async fn my_expensive_method(&self) -> RpcResult<&'static str> {
//! // Do work
//! Ok("hello")
//! }
//! }
//! ```
//!
//! Alternatively, you can use the `resource` method when creating a module manually without the help of the macro:
//!
//! ```
//! # use jsonrpsee::{RpcModule, types::RpcResult};
//! #
//! # fn main() -> RpcResult<()> {
//! #
//! let mut module = RpcModule::new(());
//!
//! module
//! .register_async_method("my_expensive_method", |_, _| async move {
//! // Do work
//! Ok("hello")
//! })?
//! .resource("cpu", 5)?
//! .resource("mem", 2)?;
//! # Ok(())
//! # }
//! ```
//!
//! Each resource needs to have a unique name, such as `"cpu"` or `"memory"`, which can then be used across all
//! [`RpcModule`s](crate::server::rpc_module::RpcModule). In case a module definition uses a resource label not
//! defined on the server, starting the server with such a module will result in a runtime error containing the
//! information about the offending method.

use std::sync::Arc;

use arrayvec::ArrayVec;
Expand Down
10 changes: 8 additions & 2 deletions utils/src/server/rpc_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,13 @@ impl Methods {
None => return Err(Error::ResourceNameNotFoundForMethod(label, method_name)),
};

map[idx] = units;
// If resource capacity set to `0`, we ignore the unit value of the method
// and set it to `0` as well, effectively making the resource unlimited.
if resources.capacities[idx] == 0 {
map[idx] = 0;
} else {
map[idx] = units;
}
}

callback.resources = MethodResources::Initialized(map);
Expand All @@ -254,7 +260,7 @@ impl Methods {
Arc::make_mut(&mut self.callbacks)
}

/// Merge two [`Methods`]'s by adding all [`MethodKind`]s from `other` into `self`.
/// Merge two [`Methods`]'s by adding all [`MethodCallback`]s from `other` into `self`.
/// Fails if any of the methods in `other` is present already.
pub fn merge(&mut self, other: impl Into<Methods>) -> Result<(), Error> {
let mut other = other.into();
Expand Down
1 change: 1 addition & 0 deletions ws-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ tokio-util = { version = "0.6", features = ["compat"] }
anyhow = "1"
env_logger = "0.9"
jsonrpsee-test-utils = { path = "../test-utils" }
jsonrpsee = { path = "../jsonrpsee", features = ["full"] }
8 changes: 5 additions & 3 deletions ws-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,13 @@ impl Builder {
self
}

/// Register a new resource kind. Errors if `label` is already registered, or if number of
/// registered resources would exceed 8.
/// Register a new resource kind. Errors if `label` is already registered, or if the number of
/// registered resources on this server instance would exceed 8.
///
/// See the module documentation for [`resurce_limiting`](../jsonrpsee_utils/server/resource_limiting/index.html#resource-limiting)
/// for details.
pub fn register_resource(mut self, label: &'static str, capacity: u16, default: u16) -> Result<Self, Error> {
self.resources.register(label, capacity, default)?;

Ok(self)
}

Expand Down

0 comments on commit 75045f4

Please sign in to comment.