BULUT & MALİYET~7 dakika okuma süresi

API Fatura Kabusundan Uyanmak: Quota (Kota) Yönetimi Hayat Kurtarır

Google Maps, AWS veya yapay zeka API'lerini kullanırken bir sabah binlerce dolarlık faturayla uyanmamak için Quota (Kota) yönetiminin mimarideki yerini ve maliyetleri nasıl kontrol altına alacağımızı inceliyoruz.

API Fatura Kabusundan Uyanmak: Quota Yönetimi

Geliştirici olmanın en heyecan verici yanlarından biri, Google Maps gibi devasa altyapıları veya güçlü yapay zeka modellerini sadece bir API anahtarı (API Key) ile kendi uygulamamıza entegre edebilmektir. Bir hesap açar, anahtarı alır, kodu yazar ve sihrin gerçekleşmesini izleriz. Ancak bu "sınırsız" gücün karanlık bir yüzü vardır: Faturalandırma döngüsü.

Neden bu konuyu seçtiğime gelirsek... Sektörde sıkça duyduğumuz, belki de bizzat yaşadığımız o meşhur hikayeyi bilirsiniz: Bir Cuma akşamı canlıya alınan yeni bir özellik, frontend tarafındaki sonsuz bir useEffect döngüsü veya backend'deki hatalı bir while (retry) mantığı yüzünden hafta sonu boyunca saniyede yüzlerce kez harici bir API'ye istek atar. Pazartesi sabahı ofise gelindiğinde, ekibi bekleyen şey sadece bir performans darboğazı değil, aynı zamanda 15.000 dolarlık sürpriz bir bulut faturasıdır.

İşte tam da bu yüzden, bir backend mühendisi sadece sistemin ayakta kalmasından değil, aynı zamanda şirketin finansal güvenliğinden de sorumludur. Bu yazıda, "Quota" (Kota) kullanımının mimarimizdeki yerini ve dışa bağımlı olduğumuz API'lerde cüzdanımızı nasıl koruyacağımızı konuşacağız.

Mimari Bakış: Rate Limiting vs. Quota Management

Genellikle bu iki kavram birbirine karıştırılır, ancak çözdükleri problemler tamamen farklıdır:

  • Rate Limiting (Hız Sınırlandırma): Sistemin anlık kapasitesini korumak içindir. "Saniyede 100 istekten fazlasını kabul etme, aksi halde sunucu çöker" der. Teknik bir güvenlik sübabıdır.
  • Quota Management (Kota Yönetimi): İş modelini ve bütçeyi korumak içindir. "Bu servisin ayda 10.000 harita sorgulama bütçesi var, sonrasında hizmeti durdur veya fallback (yedek) senaryoya geç" der. Finansal bir güvenlik sübabıdır.

GCP, AWS veya OpenAI gibi servis sağlayıcılar, kendi altyapılarını korumak için size Rate Limit uygularlar. Ancak sizin bütçenizi korumak onların önceliği değildir. Siz onlara "Günde en fazla 50$ harcamak istiyorum" demezseniz, limitlerinizi sonuna kadar zorlar ve faturayı keserler.

Bu yüzden mimariyi tasarlarken sadece "Bu API'yi nasıl çağırırım?" diye değil, "Bu API çağrılarının maliyetini kendi içimde nasıl sınırlarım?" diye düşünmeliyiz.

Mimari Bakış: Kota Yönetimini Nerede Yapmalıyız?

Kota yönetimi tek bir katmanda çözülmesi gereken bir sorun değildir. Genellikle en etkili çözümler, farklı mimari katmanların birlikte çalışmasıyla ortaya çıkar.

1. API Gateway Seviyesinde (En İyi Savunma Hattı)

Modern mimarilerde, dış dünya ile servislerimiz arasındaki tüm trafik bir API Gateway (AWS API Gateway, Kong, Apigee vb.) üzerinden akar. Bu, kota yönetimi için harika bir fırsattır.

  • Neden Burada? API Gateway'ler, genellikle "Usage Plans" (Kullanım Planları) adı verilen yapılarla, hangi API anahtarının dakikada/saatte/günde kaç istek yapabileceğini kod yazmadan, deklaratif olarak tanımlamanıza olanak tanır.
  • Avantajı: Uygulama kodunu kirletmez. Merkezi bir yerden yönetilir ve tüm servislere uygulanabilir. Genellikle rate limiting ile birlikte gelir.
  • Dezavantajı: Esnekliği daha azdır. "Sadece A müşterisinin X özelliğini kullanırken kotasını düşür" gibi karmaşık iş kuralları uygulamak zordur.

