diff --git a/app/api/inngest/functions/index_follows.ts b/app/api/inngest/functions/index_follows.ts index 14173ef7..dcba1dc3 100644 --- a/app/api/inngest/functions/index_follows.ts +++ b/app/api/inngest/functions/index_follows.ts @@ -89,13 +89,6 @@ export const index_follows = inngest.createFunction( const insertBatchSize = 100; let insertBatchNumber = 0; - await step.run("clear existing follows", () => { - return supabaseServerClient - .from("bsky_follows") - .delete() - .eq("identity", event.data.did); - }); - // Create all insert batches in parallel const insertBatches = []; for (let i = 0; i < existingFollows.length; i += insertBatchSize) { @@ -117,6 +110,47 @@ export const index_follows = inngest.createFunction( // Wait for all insert batches to complete await Promise.all(insertBatches); + + // Delete follows that are no longer in the fetched list + // For large follow lists, we need to batch this operation + await step.run("delete-unfollowed", async () => { + // Get all current follows from the database + const { data: currentFollows } = await supabaseServerClient + .from("bsky_follows") + .select("follows") + .eq("identity", event.data.did); + + if (!currentFollows || currentFollows.length === 0) { + return { deleted: 0 }; + } + + // Find follows that are in the database but not in the newly fetched list + const currentFollowDids = currentFollows.map((f) => f.follows); + const toDelete = currentFollowDids.filter( + (did) => !existingFollows.includes(did) + ); + + if (toDelete.length === 0) { + return { deleted: 0 }; + } + + // Delete in batches to avoid query size limits + const deleteBatchSize = 100; + const deletePromises = []; + for (let i = 0; i < toDelete.length; i += deleteBatchSize) { + const batch = toDelete.slice(i, i + deleteBatchSize); + deletePromises.push( + supabaseServerClient + .from("bsky_follows") + .delete() + .eq("identity", event.data.did) + .in("follows", batch) + ); + } + + await Promise.all(deletePromises); + return { deleted: toDelete.length }; + }); return { done: true, };