ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto

removal artifical delays, replace w exponential backoff

+38 -17
+20 -4
netlify/functions/batch-follow-users.ts
··· 98 98 // Create agent from OAuth session 99 99 const agent = new Agent(oauthSession); 100 100 101 - // Follow all users - process with small delays to respect rate limits 101 + // Follow all users 102 102 const results = []; 103 + let consecutiveErrors = 0; 104 + const MAX_CONSECUTIVE_ERRORS = 3; 105 + 103 106 for (const did of dids) { 104 107 try { 105 108 await agent.api.com.atproto.repo.createRecord({ ··· 117 120 success: true, 118 121 error: null 119 122 }); 123 + 124 + // Reset error counter on success 125 + consecutiveErrors = 0; 120 126 } catch (error) { 127 + consecutiveErrors++; 128 + 121 129 results.push({ 122 130 did, 123 131 success: false, 124 132 error: error instanceof Error ? error.message : 'Follow failed' 125 133 }); 134 + 135 + // If we hit rate limits, implement exponential backoff 136 + if (error instanceof Error && 137 + (error.message.includes('rate limit') || error.message.includes('429'))) { 138 + const backoffDelay = Math.min(200 * Math.pow(2, consecutiveErrors), 2000); 139 + console.log(`Rate limit hit. Backing off for ${backoffDelay}ms...`); 140 + await new Promise(resolve => setTimeout(resolve, backoffDelay)); 141 + } else if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) { 142 + // For other repeated errors, small backoff 143 + await new Promise(resolve => setTimeout(resolve, 500)); 144 + } 126 145 } 127 - 128 - // Small delay between follows to be respectful of rate limits 129 - await new Promise(resolve => setTimeout(resolve, 100)); 130 146 } 131 147 132 148 const successCount = results.filter(r => r.success).length;
-2
src/constants/platforms.ts
··· 63 63 export const SEARCH_CONFIG = { 64 64 BATCH_SIZE: 25, 65 65 MAX_MATCHES: 1000, 66 - BATCH_DELAY_MS: 500, 67 66 }; 68 67 69 68 export const FOLLOW_CONFIG = { 70 69 BATCH_SIZE: 50, 71 - BATCH_DELAY_MS: 1000, 72 70 };
+2 -5
src/hooks/useFollows.ts
··· 34 34 let totalFailed = 0; 35 35 36 36 try { 37 - const { BATCH_SIZE, BATCH_DELAY_MS } = FOLLOW_CONFIG; 37 + const { BATCH_SIZE } = FOLLOW_CONFIG; 38 38 39 39 for (let i = 0; i < selectedUsers.length; i += BATCH_SIZE) { 40 40 const batch = selectedUsers.slice(i, i + BATCH_SIZE); ··· 70 70 console.error('Batch follow error:', error); 71 71 } 72 72 73 - // Small delay between batches 74 - if (i + BATCH_SIZE < selectedUsers.length) { 75 - await new Promise(resolve => setTimeout(resolve, BATCH_DELAY_MS)); 76 - } 73 + // Rate limit handling is in the backend 77 74 } 78 75 79 76 const finalMsg = `Successfully followed ${totalFollowed} users${totalFailed > 0 ? `. ${totalFailed} failed.` : ''}`;
+16 -6
src/hooks/useSearch.ts
··· 24 24 setSearchProgress({ searched: 0, found: 0, total: resultsToSearch.length }); 25 25 onProgressUpdate(`Starting search for ${resultsToSearch.length} users...`); 26 26 27 - const { BATCH_SIZE, MAX_MATCHES, BATCH_DELAY_MS } = SEARCH_CONFIG; 27 + const { BATCH_SIZE, MAX_MATCHES } = SEARCH_CONFIG; 28 28 let totalSearched = 0; 29 29 let totalFound = 0; 30 + let consecutiveErrors = 0; 31 + const MAX_CONSECUTIVE_ERRORS = 3; 30 32 31 33 for (let i = 0; i < resultsToSearch.length; i += BATCH_SIZE) { 32 34 if (totalFound >= MAX_MATCHES) { ··· 47 49 48 50 try { 49 51 const data = await apiClient.batchSearchActors(usernames); 52 + 53 + // Reset error counter on success 54 + consecutiveErrors = 0; 50 55 51 56 // Process batch results 52 57 data.results.forEach((result) => { ··· 88 93 89 94 } catch (error) { 90 95 console.error('Batch search error:', error); 96 + consecutiveErrors++; 97 + 91 98 // Mark batch as failed 92 99 setSearchResults(prev => prev.map((result, index) => 93 100 i <= index && index < i + BATCH_SIZE 94 101 ? { ...result, isSearching: false, error: 'Search failed' } 95 102 : result 96 103 )); 97 - } 98 - 99 - // Small delay between batches 100 - if (i + BATCH_SIZE < resultsToSearch.length && totalFound < MAX_MATCHES) { 101 - await new Promise(resolve => setTimeout(resolve, BATCH_DELAY_MS)); 104 + 105 + // If we hit rate limits or repeated errors, add exponential backoff 106 + if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) { 107 + const backoffDelay = Math.min(1000 * Math.pow(2, consecutiveErrors - MAX_CONSECUTIVE_ERRORS), 5000); 108 + console.log(`Rate limit detected. Backing off for ${backoffDelay}ms...`); 109 + onProgressUpdate(`Rate limit detected. Pausing briefly...`); 110 + await new Promise(resolve => setTimeout(resolve, backoffDelay)); 111 + } 102 112 } 103 113 } 104 114