2. Uygulama Seviyesinde (Esnek ve Akıllı Kontrol)

Makalenin devamında göreceğimiz Spring Boot örneği bu katmana aittir.

  • Neden Burada? API çağrısını yapan kodun tam içindeyiz. Bu, bize çağrının bağlamını (context) anlama ve çok daha akıllı kararlar verme gücü verir. Örneğin, "Bu bir deneme (trial) kullanıcısı, kotası daha düşük olmalı" veya "Bu veri çok kritik değil, kota dolduysa önbellekten eski veri dönebilirim" gibi kararları burada alırız.
  • Avantajı: Maksimum esneklik ve iş mantığına tam entegrasyon.
  • Dezavantajı: Her servisin kendi içinde bu mantığı uygulaması gerekir, bu da kod tekrarına ve yönetim zorluğuna yol açabilir. (Bu durum, merkezi bir "kota servisi" yazılarak aşılabilir.)

Öneri: Genellikle en iyi pratik, API Gateway seviyesinde genel ve cömert bir "güvenlik ağı" (örneğin, aylık 1 milyon istek) kurmak ve uygulama katmanında ise iş kurallarına dayalı daha hassas, günlük veya saatlik kotaları yönetmektir.

Spring Boot ile Akıllı İstek Yönetimi

Dış bir API'ye istek atmadan önce, bu isteğin gerçekten gerekli olup olmadığını ve kotamızı aşıp aşmadığını kendi backend sistemimizde kontrol etmeliyiz. Burada kuracağımız mantık aslında üç adımdan oluşuyor: Cache-First (Önce Önbellek), Fail-Fast (Hızlı Hata Fırlatma) ve Cost-Awareness (Maliyet Farkındalığı).

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDate;

@Service
public class GoogleMapsQuotaService {

    private final StringRedisTemplate redisTemplate;
    private final MapsClient mapsClient; // Dış API'ye istek atan Feign veya RestTemplate client'ı

    @Value("${app.quotas.google-maps.daily-limit}")
    private long dailyLimit;

    public GoogleMapsQuotaService(StringRedisTemplate redisTemplate, MapsClient mapsClient) {
        this.redisTemplate = redisTemplate;
        this.mapsClient = mapsClient;
    }

    public LocationData getCoordinates(String address) {
        // 1. Önce önbellekte (Cache) var mı diye kontrol et (Maliyet: 0$)
        String cacheKey = "maps:cache:" + address.toLowerCase().replace(" ", "");
        String cachedData = redisTemplate.opsForValue().get(cacheKey);

        if (cachedData != null) {
            return parseData(cachedData);
        }

        // 2. Bugün bu API'yi kaç kez çağırdığımızı kontrol et
        String today = LocalDate.now().toString();
        String quotaKey = "maps:quota:usage:" + today;

        // Mevcut kullanımı al, yoksa 0 döner
        String currentUsageStr = redisTemplate.opsForValue().get(quotaKey);
        long currentUsage = currentUsageStr != null ? Long.parseLong(currentUsageStr) : 0;

        // 3. Eğer günlük bütçe limitine ulaştıysak, dışarıya istek atma! (Fail-Fast)
        if (currentUsage >= dailyLimit) {
            // Burada fallback mekanizması çalıştırılabilir, default bir veri dönebilir
            // veya özel bir exception fırlatılıp UI tarafında bilgi verilebilir.
            throw new QuotaExceededException("Günlük Google Maps API kotası aşıldı! Limit: " + dailyLimit);
        }

        // 4. Limiti aşmadıysak gerçek API'ye git (Maliyet: ~0.005$)
        LocationData actualData = mapsClient.fetchCoordinatesFromGoogle(address);

        // 5. Kullanımı artır ve anahtarın ömrünü gün sonuna kadar geçerli yap
        redisTemplate.opsForValue().increment(quotaKey);
        redisTemplate.expire(quotaKey, Duration.ofDays(1));

        // 6. Bir dahaki sefere para ödememek için sonucu Cache'e yaz!
        redisTemplate.opsForValue().set(cacheKey, actualData.toString(), Duration.ofDays(30));

        return actualData;
    }

