Skip to content

Commit 6ef202b

Browse files
committed
feat: 삭제된 지 24시간이 지난 Todo에 대해 필요한 데이터만 남겨 DB 용량 최소화
1 parent 69e6156 commit 6ef202b

3 files changed

Lines changed: 116 additions & 0 deletions

File tree

Firebase/firestore.index.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
{
22
"indexes": [
3+
{
4+
"collectionGroup": "todoLists",
5+
"queryScope": "COLLECTION_GROUP",
6+
"fields": [
7+
{
8+
"fieldPath": "compactedAt",
9+
"order": "ASCENDING"
10+
},
11+
{
12+
"fieldPath": "deletedAt",
13+
"order": "ASCENDING"
14+
},
15+
{
16+
"fieldPath": "__name__",
17+
"order": "ASCENDING"
18+
}
19+
]
20+
},
321
{
422
"collectionGroup": "todoLists",
523
"queryScope": "COLLECTION_GROUP",
@@ -588,6 +606,28 @@
588606
}
589607
],
590608
"fieldOverrides": [
609+
{
610+
"collectionGroup": "todoLists",
611+
"fieldPath": "compactedAt",
612+
"indexes": [
613+
{
614+
"order": "ASCENDING",
615+
"queryScope": "COLLECTION"
616+
},
617+
{
618+
"order": "DESCENDING",
619+
"queryScope": "COLLECTION"
620+
},
621+
{
622+
"order": "ASCENDING",
623+
"queryScope": "COLLECTION_GROUP"
624+
},
625+
{
626+
"order": "DESCENDING",
627+
"queryScope": "COLLECTION_GROUP"
628+
}
629+
]
630+
},
591631
{
592632
"collectionGroup": "todoLists",
593633
"fieldPath": "createdAt",

Firebase/functions/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ import {
3232
scheduleTodoReminder
3333
} from "./fcm/schedule";
3434

35+
import {
36+
compactSoftDeletedTodos
37+
} from "./todo/cleanup";
38+
3539
import {
3640
syncTodoNotificationCategory
3741
} from "./todo/update";
@@ -109,6 +113,7 @@ export {
109113
removeTodoNotificationDocuments,
110114
removeCompletedTodoNotificationRecords,
111115
cleanupNotificationDispatches,
116+
compactSoftDeletedTodos,
112117
syncTodoNotificationCategory,
113118
requestMoveRemovedCategoryTodosToEtc,
114119
completeMoveRemovedCategoryTodosToEtc,
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { onSchedule } from "firebase-functions/v2/scheduler";
2+
import * as admin from "firebase-admin";
3+
import * as logger from "firebase-functions/logger";
4+
import { toError } from "../common/error";
5+
6+
const LOCATION = "asia-northeast3";
7+
const CLEANUP_BATCH_SIZE = 200;
8+
const TOMBSTONE_GRACE_PERIOD_HOURS = 24;
9+
10+
// 삭제 후 유예 기간이 지난 todo를 표시용 최소 필드만 남는 축약 문서 형태로 압축
11+
export const compactSoftDeletedTodos = onSchedule({
12+
maxInstances: 1,
13+
region: LOCATION,
14+
schedule: "0 9 * * *",
15+
timeZone: "Asia/Seoul"
16+
},
17+
async () => {
18+
const cutoff = new Date(Date.now() - (TOMBSTONE_GRACE_PERIOD_HOURS * 60 * 60 * 1000));
19+
20+
try {
21+
let lastDocument:
22+
FirebaseFirestore.QueryDocumentSnapshot<FirebaseFirestore.DocumentData> | undefined;
23+
24+
while (true) {
25+
let query = admin.firestore()
26+
.collectionGroup("todoLists")
27+
.where("compactedAt", "==", null)
28+
.where("deletedAt", "<=", admin.firestore.Timestamp.fromDate(cutoff))
29+
.orderBy("deletedAt")
30+
.orderBy(admin.firestore.FieldPath.documentId())
31+
.limit(CLEANUP_BATCH_SIZE);
32+
if (lastDocument) {
33+
query = query.startAfter(lastDocument);
34+
}
35+
36+
const snapshot = await query.get();
37+
if (snapshot.empty) { return; }
38+
39+
const batch = admin.firestore().batch();
40+
snapshot.docs.forEach((document) => {
41+
batch.update(document.ref, {
42+
compactedAt: admin.firestore.FieldValue.serverTimestamp(),
43+
content: admin.firestore.FieldValue.delete(),
44+
dueDate: admin.firestore.FieldValue.delete(),
45+
isChecked: admin.firestore.FieldValue.delete(),
46+
isCompleted: admin.firestore.FieldValue.delete(),
47+
isDeleting: admin.firestore.FieldValue.delete(),
48+
isPinned: admin.firestore.FieldValue.delete(),
49+
isDeleted: admin.firestore.FieldValue.delete(),
50+
tags: admin.firestore.FieldValue.delete()
51+
});
52+
});
53+
await batch.commit();
54+
55+
if (snapshot.size < CLEANUP_BATCH_SIZE) { return; }
56+
lastDocument = snapshot.docs[snapshot.docs.length - 1];
57+
}
58+
} catch (error) {
59+
logger.error(
60+
"soft deleted todo 축약 문서 압축 실패",
61+
toError(error),
62+
{
63+
collectionGroup: "todoLists",
64+
filter: `compactedAt == null && deletedAt <= now - ${TOMBSTONE_GRACE_PERIOD_HOURS}h`,
65+
orderBy: ["deletedAt", "documentId"],
66+
cleanupBatchSize: CLEANUP_BATCH_SIZE
67+
}
68+
);
69+
}
70+
}
71+
);

0 commit comments

Comments
 (0)