feat: optimize bulk cipher restoration

This commit is contained in:
qaz741wsd856 2025-12-01 10:30:50 +00:00
parent 6beec5f1ed
commit b8a961a936
2 changed files with 23 additions and 20 deletions

View file

@ -331,8 +331,17 @@ pub async fn restore_ciphers_bulk(
let now = Utc::now().format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(); let now = Utc::now().format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
let batch_size = get_batch_size(&env); let batch_size = get_batch_size(&env);
let ids = payload.ids; let ids = payload.ids;
let mut update_statements: Vec<D1PreparedStatement> = Vec::with_capacity(ids.len());
if ids.is_empty() {
return Ok(Json(BulkRestoreResponse {
data: vec![],
object: "list".to_string(),
continuation_token: None,
}));
}
// Batch UPDATE operations
let mut update_statements: Vec<D1PreparedStatement> = Vec::with_capacity(ids.len());
for id in ids.iter() { for id in ids.iter() {
let stmt = query!( let stmt = query!(
&db, &db,
@ -345,26 +354,20 @@ pub async fn restore_ciphers_bulk(
update_statements.push(stmt); update_statements.push(stmt);
} }
db::execute_in_batches(&db, update_statements, batch_size).await?; db::execute_in_batches(&db, update_statements, batch_size).await?;
let mut restored_ciphers = Vec::with_capacity(ids.len()); // Batch SELECT using json_each() - avoid N+1 query problem
let ids_json = serde_json::to_string(&ids).map_err(|_| AppError::Internal)?;
for id in ids { let restored_ciphers: Vec<Cipher> = db
let cipher_db: Option<crate::models::cipher::CipherDBModel> = query!( .prepare("SELECT * FROM ciphers WHERE user_id = ?1 AND id IN (SELECT value FROM json_each(?2))")
&db, .bind(&[claims.sub.clone().into(), ids_json.into()])?
"SELECT * FROM ciphers WHERE id = ?1 AND user_id = ?2", .all()
id, .await?
claims.sub .results::<crate::models::cipher::CipherDBModel>()?
) .into_iter()
.map_err(|_| AppError::Database)? .map(|cipher| cipher.into())
.first(None) .collect();
.await?;
if let Some(cipher) = cipher_db {
restored_ciphers.push(cipher.into());
}
}
db::touch_user_updated_at(&db, &claims.sub).await?; db::touch_user_updated_at(&db, &claims.sub).await?;

View file

@ -155,7 +155,7 @@ impl Into<Cipher> for CipherDBModel {
deleted_at: self.deleted_at, deleted_at: self.deleted_at,
created_at: self.created_at, created_at: self.created_at,
updated_at: self.updated_at, updated_at: self.updated_at,
object: "default_object".to_string(), object: "cipherDetails".to_string(),
organization_use_totp: false, organization_use_totp: false,
edit: true, edit: true,
view_password: true, view_password: true,
@ -264,7 +264,7 @@ impl Serialize for Cipher {
} }
fn default_object() -> String { fn default_object() -> String {
"cipher".to_string() "cipherDetails".to_string()
} }
fn default_true() -> bool { fn default_true() -> bool {