2 examples

Duplicate request

Same request unintentionally sent multiple times.

[ FAQ1 ]

What is a duplicate request?

A duplicate request is when the exact same operation or data submission is sent multiple times, typically over HTTP APIs. Common scenarios include network timeouts prompting retries or accidental repeated submissions from user interfaces. Duplicate requests are particularly problematic with non-idempotent operations (such as HTTP POST requests) because each repetition might create additional unwanted resources or side effects. Without proper handling, these requests can lead to data corruption, resource conflicts, or race conditions that disrupt application reliability and integrity.
[ FAQ2 ]

How to handle duplicate request

To handle duplicate requests, design your API or system operations to be idempotent, meaning the same request repeated multiple times produces the same result without additional side effects. For example, use idempotency keys to uniquely identify each request and detect duplicates effectively. Implement appropriate HTTP methods (PUT or GET) where suitable, as they inherently support idempotency. Additionally, set clear retry policies on the client side to limit unnecessary repeated requests. Proactively addressing idempotency and duplicate handling ensures robust and reliable interactions within networked applications.
diff block
+#!/usr/bin/env python3
+"""
+CoinGecko Integration Update Script
+This script handles updating and synchronizing data from CoinGecko API.
+"""
+
+import os
+import sys
+import json
+import time
+import logging
+import requests
+from datetime import datetime
+
+# Constants
+JSON_INDENT = 2
+DEFAULT_TOP_COINS_LIMIT = 250
+DEFAULT_PAGE = 1
+REQUEST_TIMEOUT = 30
+API_VERSION = "1.0.0"
+ERROR_EXIT_CODE = 1
+
+# Configure logging
+logging.basicConfig(
+ level=logging.INFO,
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
+ handlers=[logging.StreamHandler(sys.stdout)]
+)
+logger = logging.getLogger("coingecko-integration")
+
+# Configuration
+COINGECKO_API_BASE = "https://api.coingecko.com/api/v3"
+COINGECKO_API_KEY = os.environ.get("COINGECKO_API_KEY", "")
+if not COINGECKO_API_KEY:
+ logger.warning("COINGECKO_API_KEY not set - running in free tier mode with rate limits")
+DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "coingecko")
+
+
+def get_headers():
+ """
+ Returns HTTP headers for CoinGecko API requests, including an API key if set.
+ """
+ headers = {
+ "Accept": "application/json"
+ }
+ if COINGECKO_API_KEY:
+ headers["X-CG-Pro-API-Key"] = COINGECKO_API_KEY
+ return headers
+
+
+def respect_rate_limits(response):
+ """Check response headers and respect rate limits"""
+ remaining = response.headers.get('X-RateLimit-Remaining')
+ if remaining and int(remaining) < 5:
+ reset_time = response.headers.get('X-RateLimit-Reset')
+ if reset_time:
+ sleep_time = max(int(reset_time) - time.time(), 0) + 1
+ logger.info(f"Rate limit approaching, sleeping for {sleep_time} seconds")
+ time.sleep(sleep_time)
+ else:
+ logger.info("Rate limit approaching, sleeping for 10 seconds")
+ time.sleep(10)
+ else:
+ # Add a small delay between requests regardless
+ time.sleep(0.5)
+
+def fetch_coins_list():
+ """
+ Fetches the complete list of coins from the CoinGecko API and saves it to a local JSON file.
+
+ Returns:
+ The list of coins as parsed from the API response, or None if the request fails.
+ """
+ url = f"{COINGECKO_API_BASE}/coins/list"
+ logger.info(f"Fetching coins list from {url}")
+
+ try:
+ response = requests.get(url, headers=get_headers())
+ try:
+ response.raise_for_status()
+ except requests.exceptions.RequestException as e:
+ logger.error(
+ f"Error fetching coins list: {str(e)}, "
+ f"Status Code: {e.response.status_code if hasattr(e, 'response') else 'N/A'}, "
+ f"Response: {e.response.text if hasattr(e, 'response') else 'N/A'}, "
+ f"URL: {url}"
+ )
+
+def fetch_coins_list():
+ raise
+ coins = response.json()
+
+ # Save to file
+ output_file = os.path.join(DATA_DIR, "coins_list.json")
+ with open(output_file, "w") as f:
+ json.dump(coins, f, indent=JSON_INDENT)
+
+ logger.info(f"Saved {len(coins)} coins to {output_file}")
+ return coins
+ except Exception as e:
+ logger.error(f"Error fetching coins list: {e}")
+ return None
+
+def fetch_global_data():
+ """
+ Fetches global cryptocurrency market data from the CoinGecko API.
+
+ Retrieves overall market statistics, saves the data to 'global.json' in the data directory,
+ and returns the parsed JSON data. Returns None if the request fails.
+ """
+ url = f"{COINGECKO_API_BASE}/global"
+ logger.info(f"Fetching global market data from {url}")
+
+ try:
+ response = requests.get(url, headers=get_headers())
+ response.raise_for_status()
+ respect_rate_limits(response)
+ global_data = response.json()
+
+ # Save to file
+ output_file = os.path.join(DATA_DIR, "global.json")
+ with open(output_file, "w") as f:
+ json.dump(global_data, f, indent=JSON_INDENT)
+
+ logger.info(f"Saved global market data to {output_file}")
+ return global_data
+ except Exception as e:
+ except Exception as e:
+ logger.error(
+ f"Error fetching top coins: {str(e)}, "
+ f"Status Code: {e.response.status_code if hasattr(e, 'response') else 'N/A'}, "
+ f"Response: {e.response.text if hasattr(e, 'response') else 'N/A'}, "
+ f"URL: {url}"
+ )
+ return None
+ return None
+
+def fetch_top_coins(limit=DEFAULT_TOP_COINS_LIMIT):
+ """
+ Fetches detailed market data for the top cryptocurrencies by market capitalization.
+
+ Retrieves market data for the top `limit` coins from the CoinGecko API, including price change percentages over 1 hour, 24 hours, and 7 days. Saves the resulting data to a JSON file in the data directory. Returns the data as a list of dictionaries, or None if the request fails.
+
+ Args:
+ limit: The number of top coins to fetch (default is 250).
+
+ Returns:
+ A list of dictionaries containing market data for each coin, or None on failure.
+ """
+ url = f"{COINGECKO_API_BASE}/coins/markets"
+ params = {
+ "vs_currency": "usd",
+ "order": "market_cap_desc",
+ "per_page": limit,
+ "page": DEFAULT_PAGE,
+ "sparkline": False,
+ "price_change_percentage": "1h,24h,7d"
+ }
+
+ logger.info(f"Fetching top {limit} coins from {url}")
+
+ try:
+ response = requests.get(url, headers=get_headers(), params=params, timeout=REQUEST_TIMEOUT)
+ response.raise_for_status()
+ respect_rate_limits(response)
+ top_coins = response.json()
+
+ # Save to file
+ output_file = os.path.join(DATA_DIR, f"top_{limit}_coins.json")
+ logger.info(f"Fetching top {limit} coins from {url}")
+
Greptile
greptile
I'll help you fix the duplicate logging and request code block in the `fetch_top_coins` function. The issue is that lines 170-177 duplicate the previous request code. Here's the fix:
suggested fix
logger.info(f"Fetching top {limit} coins from {url}")
    
     try:
         response = requests.get(url, headers=get_headers(), params=params, timeout=REQUEST_TIMEOUT)
         response.raise_for_status()
         respect_rate_limits(response)
         top_coins = response.json()
        
         # Save to file
         output_file = os.path.join(DATA_DIR, f"top_{limit}_coins.json")
         with open(output_file, "w") as f:
This suggestion: 1. Removes the duplicate logging statement 2. Removes the duplicate request code block 3. Keeps the original request logic intact 4. Maintains proper indentation 5. Continues with the file saving operation The fix removes the redundant code while preserving the core functionality of fetching and saving the top coins data.
diff block
tab === SurveysTabs.Active ? 'No surveys. Create a new survey?' : 'No surveys found'
}
loading={surveysLoading}
+ footer={
+ hasNextPage && (
+ <div className="flex justify-center p-1">
+ <LemonButton
+ onClick={loadNextPage}
+ className="min-w-full flex justify-between"
+ >
Greptile
greptile
logic: Button should be disabled while loading to prevent duplicate requests
suggested fix
<LemonButton
onClick={loadNextPage}
className="min-w-full flex justify-between"
+ disabled={surveysLoading}
>