1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

refactor(ext/cache): use concrete error type (#26109)

This commit is contained in:
Leo Kettmeir 2024-10-12 09:15:10 -07:00 committed by GitHub
parent 3df8f16500
commit 938a8ebe34
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 88 additions and 38 deletions

1
Cargo.lock generated
View file

@ -1354,6 +1354,7 @@ dependencies = [
"rusqlite", "rusqlite",
"serde", "serde",
"sha2", "sha2",
"thiserror",
"tokio", "tokio",
] ]

View file

@ -19,4 +19,5 @@ deno_core.workspace = true
rusqlite.workspace = true rusqlite.workspace = true
serde.workspace = true serde.workspace = true
sha2.workspace = true sha2.workspace = true
thiserror.workspace = true
tokio.workspace = true tokio.workspace = true

60
ext/cache/lib.rs vendored
View file

@ -7,7 +7,6 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use deno_core::error::type_error; use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op2; use deno_core::op2;
use deno_core::serde::Deserialize; use deno_core::serde::Deserialize;
use deno_core::serde::Serialize; use deno_core::serde::Serialize;
@ -19,6 +18,20 @@ use deno_core::ResourceId;
mod sqlite; mod sqlite;
pub use sqlite::SqliteBackedCache; pub use sqlite::SqliteBackedCache;
#[derive(Debug, thiserror::Error)]
pub enum CacheError {
#[error(transparent)]
Sqlite(#[from] rusqlite::Error),
#[error(transparent)]
JoinError(#[from] tokio::task::JoinError),
#[error(transparent)]
Resource(deno_core::error::AnyError),
#[error(transparent)]
Other(deno_core::error::AnyError),
#[error(transparent)]
Io(#[from] std::io::Error),
}
#[derive(Clone)] #[derive(Clone)]
pub struct CreateCache<C: Cache + 'static>(pub Arc<dyn Fn() -> C>); pub struct CreateCache<C: Cache + 'static>(pub Arc<dyn Fn() -> C>);
@ -92,26 +105,31 @@ pub struct CacheDeleteRequest {
pub trait Cache: Clone + 'static { pub trait Cache: Clone + 'static {
type CacheMatchResourceType: Resource; type CacheMatchResourceType: Resource;
async fn storage_open(&self, cache_name: String) -> Result<i64, AnyError>; async fn storage_open(&self, cache_name: String) -> Result<i64, CacheError>;
async fn storage_has(&self, cache_name: String) -> Result<bool, AnyError>; async fn storage_has(&self, cache_name: String) -> Result<bool, CacheError>;
async fn storage_delete(&self, cache_name: String) -> Result<bool, AnyError>; async fn storage_delete(
&self,
cache_name: String,
) -> Result<bool, CacheError>;
/// Put a resource into the cache. /// Put a resource into the cache.
async fn put( async fn put(
&self, &self,
request_response: CachePutRequest, request_response: CachePutRequest,
resource: Option<Rc<dyn Resource>>, resource: Option<Rc<dyn Resource>>,
) -> Result<(), AnyError>; ) -> Result<(), CacheError>;
async fn r#match( async fn r#match(
&self, &self,
request: CacheMatchRequest, request: CacheMatchRequest,
) -> Result< ) -> Result<
Option<(CacheMatchResponseMeta, Option<Self::CacheMatchResourceType>)>, Option<(CacheMatchResponseMeta, Option<Self::CacheMatchResourceType>)>,
AnyError, CacheError,
>; >;
async fn delete(&self, request: CacheDeleteRequest) async fn delete(
-> Result<bool, AnyError>; &self,
request: CacheDeleteRequest,
) -> Result<bool, CacheError>;
} }
#[op2(async)] #[op2(async)]
@ -119,7 +137,7 @@ pub trait Cache: Clone + 'static {
pub async fn op_cache_storage_open<CA>( pub async fn op_cache_storage_open<CA>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[string] cache_name: String, #[string] cache_name: String,
) -> Result<i64, AnyError> ) -> Result<i64, CacheError>
where where
CA: Cache, CA: Cache,
{ {
@ -131,7 +149,7 @@ where
pub async fn op_cache_storage_has<CA>( pub async fn op_cache_storage_has<CA>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[string] cache_name: String, #[string] cache_name: String,
) -> Result<bool, AnyError> ) -> Result<bool, CacheError>
where where
CA: Cache, CA: Cache,
{ {
@ -143,7 +161,7 @@ where
pub async fn op_cache_storage_delete<CA>( pub async fn op_cache_storage_delete<CA>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[string] cache_name: String, #[string] cache_name: String,
) -> Result<bool, AnyError> ) -> Result<bool, CacheError>
where where
CA: Cache, CA: Cache,
{ {
@ -155,13 +173,19 @@ where
pub async fn op_cache_put<CA>( pub async fn op_cache_put<CA>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[serde] request_response: CachePutRequest, #[serde] request_response: CachePutRequest,
) -> Result<(), AnyError> ) -> Result<(), CacheError>
where where
CA: Cache, CA: Cache,
{ {
let cache = get_cache::<CA>(&state)?; let cache = get_cache::<CA>(&state)?;
let resource = match request_response.response_rid { let resource = match request_response.response_rid {
Some(rid) => Some(state.borrow_mut().resource_table.take_any(rid)?), Some(rid) => Some(
state
.borrow_mut()
.resource_table
.take_any(rid)
.map_err(CacheError::Resource)?,
),
None => None, None => None,
}; };
cache.put(request_response, resource).await cache.put(request_response, resource).await
@ -172,7 +196,7 @@ where
pub async fn op_cache_match<CA>( pub async fn op_cache_match<CA>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[serde] request: CacheMatchRequest, #[serde] request: CacheMatchRequest,
) -> Result<Option<CacheMatchResponse>, AnyError> ) -> Result<Option<CacheMatchResponse>, CacheError>
where where
CA: Cache, CA: Cache,
{ {
@ -191,7 +215,7 @@ where
pub async fn op_cache_delete<CA>( pub async fn op_cache_delete<CA>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
#[serde] request: CacheDeleteRequest, #[serde] request: CacheDeleteRequest,
) -> Result<bool, AnyError> ) -> Result<bool, CacheError>
where where
CA: Cache, CA: Cache,
{ {
@ -199,7 +223,7 @@ where
cache.delete(request).await cache.delete(request).await
} }
pub fn get_cache<CA>(state: &Rc<RefCell<OpState>>) -> Result<CA, AnyError> pub fn get_cache<CA>(state: &Rc<RefCell<OpState>>) -> Result<CA, CacheError>
where where
CA: Cache, CA: Cache,
{ {
@ -211,7 +235,9 @@ where
state.put(cache); state.put(cache);
Ok(state.borrow::<CA>().clone()) Ok(state.borrow::<CA>().clone())
} else { } else {
Err(type_error("CacheStorage is not available in this context")) Err(CacheError::Other(type_error(
"CacheStorage is not available in this context",
)))
} }
} }

50
ext/cache/sqlite.rs vendored
View file

@ -30,6 +30,7 @@ use crate::serialize_headers;
use crate::vary_header_matches; use crate::vary_header_matches;
use crate::Cache; use crate::Cache;
use crate::CacheDeleteRequest; use crate::CacheDeleteRequest;
use crate::CacheError;
use crate::CacheMatchRequest; use crate::CacheMatchRequest;
use crate::CacheMatchResponseMeta; use crate::CacheMatchResponseMeta;
use crate::CachePutRequest; use crate::CachePutRequest;
@ -102,7 +103,7 @@ impl Cache for SqliteBackedCache {
/// Open a cache storage. Internally, this creates a row in the /// Open a cache storage. Internally, this creates a row in the
/// sqlite db if the cache doesn't exist and returns the internal id /// sqlite db if the cache doesn't exist and returns the internal id
/// of the cache. /// of the cache.
async fn storage_open(&self, cache_name: String) -> Result<i64, AnyError> { async fn storage_open(&self, cache_name: String) -> Result<i64, CacheError> {
let db = self.connection.clone(); let db = self.connection.clone();
let cache_storage_dir = self.cache_storage_dir.clone(); let cache_storage_dir = self.cache_storage_dir.clone();
spawn_blocking(move || { spawn_blocking(move || {
@ -121,14 +122,14 @@ impl Cache for SqliteBackedCache {
)?; )?;
let responses_dir = get_responses_dir(cache_storage_dir, cache_id); let responses_dir = get_responses_dir(cache_storage_dir, cache_id);
std::fs::create_dir_all(responses_dir)?; std::fs::create_dir_all(responses_dir)?;
Ok::<i64, AnyError>(cache_id) Ok::<i64, CacheError>(cache_id)
}) })
.await? .await?
} }
/// Check if a cache with the provided name exists. /// Check if a cache with the provided name exists.
/// Note: this doesn't check the disk, it only checks the sqlite db. /// Note: this doesn't check the disk, it only checks the sqlite db.
async fn storage_has(&self, cache_name: String) -> Result<bool, AnyError> { async fn storage_has(&self, cache_name: String) -> Result<bool, CacheError> {
let db = self.connection.clone(); let db = self.connection.clone();
spawn_blocking(move || { spawn_blocking(move || {
let db = db.lock(); let db = db.lock();
@ -140,13 +141,16 @@ impl Cache for SqliteBackedCache {
Ok(count > 0) Ok(count > 0)
}, },
)?; )?;
Ok::<bool, AnyError>(cache_exists) Ok::<bool, CacheError>(cache_exists)
}) })
.await? .await?
} }
/// Delete a cache storage. Internally, this deletes the row in the sqlite db. /// Delete a cache storage. Internally, this deletes the row in the sqlite db.
async fn storage_delete(&self, cache_name: String) -> Result<bool, AnyError> { async fn storage_delete(
&self,
cache_name: String,
) -> Result<bool, CacheError> {
let db = self.connection.clone(); let db = self.connection.clone();
let cache_storage_dir = self.cache_storage_dir.clone(); let cache_storage_dir = self.cache_storage_dir.clone();
spawn_blocking(move || { spawn_blocking(move || {
@ -167,7 +171,7 @@ impl Cache for SqliteBackedCache {
std::fs::remove_dir_all(cache_dir)?; std::fs::remove_dir_all(cache_dir)?;
} }
} }
Ok::<bool, AnyError>(maybe_cache_id.is_some()) Ok::<bool, CacheError>(maybe_cache_id.is_some())
}) })
.await? .await?
} }
@ -176,10 +180,12 @@ impl Cache for SqliteBackedCache {
&self, &self,
request_response: CachePutRequest, request_response: CachePutRequest,
resource: Option<Rc<dyn Resource>>, resource: Option<Rc<dyn Resource>>,
) -> Result<(), AnyError> { ) -> Result<(), CacheError> {
let db = self.connection.clone(); let db = self.connection.clone();
let cache_storage_dir = self.cache_storage_dir.clone(); let cache_storage_dir = self.cache_storage_dir.clone();
let now = SystemTime::now().duration_since(UNIX_EPOCH)?; let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime is before unix epoch");
if let Some(resource) = resource { if let Some(resource) = resource {
let body_key = hash(&format!( let body_key = hash(&format!(
@ -193,7 +199,11 @@ impl Cache for SqliteBackedCache {
let mut file = tokio::fs::File::create(response_path).await?; let mut file = tokio::fs::File::create(response_path).await?;
let mut buf = BufMutView::new(64 * 1024); let mut buf = BufMutView::new(64 * 1024);
loop { loop {
let (size, buf2) = resource.clone().read_byob(buf).await?; let (size, buf2) = resource
.clone()
.read_byob(buf)
.await
.map_err(CacheError::Other)?;
if size == 0 { if size == 0 {
break; break;
} }
@ -224,7 +234,7 @@ impl Cache for SqliteBackedCache {
request: CacheMatchRequest, request: CacheMatchRequest,
) -> Result< ) -> Result<
Option<(CacheMatchResponseMeta, Option<CacheResponseResource>)>, Option<(CacheMatchResponseMeta, Option<CacheResponseResource>)>,
AnyError, CacheError,
> { > {
let db = self.connection.clone(); let db = self.connection.clone();
let cache_storage_dir = self.cache_storage_dir.clone(); let cache_storage_dir = self.cache_storage_dir.clone();
@ -290,19 +300,17 @@ impl Cache for SqliteBackedCache {
} }
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
}; };
return Ok(Some((cache_meta, Some(CacheResponseResource::new(file))))); Ok(Some((cache_meta, Some(CacheResponseResource::new(file)))))
} }
Some((cache_meta, None)) => { Some((cache_meta, None)) => Ok(Some((cache_meta, None))),
return Ok(Some((cache_meta, None))); None => Ok(None),
}
None => return Ok(None),
} }
} }
async fn delete( async fn delete(
&self, &self,
request: CacheDeleteRequest, request: CacheDeleteRequest,
) -> Result<bool, AnyError> { ) -> Result<bool, CacheError> {
let db = self.connection.clone(); let db = self.connection.clone();
spawn_blocking(move || { spawn_blocking(move || {
// TODO(@satyarohith): remove the response body from disk if one exists // TODO(@satyarohith): remove the response body from disk if one exists
@ -311,17 +319,17 @@ impl Cache for SqliteBackedCache {
"DELETE FROM request_response_list WHERE cache_id = ?1 AND request_url = ?2", "DELETE FROM request_response_list WHERE cache_id = ?1 AND request_url = ?2",
(request.cache_id, &request.request_url), (request.cache_id, &request.request_url),
)?; )?;
Ok::<bool, AnyError>(rows_effected > 0) Ok::<bool, CacheError>(rows_effected > 0)
}) })
.await? .await?
} }
} }
async fn insert_cache_asset( async fn insert_cache_asset(
db: Arc<Mutex<rusqlite::Connection>>, db: Arc<Mutex<Connection>>,
put: CachePutRequest, put: CachePutRequest,
response_body_key: Option<String>, response_body_key: Option<String>,
) -> Result<Option<String>, deno_core::anyhow::Error> { ) -> Result<Option<String>, CacheError> {
spawn_blocking(move || { spawn_blocking(move || {
let maybe_response_body = { let maybe_response_body = {
let db = db.lock(); let db = db.lock();
@ -339,7 +347,7 @@ async fn insert_cache_asset(
response_body_key, response_body_key,
put.response_status, put.response_status,
put.response_status_text, put.response_status_text,
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(), SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime is before unix epoch").as_secs(),
), ),
|row| { |row| {
let response_body_key: Option<String> = row.get(0)?; let response_body_key: Option<String> = row.get(0)?;
@ -347,7 +355,7 @@ async fn insert_cache_asset(
}, },
)? )?
}; };
Ok::<Option<String>, AnyError>(maybe_response_body) Ok::<Option<String>, CacheError>(maybe_response_body)
}).await? }).await?
} }

