feat: replace fixed 50-entry cap with size-based feed trimming
Emails are now trimmed from the oldest end when total serialised size exceeds FEED_MAX_SIZE_BYTES (default 512 KB). Each EmailMetadata entry stores its size so future trims are computed without re-reading KV. Adds FEED_MAX_SIZE_BYTES, PROXY_TRUSTED_IPS and PROXY_AUTH_SECRET to Env. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
984362f637
commit
9eba4c34c6
3 files changed
+78
-4
No files matched your search
@@ -90,18 +90,39 @@ export async function processEmail(
|
||||
env.EMAIL_STORAGE.get(feedMetadataKey, "json"),
|
||||
]);
|
||||
|
||||
// Note: KV has no atomic compare-and-swap. Concurrent invocations for the
|
||||
// same feed can read stale metadata and produce orphaned KV entries or
|
||||
// duplicate trim deletions. This is an accepted limitation given Cloudflare
|
||||
// KV's eventual-consistency model.
|
||||
const feedMetadata = ((rawMetadata as FeedMetadata | null) || {
|
||||
emails: [],
|
||||
}) as FeedMetadata;
|
||||
|
||||
const DEFAULT_MAX_BYTES = 524288; // 512 KB
|
||||
const maxBytes =
|
||||
parseInt(env.FEED_MAX_SIZE_BYTES ?? "", 10) || DEFAULT_MAX_BYTES;
|
||||
|
||||
const serialised = JSON.stringify(emailData);
|
||||
const serialisedSize = new TextEncoder().encode(serialised).byteLength;
|
||||
feedMetadata.emails.unshift({
|
||||
key: emailKey,
|
||||
subject: emailData.subject,
|
||||
receivedAt: emailData.receivedAt,
|
||||
size: serialisedSize,
|
||||
});
|
||||
if (feedMetadata.emails.length > 50) {
|
||||
feedMetadata.emails = feedMetadata.emails.slice(0, 50);
|
||||
|
||||
let totalSize = feedMetadata.emails.reduce((sum, e) => sum + (e.size ?? 0), 0);
|
||||
const toDelete: string[] = [];
|
||||
while (totalSize > maxBytes && feedMetadata.emails.length > 1) {
|
||||
const dropped = feedMetadata.emails.pop()!;
|
||||
totalSize -= dropped.size ?? 0;
|
||||
toDelete.push(dropped.key);
|
||||
}
|
||||
await env.EMAIL_STORAGE.put(feedMetadataKey, JSON.stringify(feedMetadata));
|
||||
|
||||
await Promise.all([
|
||||
env.EMAIL_STORAGE.put(feedMetadataKey, JSON.stringify(feedMetadata)),
|
||||
...toDelete.map((k) => env.EMAIL_STORAGE.delete(k)),
|
||||
]);
|
||||
|
||||
console.log(`Successfully processed email for feed ${feedId}`);
|
||||
return new Response("Email processed successfully", { status: 200 });
|
||||
|
||||
Reference in new issue
Block a user