feat(server): impl doc gc (#15282)

#### PR Dependency Tree


* **PR #15282** 👈

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 document cleanup to reconcile missing workspace docs,
delete related stored data, and recover if the doc returns.
* Added effect-based follow-up reconciliation for search indexing,
Copilot embeddings, and comment attachment cleanup with explicit
acknowledgements.
* **Bug Fixes**
* Deleted-document references now persist as dangling references rather
than disappearing.
* Improved document deletion flow to enforce permissions and ensure
authorized deletions succeed.
* **Tests**
* Expanded coverage for cleanup recovery, indexing/embedding
reconciliation, permissions, and reference semantics.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-07-20 15:29:23 +08:00
committed by GitHub
parent 81df4751a3
commit bb55d6fd21
30 changed files with 2657 additions and 337 deletions
+34 -1
View File
@@ -22,7 +22,6 @@ export declare class BackendRuntime {
compactPendingDocUpdates(workspaceId: string, docId: string, batchLimit: number, historyMinIntervalMs: number, historyMaxAgeSeconds: number, owner: string, leaseTtlMs: number): Promise<RuntimeDocCompactionResult>
upsertDocSnapshot(workspaceId: string, docId: string, blob: Buffer, timestampMs: number, editorId?: string | undefined | null): Promise<boolean>
createDocHistory(input: RuntimeDocHistoryInput): Promise<boolean>
deleteDocStorage(workspaceId: string, docId: string): Promise<void>
putRuntimeGateIfAbsent(key: string, ttlMs: number): Promise<boolean>
cleanupExpiredRuntimeGates(limit: number): Promise<number>
cleanupExpiredUserSessions(limit: number): Promise<number>
@@ -78,6 +77,9 @@ export declare class StorageRuntime {
backfillMissingBlobMetadata(workspaceId: string | undefined | null, limit: number): Promise<RuntimeBlobMetadataBackfillResult>
rebuildDocBlobRefs(workspaceId: string, docId: string): Promise<RuntimeDocBlobRefsResult>
rebuildWorkspaceDocBlobRefs(workspaceId: string, limit: number): Promise<RuntimeDocBlobRefsResult>
reconcileWorkspaceDocuments(workspaceId: string): Promise<RuntimeDocumentCleanupReconcileResult>
executeDocumentCleanupCandidates(workspaceId: string | undefined | null, gracePeriodDays: number, limit: number): Promise<RuntimeDocumentCleanupExecuteResult>
ackDocumentCleanupEffect(workspaceId: string, docId: string, cleanupVersion: string, effect: string): Promise<RuntimeDocumentCleanupAckResult>
constructor()
start(): Promise<void>
configure(configJson: string): void
@@ -976,6 +978,37 @@ export interface RuntimeDocHistoryInput {
historyMaxAgeMs: number
}
export interface RuntimeDocumentCleanupAckResult {
completed: boolean
}
export interface RuntimeDocumentCleanupEffect {
workspaceId: string
docId: string
cleanupVersion: string
commentObjectsDone: boolean
searchDone: boolean
copilotDone: boolean
}
export interface RuntimeDocumentCleanupExecuteResult {
scannedCandidates: number
serializationRetries: number
executed: number
recovered: number
reset: number
failed: number
deletedRows: number
effects: Array<RuntimeDocumentCleanupEffect>
}
export interface RuntimeDocumentCleanupReconcileResult {
scannedDocs: number
marked: number
reset: number
recovered: number
}
export interface RuntimeInviteAbuseActionRequired {
action: string
subjectKey: string