fix(storage): safely batch folder deletion binds

This commit is contained in:
shuaiplus
2026-07-13 13:30:52 +08:00
parent e943357067
commit 63b642b251
2 changed files with 20 additions and 12 deletions
+12 -9
View File
@@ -68,20 +68,22 @@ export async function bulkDeleteFolders(
db: D1Database, db: D1Database,
userId: string, userId: string,
ids: string[], ids: string[],
sqlChunkSize: (fixedBindCount: number) => number, sqlChunkSize: (fixedBindCount: number, bindCountPerItem?: number) => number,
updateRevisionDate: (userId: string) => Promise<string> updateRevisionDate: (userId: string) => Promise<string>
): Promise<string | null> { ): Promise<string | null> {
const uniqueIds = Array.from(new Set(ids.map((id) => String(id || '').trim()).filter(Boolean))); const uniqueIds = Array.from(new Set(ids.map((id) => String(id || '').trim()).filter(Boolean)));
if (!uniqueIds.length) return null; if (!uniqueIds.length) return null;
const now = new Date().toISOString(); const now = new Date().toISOString();
const chunkSize = sqlChunkSize(2); // Each folder ID is bound in all three compatibility predicates below.
const chunkSize = sqlChunkSize(2, 3);
const statements: D1PreparedStatement[] = [];
for (let i = 0; i < uniqueIds.length; i += chunkSize) { for (let i = 0; i < uniqueIds.length; i += chunkSize) {
const chunk = uniqueIds.slice(i, i + chunkSize); const chunk = uniqueIds.slice(i, i + chunkSize);
const placeholders = chunk.map(() => '?').join(','); const placeholders = chunk.map(() => '?').join(',');
await db statements.push(
.prepare( db.prepare(
`UPDATE ciphers `UPDATE ciphers
SET folder_id = NULL, updated_at = ?, SET folder_id = NULL, updated_at = ?,
data = json_remove(data, '$.folderId', '$.folder_id', '$.updatedAt', '$.revisionDate') data = json_remove(data, '$.folderId', '$.folder_id', '$.updatedAt', '$.revisionDate')
@@ -93,14 +95,15 @@ export async function bulkDeleteFolders(
)` )`
) )
.bind(now, userId, ...chunk, ...chunk, ...chunk) .bind(now, userId, ...chunk, ...chunk, ...chunk)
.run(); );
statements.push(
await db db.prepare(`DELETE FROM folders WHERE user_id = ? AND id IN (${placeholders})`)
.prepare(`DELETE FROM folders WHERE user_id = ? AND id IN (${placeholders})`)
.bind(userId, ...chunk) .bind(userId, ...chunk)
.run(); );
} }
await db.batch(statements);
return updateRevisionDate(userId); return updateRevisionDate(userId);
} }
+7 -2
View File
@@ -209,10 +209,15 @@ export class StorageService {
return REQUIRED_SCHEMA_TABLES.every((table) => found.has(table)); return REQUIRED_SCHEMA_TABLES.every((table) => found.has(table));
} }
private sqlChunkSize(fixedBindCount: number): number { private sqlChunkSize(fixedBindCount: number, bindCountPerItem = 1): number {
const safeFixedBindCount = Math.max(0, Math.floor(fixedBindCount));
const safeBindCountPerItem = Math.max(1, Math.floor(bindCountPerItem));
return Math.max( return Math.max(
1, 1,
Math.min(LIMITS.performance.bulkMoveChunkSize, StorageService.MAX_D1_SQL_VARIABLES - fixedBindCount) Math.min(
LIMITS.performance.bulkMoveChunkSize,
Math.floor((StorageService.MAX_D1_SQL_VARIABLES - safeFixedBindCount) / safeBindCountPerItem)
)
); );
} }