mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-23 05:25:53 +08:00
feat(server): blob reconciliation (#15165)
#### PR Dependency Tree * **PR #15165** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added automated backend maintenance for missing blob metadata backfill, document-to-blob reference rebuilding, and unreferenced blob cleanup planning/execution. * Introduced scheduled batch processing (workspace-paged) and paginated object-storage listing. * **Bug Fixes** * Improved reliability of object-storage reads by treating expected “not found” results as non-errors. * Strengthened blob/expired cleanup flows with runtime-driven batching and reduced coupling to metadata synchronization. * **Tests** * Expanded unit and e2e coverage for partial blob metadata and updated runtime/job cleanup test assertions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -105,31 +105,6 @@ test('should list blobs', async t => {
|
||||
t.is(blobs[1].key, blob2.key);
|
||||
});
|
||||
|
||||
test('should list deleted blobs', async t => {
|
||||
const workspace = await module.create(Mockers.Workspace);
|
||||
const blob = await models.blob.upsert({
|
||||
workspaceId: workspace.id,
|
||||
key: 'test-key',
|
||||
mime: 'text/plain',
|
||||
size: 100,
|
||||
});
|
||||
|
||||
await models.blob.delete(workspace.id, blob.key);
|
||||
|
||||
const blobs = await models.blob.listDeleted(workspace.id);
|
||||
|
||||
t.is(blobs.length, 1);
|
||||
t.is(blobs[0].key, blob.key);
|
||||
t.truthy(blobs[0].deletedAt);
|
||||
|
||||
// delete permanently
|
||||
await models.blob.delete(workspace.id, blob.key, true);
|
||||
|
||||
const blobs2 = await models.blob.listDeleted(workspace.id);
|
||||
|
||||
t.is(blobs2.length, 0);
|
||||
});
|
||||
|
||||
test('should get blob', async t => {
|
||||
const workspace = await module.create(Mockers.Workspace);
|
||||
const blob = await models.blob.upsert({
|
||||
|
||||
@@ -96,32 +96,6 @@ export class BlobModel extends BaseModel {
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async listPendingExpired(before: Date) {
|
||||
return await this.db.blob.findMany({
|
||||
where: {
|
||||
status: 'pending',
|
||||
deletedAt: null,
|
||||
createdAt: {
|
||||
lt: before,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
workspaceId: true,
|
||||
key: true,
|
||||
uploadId: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async listDeleted(workspaceId: string) {
|
||||
return await this.db.blob.findMany({
|
||||
where: {
|
||||
workspaceId,
|
||||
deletedAt: { not: null },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async totalSize(workspaceId: string) {
|
||||
const sum = await this.db.blob.aggregate({
|
||||
where: {
|
||||
|
||||
@@ -162,21 +162,4 @@ export class HistoryModel extends BaseModel {
|
||||
editor: row.createdByUser,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean expired histories.
|
||||
*/
|
||||
async cleanExpired() {
|
||||
const { count } = await this.db.snapshotHistory.deleteMany({
|
||||
where: {
|
||||
expiredAt: {
|
||||
lte: new Date(),
|
||||
},
|
||||
},
|
||||
});
|
||||
if (count > 0) {
|
||||
this.logger.log(`Deleted ${count} expired histories`);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,17 +148,4 @@ export class SessionModel extends BaseModel {
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
async cleanExpiredUserSessions() {
|
||||
const { count } = await this.db.userSession.deleteMany({
|
||||
where: {
|
||||
expiresAt: {
|
||||
lte: new Date(),
|
||||
},
|
||||
},
|
||||
});
|
||||
if (count > 0) {
|
||||
this.logger.log(`Cleaned ${count} expired user sessions`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user