    // Yardımcı metodlar (parseData vb.) burada yer alır...
}

Yukarıdaki kurgu sayesinde, dışarıya atılacak her isteği önce Redis üzerinden bir maliyet süzgecinden geçirmiş oluyoruz. Gereksiz tekrarları önbellekten karşılıyor, bütçe aşıldığında ise sistemi anında durdurarak sürpriz faturaların önüne geçiyoruz.

Ancak yukarıdaki örnek, "Sabit Pencere" (Fixed Window) adı verilen en basit stratejiyi kullanır. Gelin, daha profesyonel yaklaşımlara göz atalım.

Strateji Seçimi: Sabit Pencereden Kayan Pencereye

Kota yönetiminde kullandığımız algoritma, sistemin esnekliğini ve dayanıklılığını doğrudan etkiler.

  • Sabit Pencere (Fixed Window): Yukarıdaki örnekte olduğu gibi, zaman dilimleri sabittir (her gün, her saat vb.). Gece 00:00'da sayaç sıfırlanır.
    • Sorun: Kötü niyetli veya hatalı bir istemci, pencerenin başlangıcında (örneğin 00:01'de) tüm kotayı tüketebilir ve sistem günün geri kalanında kullanılamaz hale gelir.
  • Kayan Pencere (Sliding Window Log): Bu daha modern yaklaşımda, her bir istek bir zaman damgasıyla birlikte bir kayda (örneğin Redis Sorted Set) eklenir. Kota kontrolü yapılırken "son 1 saat içindeki istek sayısı" hesaplanır. Bu, trafik patlamalarını daha yumuşak bir şekilde ele alır ve pencere sınırlarında yaşanan yığılmaları önler.
  • Token Bucket / Leaky Bucket: Bu algoritmalar genellikle rate limiting ile anılsa da, prensipleri kota yönetimine uyarlanabilir. Sisteme belirli aralıklarla "token" (jeton) eklenir ve her API çağrısı bir veya daha fazla jeton tüketir. Jeton kalmadığında istek reddedilir. Bu model, farklı API çağrılarının farklı maliyetlere (farklı sayıda jeton) sahip olduğu esnek iş modelleri için idealdir.

Zenginleştirilmiş Fallback (Yedek) Senaryoları: Hata Fırlatmaktan Daha İyisi

QuotaExceededException fırlatmak en basit yöntemdir, ancak kullanıcı deneyimini olumsuz etkiler. Profesyonel sistemler daha akıllıca davranır:

  • Düşük Kaliteli Veri Sunma (Degraded Data): Kota aşıldığında, harici API'ye gitmek yerine, daha önce önbelleklenmiş veya daha az hassas olan "kabul edilebilir" bir veri dönebiliriz. Örneğin, tam adres yerine sadece şehir bilgisi gibi. Bu, servisin tamamen durmasını engeller.
  • Asenkron İşleme ve Kuyruk (Queueing): Kota aşıldığında isteği hemen reddetmek yerine, bir mesaj kuyruğuna (RabbitMQ, SQS) atabiliriz. Kota tekrar kullanılabilir olduğunda (örneğin bir sonraki saat), kuyruktaki işler toplu olarak işlenir. Bu, kullanıcıya "İsteğiniz alındı, verileriniz kısa süre içinde güncellenecektir" mesajı göstermemizi sağlar.
  • İkincil Bir Sağlayıcıya Yönelme (Failover): Bu, en gelişmiş ama en dayanıklı yöntemdir. Eğer birincil sağlayıcının (örneğin Google Maps) kotası dolarsa, sistem otomatik olarak ikinci bir sağlayıcıya (örneğin Mapbox) yönlendirilir. Bu strateji, ek entegrasyon maliyeti gerektirir ancak kritik servisler için hayat kurtarıcıdır.

Eğer API'yi Sağlayan Taraf Sizseniz...

Madalyonun diğer yüzünü de düşünelim. Eğer kendi API'nizi dış dünyaya sunuyorsanız, siz de kendi müşterileriniz için kota yönetimi yapmak zorundasınız.

  • Katmanlı Abonelikler (Tiered Subscriptions): Kullanıcılarınıza "Free", "Pro", "Enterprise" gibi farklı seviyelerde abonelikler sunun. Her katmanın kendi API kotası (örneğin, Free için ayda 1.000, Pro için 100.000 istek) olur.
  • Maliyet Farkındalığı: Farklı API endpoint'leriniz farklı maliyetlere sahip olabilir. Örneğin, basit bir veri okuma (GET /users/{id}) 1 kredi harcarken, karmaşık bir rapor oluşturan (POST /reports) bir endpoint 50 kredi harcayabilir. Kota yönetiminiz bu maliyet farkını yansıtmalıdır.
  • Header'lar ile Bilgilendirme: API yanıtlarınızın header'larına (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset gibi) kullanıcının kalan kota bilgilerini eklemek, onların da kendi istemcilerini daha sorumlu bir şekilde yazmalarına yardımcı olur.

Hata Defteri: API Maliyetlerinde Hayatta Kalma Rehberi

Sadece koda güvenmek yeterli değildir. Bulut sağlayıcı (Cloud Provider) konsolunda da almamız gereken hayati önlemler var. Geçmişteki acı tecrübelerden derlediğim "best practice" listesi:

  • API Anahtarlarını Çıplak Bırakmayın: GCP veya AWS konsolunda bir API Key oluşturduğunuzda, bu anahtarı mutlaka kısıtlayın (Restriction). Sadece belirli IP adreslerinden (sizin backend sunucularınız) veya belirli domain'lerden gelen istekleri kabul edecek şekilde yapılandırın. GitHub'a sızan korumasız bir key, saniyeler içinde botnetlerin oyuncağı olur.
  • Soft Limit ve Hard Limit Birlikte Kullanılmalı: * Soft Limit: Bütçenizin %70'ine ulaştığınızda size sadece e-posta veya Slack bildirimi atar. Erken uyarı sistemidir.
    • Hard Limit: Bütçenizin %100'üne ulaştığınızda, sistemi acımasızca "Kapatır" (Quotas panelinden günlük API çağrı üst sınırını belirlemek). Sistemi kapatmak kullanıcı deneyimi açısından kötü görünebilir, ancak fatura yüzünden şirketin iflas etmesinden iyidir.
  • Önbellekleme (Caching) Sizin En İyi Dostunuzdur: Sık sorgulanan statik veya yarı-statik verileri (örneğin popüler mağazalarınızın koordinatları) mutlaka sisteminizde (Redis, Memcached) önbellekleyin. Aynı koordinatı Google'a günde 10.000 kez sormanın mantıklı hiçbir açıklaması olamaz.
  • Circuit Breaker (Devre Kesici) Kullanın: Dış API yavaşladığında veya hata vermeye başladığında, uygulamanızın inatla istek atmaya devam etmesini engellemek için Resilience4j gibi kütüphanelerle devre kesiciler kurgulayın. Bazen bozuk bir API'den dönen HTTP 500 hataları bile kotanızdan düşebilir ve hem paranızı hem de thread'lerinizi tüketir.

Son Söz

Bulut dünyasında hiçbir kaynak gerçekten sınırsız değildir; limiti her zaman kredi kartı ekstreniz belirler. Bir backend geliştiricisi olarak kod yazarken, yazdığımız her satırın operasyonel bir maliyeti olduğunu bilmek zorundayız. "Defensive Programming" (Savunmacı Programlama) yaklaşımını sadece güvenlik açıklarına karşı değil, maliyet yönetimine de uygulamalıyız.

Quota yönetimi, ilk başta sisteme ek bir karmaşıklık gibi görünebilir. Ancak günün sonunda geceleri rahat uyumanızı sağlayan şey, sadece yazdığınız temiz kod değil, sisteminizin bütçenizi sinsice tüketmeyeceğinden emin olmanızdır.

Siz projelerinizde harici API maliyetlerini kontrol altında tutmak için ne gibi stratejiler izliyorsunuz? Sürpriz bir fatura hikayeniz varsa, yorumlarda bizimle paylaşın, beraber dertleşelim! 🚀

Tüm Bulut & Maliyet yazıları