Skip to content

Commit c6c1272

Browse files
authored
chore: debug remove stale objects (#372)
1 parent d44a8e2 commit c6c1272

File tree

2 files changed

+85
-35
lines changed

2 files changed

+85
-35
lines changed

.github/workflows/remove-stale.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,17 @@ jobs:
55
rm_stale_packages:
66
runs-on: ubuntu-latest
77
steps:
8-
- name: Send POST request
8+
- name: Send POST request and capture output
99
run: |
1010
curl -X POST \
11+
-N \
1112
-H "sb-rm-stale-key: ${{ secrets.NITRO_RM_STALE_KEY }}" \
12-
https://pkg.pr.new/rm/stale
13+
https://pkg.pr.new/rm/stale \
14+
| tee rm-stale.log
15+
16+
- name: Upload rm-stale.log as artifact
17+
uses: actions/upload-artifact@v4
18+
with:
19+
name: rm-stale-log
20+
path: rm-stale.log
21+
Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,48 @@
11
import type { H3Event } from "h3";
22

33
export default eventHandler(async (event) => {
4+
setResponseHeader(event, "Transfer-Encoding", "chunked");
5+
setResponseHeader(event, "Cache-Control", "no-cache");
6+
setResponseHeader(event, "Content-Type", "text/plain");
7+
48
const rmStaleKeyHeader = getHeader(event, "sb-rm-stale-key");
9+
const signal = toWebRequest(event).signal;
510
const { rmStaleKey } = useRuntimeConfig(event);
11+
612
if (rmStaleKeyHeader !== rmStaleKey) {
713
throw createError({
814
status: 403,
915
});
1016
}
11-
return {
12-
ok: true,
13-
removed: [
14-
...(await Promise.all([
15-
iterateAndDelete(event, {
16-
prefix: usePackagesBucket.base,
17-
limit: 100,
18-
}),
19-
iterateAndDelete(event, {
20-
prefix: useTemplatesBucket.base,
21-
limit: 100,
22-
}),
23-
]).then((results) => results.flat())),
24-
],
25-
};
17+
18+
const { readable, writable } = new TransformStream()
19+
20+
event.waitUntil(
21+
(async () => {
22+
// const writer = writable.getWriter()
23+
// console.log('here')
24+
// await writer.ready
25+
// await writer.write(new TextEncoder().encode("start\n"))
26+
// writer.releaseLock()
27+
28+
await iterateAndDelete(event, writable, signal, {
29+
prefix: usePackagesBucket.base,
30+
limit: 100,
31+
})
32+
await iterateAndDelete(event, writable, signal, {
33+
prefix: useTemplatesBucket.base,
34+
limit: 100,
35+
})
36+
await writable.close()
37+
})()
38+
)
39+
40+
return readable
2641
});
2742

28-
async function iterateAndDelete(event: H3Event, opts: R2ListOptions) {
43+
async function iterateAndDelete(event: H3Event, writable: WritableStream, signal: AbortSignal, opts: R2ListOptions) {
44+
const writer = writable.getWriter()
45+
await writer.ready
2946
const binding = useBinding(event);
3047

3148
let truncated = true;
@@ -34,34 +51,58 @@ async function iterateAndDelete(event: H3Event, opts: R2ListOptions) {
3451
const downloadedAtBucket = useDownloadedAtBucket(event);
3552
const today = Date.parse(new Date().toString());
3653

37-
const removed: string[] = [];
38-
while (truncated) {
54+
while (truncated && !signal.aborted) {
3955
// TODO: Avoid using context.cloudflare and migrate to unstorage, but it does not have truncated for now
4056
const next = await binding.list({
4157
...opts,
4258
cursor,
4359
});
4460
for (const object of next.objects) {
61+
if (signal.aborted) {
62+
break;
63+
}
4564
const uploaded = Date.parse(object.uploaded.toString());
4665
// remove the object anyway if it's 6 months old already
47-
if ((today - uploaded) / (1000 * 3600 * 24 * 30 * 6) >= 1) {
48-
removed.push(object.key);
49-
event.context.cloudflare.context.waitUntil(binding.delete(object.key));
50-
event.context.cloudflare.context.waitUntil(
51-
downloadedAtBucket.removeItem(object.key),
52-
);
66+
// Use calendar-accurate 6 months check
67+
const uploadedDate = new Date(uploaded);
68+
const sixMonthsAgo = new Date(today);
69+
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
70+
if (uploadedDate <= sixMonthsAgo) {
71+
const downloadedAt = (await downloadedAtBucket.getItem(object.key))!;
72+
await writer.write(new TextEncoder().encode(JSON.stringify({
73+
key: object.key,
74+
uploaded: new Date(object.uploaded),
75+
downloadedAt: downloadedAt ? new Date(downloadedAt) : null,
76+
}) + "\n"))
77+
// event.context.cloudflare.context.waitUntil(binding.delete(object.key));
78+
// event.context.cloudflare.context.waitUntil(
79+
// downloadedAtBucket.removeItem(object.key),
80+
// );
81+
}
82+
const downloadedAt = (await downloadedAtBucket.getItem(object.key));
83+
84+
if (!downloadedAt) {
85+
continue;
5386
}
54-
const downloadedAt = (await downloadedAtBucket.getItem(object.key))!;
5587
// if it has not been downloaded in the last month and it's at least 1 month old
88+
// Calendar-accurate 1 month checks
89+
const downloadedAtDate = new Date(downloadedAt);
90+
const oneMonthAgo = new Date(today);
91+
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
92+
const uploadedDate2 = new Date(uploaded); // uploaded already parsed above
5693
if (
57-
!((today - downloadedAt) / (1000 * 3600 * 24 * 30) < 1) &&
58-
(today - uploaded) / (1000 * 3600 * 24 * 30) >= 1
94+
downloadedAtDate <= oneMonthAgo &&
95+
uploadedDate2 <= oneMonthAgo
5996
) {
60-
removed.push(object.key);
61-
event.context.cloudflare.context.waitUntil(binding.delete(object.key));
62-
event.context.cloudflare.context.waitUntil(
63-
downloadedAtBucket.removeItem(object.key),
64-
);
97+
await writer.write(new TextEncoder().encode(JSON.stringify({
98+
key: object.key,
99+
uploaded: new Date(object.uploaded),
100+
downloadedAt: new Date(downloadedAt),
101+
}) + "\n"))
102+
// event.context.cloudflare.context.waitUntil(binding.delete(object.key));
103+
// event.context.cloudflare.context.waitUntil(
104+
// downloadedAtBucket.removeItem(object.key),
105+
// );
65106
}
66107
}
67108

@@ -70,5 +111,5 @@ async function iterateAndDelete(event: H3Event, opts: R2ListOptions) {
70111
cursor = next.cursor;
71112
}
72113
}
73-
return removed;
114+
writer.releaseLock()
74115
}

0 commit comments

Comments
 (0)