summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/kv/config.rs136
-rw-r--r--ext/kv/lib.rs106
-rw-r--r--runtime/snapshot.rs7
-rw-r--r--runtime/web_worker.rs1
-rw-r--r--runtime/worker.rs1
5 files changed, 203 insertions, 48 deletions
diff --git a/ext/kv/config.rs b/ext/kv/config.rs
new file mode 100644
index 000000000..6e2e2c3a1
--- /dev/null
+++ b/ext/kv/config.rs
@@ -0,0 +1,136 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+pub struct KvConfig {
+ pub(crate) max_write_key_size_bytes: usize,
+ pub(crate) max_read_key_size_bytes: usize,
+ pub(crate) max_value_size_bytes: usize,
+ pub(crate) max_read_ranges: usize,
+ pub(crate) max_read_entries: usize,
+ pub(crate) max_checks: usize,
+ pub(crate) max_mutations: usize,
+ pub(crate) max_watched_keys: usize,
+ pub(crate) max_total_mutation_size_bytes: usize,
+ pub(crate) max_total_key_size_bytes: usize,
+}
+
+impl KvConfig {
+ pub fn builder() -> KvConfigBuilder {
+ KvConfigBuilder::default()
+ }
+}
+
+#[derive(Default)]
+pub struct KvConfigBuilder {
+ max_write_key_size_bytes: Option<usize>,
+ max_value_size_bytes: Option<usize>,
+ max_read_ranges: Option<usize>,
+ max_read_entries: Option<usize>,
+ max_checks: Option<usize>,
+ max_mutations: Option<usize>,
+ max_watched_keys: Option<usize>,
+ max_total_mutation_size_bytes: Option<usize>,
+ max_total_key_size_bytes: Option<usize>,
+}
+
+impl KvConfigBuilder {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn max_write_key_size_bytes(
+ &mut self,
+ max_write_key_size_bytes: usize,
+ ) -> &mut Self {
+ self.max_write_key_size_bytes = Some(max_write_key_size_bytes);
+ self
+ }
+
+ pub fn max_value_size_bytes(
+ &mut self,
+ max_value_size_bytes: usize,
+ ) -> &mut Self {
+ self.max_value_size_bytes = Some(max_value_size_bytes);
+ self
+ }
+
+ pub fn max_read_ranges(&mut self, max_read_ranges: usize) -> &mut Self {
+ self.max_read_ranges = Some(max_read_ranges);
+ self
+ }
+
+ pub fn max_read_entries(&mut self, max_read_entries: usize) -> &mut Self {
+ self.max_read_entries = Some(max_read_entries);
+ self
+ }
+
+ pub fn max_checks(&mut self, max_checks: usize) -> &mut Self {
+ self.max_checks = Some(max_checks);
+ self
+ }
+
+ pub fn max_mutations(&mut self, max_mutations: usize) -> &mut Self {
+ self.max_mutations = Some(max_mutations);
+ self
+ }
+
+ pub fn max_watched_keys(&mut self, max_watched_keys: usize) -> &mut Self {
+ self.max_watched_keys = Some(max_watched_keys);
+ self
+ }
+
+ pub fn max_total_mutation_size_bytes(
+ &mut self,
+ max_total_mutation_size_bytes: usize,
+ ) -> &mut Self {
+ self.max_total_mutation_size_bytes = Some(max_total_mutation_size_bytes);
+ self
+ }
+
+ pub fn max_total_key_size_bytes(
+ &mut self,
+ max_total_key_size_bytes: usize,
+ ) -> &mut Self {
+ self.max_total_key_size_bytes = Some(max_total_key_size_bytes);
+ self
+ }
+
+ pub fn build(&self) -> KvConfig {
+ const MAX_WRITE_KEY_SIZE_BYTES: usize = 2048;
+ // range selectors can contain 0x00 or 0xff suffixes
+ const MAX_READ_KEY_SIZE_BYTES: usize = MAX_WRITE_KEY_SIZE_BYTES + 1;
+ const MAX_VALUE_SIZE_BYTES: usize = 65536;
+ const MAX_READ_RANGES: usize = 10;
+ const MAX_READ_ENTRIES: usize = 1000;
+ const MAX_CHECKS: usize = 100;
+ const MAX_MUTATIONS: usize = 1000;
+ const MAX_WATCHED_KEYS: usize = 10;
+ const MAX_TOTAL_MUTATION_SIZE_BYTES: usize = 800 * 1024;
+ const MAX_TOTAL_KEY_SIZE_BYTES: usize = 80 * 1024;
+
+ KvConfig {
+ max_write_key_size_bytes: self
+ .max_write_key_size_bytes
+ .unwrap_or(MAX_WRITE_KEY_SIZE_BYTES),
+ max_read_key_size_bytes: self
+ .max_write_key_size_bytes
+ .map(|x|
+ // range selectors can contain 0x00 or 0xff suffixes
+ x + 1)
+ .unwrap_or(MAX_READ_KEY_SIZE_BYTES),
+ max_value_size_bytes: self
+ .max_value_size_bytes
+ .unwrap_or(MAX_VALUE_SIZE_BYTES),
+ max_read_ranges: self.max_read_ranges.unwrap_or(MAX_READ_RANGES),
+ max_read_entries: self.max_read_entries.unwrap_or(MAX_READ_ENTRIES),
+ max_checks: self.max_checks.unwrap_or(MAX_CHECKS),
+ max_mutations: self.max_mutations.unwrap_or(MAX_MUTATIONS),
+ max_watched_keys: self.max_watched_keys.unwrap_or(MAX_WATCHED_KEYS),
+ max_total_mutation_size_bytes: self
+ .max_total_mutation_size_bytes
+ .unwrap_or(MAX_TOTAL_MUTATION_SIZE_BYTES),
+ max_total_key_size_bytes: self
+ .max_total_key_size_bytes
+ .unwrap_or(MAX_TOTAL_KEY_SIZE_BYTES),
+ }
+ }
+}
diff --git a/ext/kv/lib.rs b/ext/kv/lib.rs
index 8a57d610d..c7a995073 100644
--- a/ext/kv/lib.rs
+++ b/ext/kv/lib.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+pub mod config;
pub mod dynamic;
mod interface;
pub mod remote;
@@ -56,22 +57,11 @@ use log::debug;
use serde::Deserialize;
use serde::Serialize;
+pub use crate::config::*;
pub use crate::interface::*;
pub const UNSTABLE_FEATURE_NAME: &str = "kv";
-const MAX_WRITE_KEY_SIZE_BYTES: usize = 2048;
-// range selectors can contain 0x00 or 0xff suffixes
-const MAX_READ_KEY_SIZE_BYTES: usize = MAX_WRITE_KEY_SIZE_BYTES + 1;
-const MAX_VALUE_SIZE_BYTES: usize = 65536;
-const MAX_READ_RANGES: usize = 10;
-const MAX_READ_ENTRIES: usize = 1000;
-const MAX_CHECKS: usize = 100;
-const MAX_MUTATIONS: usize = 1000;
-const MAX_WATCHED_KEYS: usize = 10;
-const MAX_TOTAL_MUTATION_SIZE_BYTES: usize = 800 * 1024;
-const MAX_TOTAL_KEY_SIZE_BYTES: usize = 80 * 1024;
-
deno_core::extension!(deno_kv,
deps = [ deno_console, deno_web ],
parameters = [ DBH: DatabaseHandler ],
@@ -88,8 +78,10 @@ deno_core::extension!(deno_kv,
esm = [ "01_db.ts" ],
options = {
handler: DBH,
+ config: KvConfig,
},
state = |state, options| {
+ state.put(Rc::new(options.config));
state.put(Rc::new(options.handler));
}
);
@@ -282,10 +274,15 @@ where
resource.db.clone()
};
- if ranges.len() > MAX_READ_RANGES {
+ let config = {
+ let state = state.borrow();
+ state.borrow::<Rc<KvConfig>>().clone()
+ };
+
+ if ranges.len() > config.max_read_ranges {
return Err(type_error(format!(
"too many ranges (max {})",
- MAX_READ_RANGES
+ config.max_read_ranges
)));
}
@@ -298,8 +295,8 @@ where
let (start, end) =
decode_selector_and_cursor(&selector, reverse, cursor.as_ref())?;
- check_read_key_size(&start)?;
- check_read_key_size(&end)?;
+ check_read_key_size(&start, &config)?;
+ check_read_key_size(&end, &config)?;
total_entries += limit as usize;
Ok(ReadRange {
@@ -312,10 +309,10 @@ where
})
.collect::<Result<Vec<_>, AnyError>>()?;
- if total_entries > MAX_READ_ENTRIES {
+ if total_entries > config.max_read_entries {
return Err(type_error(format!(
"too many entries (max {})",
- MAX_READ_ENTRIES
+ config.max_read_entries
)));
}
@@ -392,11 +389,12 @@ where
DBH: DatabaseHandler + 'static,
{
let resource = state.resource_table.get::<DatabaseResource<DBH::DB>>(rid)?;
+ let config = state.borrow::<Rc<KvConfig>>().clone();
- if keys.len() > MAX_WATCHED_KEYS {
+ if keys.len() > config.max_watched_keys {
return Err(type_error(format!(
"too many keys (max {})",
- MAX_WATCHED_KEYS
+ config.max_watched_keys
)));
}
@@ -406,7 +404,7 @@ where
.collect::<std::io::Result<_>>()?;
for k in &keys {
- check_read_key_size(k)?;
+ check_read_key_size(k, &config)?;
}
let stream = resource.db.watch(keys);
@@ -783,14 +781,22 @@ where
resource.db.clone()
};
- if checks.len() > MAX_CHECKS {
- return Err(type_error(format!("too many checks (max {})", MAX_CHECKS)));
+ let config = {
+ let state = state.borrow();
+ state.borrow::<Rc<KvConfig>>().clone()
+ };
+
+ if checks.len() > config.max_checks {
+ return Err(type_error(format!(
+ "too many checks (max {})",
+ config.max_checks
+ )));
}
- if mutations.len() + enqueues.len() > MAX_MUTATIONS {
+ if mutations.len() + enqueues.len() > config.max_mutations {
return Err(type_error(format!(
"too many mutations (max {})",
- MAX_MUTATIONS
+ config.max_mutations
)));
}
@@ -822,36 +828,37 @@ where
return Err(type_error("key cannot be empty"));
}
- total_payload_size += check_write_key_size(key)?;
+ total_payload_size += check_write_key_size(key, &config)?;
}
for (key, value) in mutations
.iter()
.flat_map(|m| m.kind.value().map(|x| (&m.key, x)))
{
- let key_size = check_write_key_size(key)?;
- total_payload_size += check_value_size(value)? + key_size;
+ let key_size = check_write_key_size(key, &config)?;
+ total_payload_size += check_value_size(value, &config)? + key_size;
total_key_size += key_size;
}
for enqueue in &enqueues {
- total_payload_size += check_enqueue_payload_size(&enqueue.payload)?;
+ total_payload_size +=
+ check_enqueue_payload_size(&enqueue.payload, &config)?;
if let Some(schedule) = enqueue.backoff_schedule.as_ref() {
total_payload_size += 4 * schedule.len();
}
}
- if total_payload_size > MAX_TOTAL_MUTATION_SIZE_BYTES {
+ if total_payload_size > config.max_total_mutation_size_bytes {
return Err(type_error(format!(
"total mutation size too large (max {} bytes)",
- MAX_TOTAL_MUTATION_SIZE_BYTES
+ config.max_total_mutation_size_bytes
)));
}
- if total_key_size > MAX_TOTAL_KEY_SIZE_BYTES {
+ if total_key_size > config.max_total_key_size_bytes {
return Err(type_error(format!(
"total key size too large (max {} bytes)",
- MAX_TOTAL_KEY_SIZE_BYTES
+ config.max_total_key_size_bytes
)));
}
@@ -881,50 +888,59 @@ fn op_kv_encode_cursor(
Ok(cursor)
}
-fn check_read_key_size(key: &[u8]) -> Result<(), AnyError> {
- if key.len() > MAX_READ_KEY_SIZE_BYTES {
+fn check_read_key_size(key: &[u8], config: &KvConfig) -> Result<(), AnyError> {
+ if key.len() > config.max_read_key_size_bytes {
Err(type_error(format!(
"key too large for read (max {} bytes)",
- MAX_READ_KEY_SIZE_BYTES
+ config.max_read_key_size_bytes
)))
} else {
Ok(())
}
}
-fn check_write_key_size(key: &[u8]) -> Result<usize, AnyError> {
- if key.len() > MAX_WRITE_KEY_SIZE_BYTES {
+fn check_write_key_size(
+ key: &[u8],
+ config: &KvConfig,
+) -> Result<usize, AnyError> {
+ if key.len() > config.max_write_key_size_bytes {
Err(type_error(format!(
"key too large for write (max {} bytes)",
- MAX_WRITE_KEY_SIZE_BYTES
+ config.max_write_key_size_bytes
)))
} else {
Ok(key.len())
}
}
-fn check_value_size(value: &KvValue) -> Result<usize, AnyError> {
+fn check_value_size(
+ value: &KvValue,
+ config: &KvConfig,
+) -> Result<usize, AnyError> {
let payload = match value {
KvValue::Bytes(x) => x,
KvValue::V8(x) => x,
KvValue::U64(_) => return Ok(8),
};
- if payload.len() > MAX_VALUE_SIZE_BYTES {
+ if payload.len() > config.max_value_size_bytes {
Err(type_error(format!(
"value too large (max {} bytes)",
- MAX_VALUE_SIZE_BYTES
+ config.max_value_size_bytes
)))
} else {
Ok(payload.len())
}
}
-fn check_enqueue_payload_size(payload: &[u8]) -> Result<usize, AnyError> {
- if payload.len() > MAX_VALUE_SIZE_BYTES {
+fn check_enqueue_payload_size(
+ payload: &[u8],
+ config: &KvConfig,
+) -> Result<usize, AnyError> {
+ if payload.len() > config.max_value_size_bytes {
Err(type_error(format!(
"enqueue payload too large (max {} bytes)",
- MAX_VALUE_SIZE_BYTES
+ config.max_value_size_bytes
)))
} else {
Ok(payload.len())
diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs
index 659356ae6..fd422603f 100644
--- a/runtime/snapshot.rs
+++ b/runtime/snapshot.rs
@@ -244,9 +244,10 @@ pub fn create_runtime_snapshot(
deno_ffi::deno_ffi::init_ops_and_esm::<Permissions>(),
deno_net::deno_net::init_ops_and_esm::<Permissions>(None, None),
deno_tls::deno_tls::init_ops_and_esm(),
- deno_kv::deno_kv::init_ops_and_esm(deno_kv::sqlite::SqliteDbHandler::<
- Permissions,
- >::new(None, None)),
+ deno_kv::deno_kv::init_ops_and_esm(
+ deno_kv::sqlite::SqliteDbHandler::<Permissions>::new(None, None),
+ deno_kv::KvConfig::builder().build(),
+ ),
deno_cron::deno_cron::init_ops_and_esm(
deno_cron::local::LocalCronHandler::new(),
),
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index 3e95045db..8178b93a8 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -480,6 +480,7 @@ impl WebWorker {
proxy: None,
},
),
+ deno_kv::KvConfig::builder().build(),
),
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
diff --git a/runtime/worker.rs b/runtime/worker.rs
index c1c918d08..c0d839166 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -404,6 +404,7 @@ impl MainWorker {
proxy: None,
},
),
+ deno_kv::KvConfig::builder().build(),
),
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),