View file

@ -10,6 +10,7 @@
//! exceptions. //! exceptions.
use deno_broadcast_channel::BroadcastChannelError; use deno_broadcast_channel::BroadcastChannelError;
use deno_cache::CacheError;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::url; use deno_core::url;
@ -154,6 +155,18 @@ pub fn get_nix_error_class(error: &nix::Error) -> &'static str {
} }
} }
pub fn get_cache_error(error: &CacheError) -> &'static str {
match error {
CacheError::Sqlite(_) => "Error",
CacheError::JoinError(_) => "Error",
CacheError::Resource(err) => {
deno_core::error::get_custom_error_class(err).unwrap_or("Error")
}
CacheError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
CacheError::Io(err) => get_io_error_class(err),
}
}
fn get_broadcast_channel_error(error: &BroadcastChannelError) -> &'static str { fn get_broadcast_channel_error(error: &BroadcastChannelError) -> &'static str {
match error { match error {
BroadcastChannelError::Resource(err) => { BroadcastChannelError::Resource(err) => {
@ -173,6 +186,7 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
.or_else(|| deno_web::get_error_class_name(e)) .or_else(|| deno_web::get_error_class_name(e))
.or_else(|| deno_webstorage::get_not_supported_error_class_name(e)) .or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
.or_else(|| deno_websocket::get_network_error_class_name(e)) .or_else(|| deno_websocket::get_network_error_class_name(e))
.or_else(|| e.downcast_ref::<CacheError>().map(get_cache_error))
.or_else(|| { .or_else(|| {
e.downcast_ref::<BroadcastChannelError>() e.downcast_ref::<BroadcastChannelError>()
.map(get_broadcast_channel_error) .map(get_broadcast_channel_error)