Add ability to configure JavaScript runtime limits (#4669)
This commit is contained in:
parent
6e09684e4c
commit
33aa5da3cc
2 changed files with 30 additions and 23 deletions
|
@ -39,6 +39,14 @@ pub static MAX_STREAM_BATCH_SIZE: Lazy<u32> =
|
||||||
pub static INDEXING_BATCH_SIZE: Lazy<u32> =
|
pub static INDEXING_BATCH_SIZE: Lazy<u32> =
|
||||||
lazy_env_parse!("SURREAL_INDEXING_BATCH_SIZE", u32, 250);
|
lazy_env_parse!("SURREAL_INDEXING_BATCH_SIZE", u32, 250);
|
||||||
|
|
||||||
|
/// The maximum stack size of the JavaScript function runtime (defaults to 256 KiB)
|
||||||
|
pub static SCRIPTING_MAX_STACK_SIZE: Lazy<usize> =
|
||||||
|
lazy_env_parse!("SURREAL_SCRIPTING_MAX_STACK_SIZE", usize, 256 * 1024);
|
||||||
|
|
||||||
|
/// The maximum memory limit of the JavaScript function runtime (defaults to 2 MiB).
|
||||||
|
pub static SCRIPTING_MAX_MEMORY_LIMIT: Lazy<usize> =
|
||||||
|
lazy_env_parse!("SURREAL_SCRIPTING_MAX_MEMORY_LIMIT", usize, 2 << 20);
|
||||||
|
|
||||||
/// Forward all signup/signin/authenticate query errors to a client performing authentication. Do not use in production.
|
/// Forward all signup/signin/authenticate query errors to a client performing authentication. Do not use in production.
|
||||||
pub static INSECURE_FORWARD_ACCESS_ERRORS: Lazy<bool> =
|
pub static INSECURE_FORWARD_ACCESS_ERRORS: Lazy<bool> =
|
||||||
lazy_env_parse!("SURREAL_INSECURE_FORWARD_ACCESS_ERRORS", bool, false);
|
lazy_env_parse!("SURREAL_INSECURE_FORWARD_ACCESS_ERRORS", bool, false);
|
||||||
|
@ -51,10 +59,10 @@ pub static INSECURE_FORWARD_ACCESS_ERRORS: Lazy<bool> =
|
||||||
feature = "kv-tikv",
|
feature = "kv-tikv",
|
||||||
))]
|
))]
|
||||||
/// Specifies the buffer limit for external sorting.
|
/// Specifies the buffer limit for external sorting.
|
||||||
/// If the environment variable is not present or cannot be parsed, a default value of 50,000 is used.
|
|
||||||
pub static EXTERNAL_SORTING_BUFFER_LIMIT: Lazy<usize> =
|
pub static EXTERNAL_SORTING_BUFFER_LIMIT: Lazy<usize> =
|
||||||
lazy_env_parse!("SURREAL_EXTERNAL_SORTING_BUFFER_LIMIT", usize, 50_000);
|
lazy_env_parse!("SURREAL_EXTERNAL_SORTING_BUFFER_LIMIT", usize, 50_000);
|
||||||
|
|
||||||
|
/// Specifies whether GraphQL querying and schema definition is enabled.
|
||||||
pub static GRAPHQL_ENABLE: Lazy<bool> =
|
pub static GRAPHQL_ENABLE: Lazy<bool> =
|
||||||
lazy_env_parse!("SURREAL_EXPERIMENTAL_GRAPHQL", bool, false);
|
lazy_env_parse!("SURREAL_EXPERIMENTAL_GRAPHQL", bool, false);
|
||||||
|
|
||||||
|
@ -63,7 +71,8 @@ pub static GRAPHQL_ENABLE: Lazy<bool> =
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
pub static EXPERIMENTAL_BEARER_ACCESS: Lazy<bool> =
|
pub static EXPERIMENTAL_BEARER_ACCESS: Lazy<bool> =
|
||||||
lazy_env_parse!("SURREAL_EXPERIMENTAL_BEARER_ACCESS", bool, false);
|
lazy_env_parse!("SURREAL_EXPERIMENTAL_BEARER_ACCESS", bool, false);
|
||||||
// Run tests with bearer access enabled as it introduces new functionality that needs to be tested.
|
|
||||||
|
/// Run tests with bearer access enabled as it introduces new functionality that needs to be tested.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub static EXPERIMENTAL_BEARER_ACCESS: Lazy<bool> = Lazy::new(|| true);
|
pub static EXPERIMENTAL_BEARER_ACCESS: Lazy<bool> = Lazy::new(|| true);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ use super::modules::loader;
|
||||||
use super::modules::resolver;
|
use super::modules::resolver;
|
||||||
use super::modules::surrealdb::query::QueryContext;
|
use super::modules::surrealdb::query::QueryContext;
|
||||||
use super::modules::surrealdb::query::QUERY_DATA_PROP_NAME;
|
use super::modules::surrealdb::query::QUERY_DATA_PROP_NAME;
|
||||||
|
use crate::cnf::SCRIPTING_MAX_MEMORY_LIMIT;
|
||||||
|
use crate::cnf::SCRIPTING_MAX_STACK_SIZE;
|
||||||
use crate::ctx::Context;
|
use crate::ctx::Context;
|
||||||
use crate::dbs::Options;
|
use crate::dbs::Options;
|
||||||
use crate::doc::CursorDoc;
|
use crate::doc::CursorDoc;
|
||||||
|
@ -57,12 +59,12 @@ pub async fn run(
|
||||||
if context.is_done() {
|
if context.is_done() {
|
||||||
return Ok(Value::None);
|
return Ok(Value::None);
|
||||||
}
|
}
|
||||||
// Create an JavaScript context
|
// Create a JavaScript context
|
||||||
let run = js::AsyncRuntime::new().unwrap();
|
let run = js::AsyncRuntime::new().unwrap();
|
||||||
// Explicitly set max stack size to 256 KiB
|
// Explicitly set max stack size to 256 KiB
|
||||||
run.set_max_stack_size(262_144).await;
|
run.set_max_stack_size(*SCRIPTING_MAX_STACK_SIZE).await;
|
||||||
// Explicitly set max memory size to 2 MB
|
// Explicitly set max memory size to 2 MB
|
||||||
run.set_memory_limit(2_000_000).await;
|
run.set_memory_limit(*SCRIPTING_MAX_MEMORY_LIMIT).await;
|
||||||
// Ensure scripts are cancelled with context
|
// Ensure scripts are cancelled with context
|
||||||
let cancellation = context.cancellation();
|
let cancellation = context.cancellation();
|
||||||
let handler = Box::new(move || cancellation.is_done());
|
let handler = Box::new(move || cancellation.is_done());
|
||||||
|
@ -75,34 +77,30 @@ pub async fn run(
|
||||||
let src = format!(
|
let src = format!(
|
||||||
"export default async function() {{ try {{ {src} }} catch(e) {{ return (e instanceof Error) ? e : new Error(e); }} }}"
|
"export default async function() {{ try {{ {src} }} catch(e) {{ return (e instanceof Error) ? e : new Error(e); }} }}"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Attempt to execute the script
|
// Attempt to execute the script
|
||||||
async_with!(ctx => |ctx| {
|
async_with!(ctx => |ctx| {
|
||||||
let res = async {
|
let res = async {
|
||||||
// register all classes to the runtime.
|
|
||||||
// Get the context global object
|
// Get the context global object
|
||||||
let global = ctx.globals();
|
let global = ctx.globals();
|
||||||
|
|
||||||
// SAFETY: This is safe because the runtime only lives for the duration of this
|
// SAFETY: This is safe because the runtime only lives for the duration of this
|
||||||
// function. For the entire duration of which context, opt, txn and doc are valid.
|
// function. For the entire duration of which context, opt, txn and doc are valid.
|
||||||
unsafe{ create_query_data(context, opt, doc, &ctx) }?;
|
unsafe{ create_query_data(context, opt, doc, &ctx) }?;
|
||||||
|
// Register the fetch module as a global function
|
||||||
|
fetch::register(&ctx)?;
|
||||||
// Register the surrealdb module as a global object
|
// Register the surrealdb module as a global object
|
||||||
let (module, promise) = Module::evaluate_def::<modules::surrealdb::Package, _>(ctx.clone(), "surrealdb")?;
|
let (module, promise) = Module::evaluate_def::<modules::surrealdb::Package, _>(ctx.clone(), "surrealdb")?;
|
||||||
promise.finish::<()>()?;
|
promise.finish::<()>()?;
|
||||||
global.set(
|
global.set("surrealdb",
|
||||||
"surrealdb",
|
|
||||||
module.get::<_, js::Value>("default")?,
|
module.get::<_, js::Value>("default")?,
|
||||||
)?;
|
)?;
|
||||||
fetch::register(&ctx)?;
|
// Register the console module as a global object
|
||||||
let console = globals::console::console(&ctx)?;
|
let console = globals::console::console(&ctx)?;
|
||||||
// Register the console function to the globals
|
|
||||||
global.set("console", console)?;
|
global.set("console", console)?;
|
||||||
// Register the special SurrealDB types as classes
|
// Register the special SurrealDB types as classes
|
||||||
classes::init(&ctx)?;
|
classes::init(&ctx)?;
|
||||||
|
// Load the script as a module and evaluate it
|
||||||
let (module, promise) = Module::declare(ctx.clone(),"script", src)?.eval()?;
|
let (module, promise) = Module::declare(ctx.clone(),"script", src)?.eval()?;
|
||||||
promise.into_future::<()>().await?;
|
promise.into_future::<()>().await?;
|
||||||
|
|
||||||
// Attempt to fetch the main export
|
// Attempt to fetch the main export
|
||||||
let fnc = module.get::<_, Function>("default")?;
|
let fnc = module.get::<_, Function>("default")?;
|
||||||
// Extract the doc if any
|
// Extract the doc if any
|
||||||
|
@ -111,7 +109,7 @@ pub async fn run(
|
||||||
let promise = fnc.call::<_, Promise>((This(doc), Rest(arg)))?.into_future::<Value>();
|
let promise = fnc.call::<_, Promise>((This(doc), Rest(arg)))?.into_future::<Value>();
|
||||||
promise.await
|
promise.await
|
||||||
}.await;
|
}.await;
|
||||||
|
// Catch and convert any errors
|
||||||
res.catch(&ctx).map_err(Error::from)
|
res.catch(&ctx).map_err(Error::from)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|
Loading…
Reference in a new issue