Sürdürülebilir Mimari Desenler: Depolama, Önbellek ve Toplu İş
Her gün aldığımız mimari kararlar; depolamayı nasıl yapılandırdığımız, depolamayı nasıl yönettiğimiz önbellek, toplu işleri nasıl düzenlediğimiz - Ölçümde yazılımın karbon ayak izi 10-100 kat daha yüksek kodun mikro optimizasyonlarıyla karşılaştırıldığında. Kötü tasarlanmış mimari Her istek üzerine veritabanına erişen, önbelleğe alma kullanmayan ve mümkün olduğunda verileri gerçek zamanlı olarak işleyen bunu toplu olarak yapmak, iyi tasarlanmış bir mimariye kıyasla çok daha fazla enerji tüketebilir.
Tarafından yapılan bir analize göre Yeşil Yazılım Vakfı Etki Çerçevesi, mimari seçimler depolama katmanlama, önbelleğe alma ve toplu planlama ile ilgili Potansiyelin %40-60'ı emisyonların azaltılması kurumsal bir yazılım sisteminin Bu sadece optimizasyonla ilgili değil Teknik: Her mimar ve geliştiricinin dikkate alması gereken mesleki bir sorumluluktur. kişinin mesleğinin ayrılmaz bir parçasıdır.
Yeşil Yazılım serisinin bu dokuzuncu makalesinde şunları inceleyeceğiz: sürdürülebilir mimari desenler daha etkili: akıllı depolama katmanlamasından karbona duyarlı önbelleğe, toplu planlamadan API sürdürülebilir tasarımında yeşil enerji pencereleri. Gerçek kod örnekleri ve eksiksiz bir vaka çalışmasıyla Bu da günlük 1 milyon ziyaret alan bir e-ticaret sitesinin karbon ayak izinde %45 oranında azalma olduğunu gösteriyor.
Ne Öğreneceksiniz
- Enerjiyi azaltmak için katmanlı depolama (sıcak/sıcak/soğuk/arşiv) ve veri yaşam döngüsü politikaları
- Karbona duyarlı önbellekleme modeli: coğrafi akıllı CDN, çok düzeyli önbellekleme, optimize edilmiş geçersiz kılma
- Carbon Aware SDK ile yeşil enerji pencerelerinde toplu işlem
- Doğru boyutlandırma ve otomatik ölçek küçültme: Enerji israfına neden olan "boşta kalan bilgi işlem" nasıl önlenir?
- Sürdürülebilir veritabanı modelleri: sorgu optimizasyonu, gerçekleştirilmiş görünümler, okuma kopyaları
- Ağ verimliliği: sıkıştırma, HTTP/3, uç bilişim
- Sürdürülebilir ön uç modelleri: yavaş yükleme, görüntü optimizasyonu, karanlık mod enerjisi
- Sürdürülebilir API tasarımı: sayfalandırma, alan seçimi, GraphQL ve REST karşılaştırması
- Hizmet başına Prometheus, Grafana ve SCI puanıyla karbon izleme
- E-ticaret örnek olay incelemesi: Tüm kalıpları uygulayarak %-45 karbon ayak izi
Yeşil Yazılım Serisi — 10 Makale
| # | Başlık | Odak | Durum |
|---|---|---|---|
| 1 | Yeşil Yazılım Mühendisliğinin İlkeleri | GSF, SCI, 8 temel prensip | Yayınlandı |
| 2 | CodeCarbon ile Emisyonların Ölçülmesi | CodeCarbon, Python enerji profili oluşturma | Yayınlandı |
| 3 | Carbon Aware SDK: Akıllı Planlama | İş yüklerinin zamansal ve coğrafi hareketi | Yayınlandı |
| 4 | Climatiq API: Gerçek Zamanlı Emisyon Verileri | API emisyonları, dönüşüm faktörleri | Yayınlandı |
| 5 | GreenOps: Kod Olarak Sürdürülebilir Altyapı | Terraform yeşili, spot örnekler, otomatik ölçeklendirme | Yayınlandı |
| 6 | Yapay Zeka ve Karbon Ayak İzi: Sorumlu Eğitim | Verimli modeller, LoRA, nicemleme | Yayınlandı |
| 7 | Yazılım İşlem Hatlarında Kapsam 3 | Yukarı/aşağı yönde emisyonlar, tedarik zinciri | Yayınlandı |
| 8 | Kapsam Modelleme: Etkiyi Simüle Etme | Simülasyon, durum analizi, yeşil yol haritası | Yayınlandı |
| 9 | Sürdürülebilir Mimari Desenler | Depolama, Önbellek, Toplu İş, API tasarımı | Bu makale |
| 10 | Yazılım Ekipleri için ESG, CSRD ve Uyumluluk | Zorunlu raporlama, denetim takibi, ESG ölçümleri | Sonraki |
Depolama Katmanlaması: Doğru Veri, Doğru Yerde
Modern sistemlerdeki ilk büyük enerji israfı depolama optimize edilmemiş: Yüksek performanslı NVMe SSD'lerde veya daha kötüsü RAM belleğinde bulunan nadiren erişilen veriler. Bir SSD işletme şu ana kadar tüketir: Boşta 6-10 Watt ve yük altında 25 Watt. HDD'de depolama 5-8 Watt tüketir. Teyp depolama veya soğuk depolama, boştayken TB başına 0,01 Watt'tan daha az enerji tüketir.
stratejisi katmanlı depolama Verilerin frekansa göre sınıflandırılmasından oluşur bunlara erişin ve bunları otomatik olarak enerjiye uygun depolama katmanına taşıyın. Bir organizasyon Katmanlı depolamayı doğru şekilde uygulayan ortamlar, depolama maliyetlerini azaltır %40-70 ve ilgili karbon ayak izi de benzer oranda.
4 Katmanlı Depolama Mimarisi
| Seviye | Tip | Gecikme | Maliyet/TB/ay | Enerji/TB | Tipik Kullanım |
|---|---|---|---|---|---|
| Sıcak | NVMe SSD / Redis | < 1ms | 200-500$ | Yüksek (25W) | Aktif veriler 30 gün sürer |
| Ilık | SSD Standardı / S3 Standardı | 1-10ms | 20-50$ | Orta (8W) | 1-12 aylık veriler, haftalık olarak erişilir |
| Soğuk | HDD/S3 IA/GCS Nearline | 50-250ms | 4-10$ | Düşük (3W) | 1-7 yıllık veriler, aylık erişim |
| Arşiv | Buzul / GCS Arşivi / Bant | 1-12 saat | 0,40-1$ | Minimum (<0,01W) | Veriler >7 yıl, uyumluluk, yedekleme |
Otomatik Veri Yaşam Döngüsü Politikalarını Uygulayın
Katmanlı depolamanın kalbi yaşam döngüsü politikalarının otomasyonudur. Yönetmek yerine Verileri manuel olarak taşıyarak sistemin otomatik olarak uygulayacağı kuralları tanımlarız. Veri erişiminin yaşı ve sıklığı.
from dataclasses import dataclass
from datetime import datetime, timedelta
from enum import Enum
from typing import Optional
import boto3
import logging
logger = logging.getLogger(__name__)
class StorageTier(Enum):
HOT = "hot"
WARM = "warm"
COLD = "cold"
ARCHIVE = "archive"
# Energia media per operazione per tier (Wh per GB spostato/acceduto)
TIER_ENERGY_PROFILE = {
StorageTier.HOT: {"idle_w_per_tb": 25, "access_wh_per_gb": 0.002},
StorageTier.WARM: {"idle_w_per_tb": 8, "access_wh_per_gb": 0.0008},
StorageTier.COLD: {"idle_w_per_tb": 3, "access_wh_per_gb": 0.0003},
StorageTier.ARCHIVE: {"idle_w_per_tb": 0.01, "access_wh_per_gb": 0.00001},
}
@dataclass(frozen=True)
class LifecyclePolicy:
"""Immutabile: definisce le soglie di transizione tra tier."""
hot_to_warm_days: int = 30
warm_to_cold_days: int = 90
cold_to_archive_days: int = 365
delete_after_days: Optional[int] = None # None = mantieni forever
@dataclass(frozen=True)
class DataAsset:
"""Rappresenta un asset dati con le sue metriche."""
key: str
size_gb: float
created_at: datetime
last_accessed: datetime
current_tier: StorageTier
access_count_30d: int
class DataLifecycleManager:
"""Gestisce il lifecycle dei dati verso tier energeticamente ottimali."""
def __init__(self, policy: LifecyclePolicy, s3_client=None):
self._policy = policy
self._s3 = s3_client or boto3.client("s3")
def evaluate_tier_transition(self, asset: DataAsset) -> Optional[StorageTier]:
"""
Determina se un asset deve essere spostato.
Ritorna il nuovo tier o None se nessun cambio necessario.
"""
age_days = (datetime.utcnow() - asset.created_at).days
days_since_access = (datetime.utcnow() - asset.last_accessed).days
# Regola 1: archivio per dati molto vecchi
if age_days > self._policy.cold_to_archive_days:
if asset.current_tier != StorageTier.ARCHIVE:
return StorageTier.ARCHIVE
# Regola 2: cold per dati accessati raramente
elif age_days > self._policy.warm_to_cold_days and asset.access_count_30d < 2:
if asset.current_tier in (StorageTier.HOT, StorageTier.WARM):
return StorageTier.COLD
# Regola 3: warm per dati maturi con accesso moderato
elif age_days > self._policy.hot_to_warm_days and asset.access_count_30d < 10:
if asset.current_tier == StorageTier.HOT:
return StorageTier.WARM
return None # Nessun cambio necessario
def calculate_carbon_savings(
self,
asset: DataAsset,
target_tier: StorageTier,
carbon_intensity_g_kwh: float = 350 # Media europea 2025
) -> dict:
"""Stima il risparmio CO2 mensile dal cambio di tier."""
current_profile = TIER_ENERGY_PROFILE[asset.current_tier]
target_profile = TIER_ENERGY_PROFILE[target_tier]
# Energia idle mensile (30 giorni * 24 ore)
current_idle_wh = (current_profile["idle_w_per_tb"] * asset.size_gb / 1000) * 720
target_idle_wh = (target_profile["idle_w_per_tb"] * asset.size_gb / 1000) * 720
savings_wh = current_idle_wh - target_idle_wh
savings_kwh = savings_wh / 1000
savings_co2_g = savings_kwh * carbon_intensity_g_kwh
return {
"monthly_savings_kwh": round(savings_kwh, 4),
"monthly_savings_co2_g": round(savings_co2_g, 2),
"annual_savings_co2_kg": round(savings_co2_g * 12 / 1000, 3),
}
def execute_transition(self, asset: DataAsset, target_tier: StorageTier, bucket: str) -> dict:
"""Sposta l'asset al nuovo tier su S3 con storage class appropriata."""
storage_class_map = {
StorageTier.WARM: "STANDARD_IA",
StorageTier.COLD: "GLACIER_IR",
StorageTier.ARCHIVE: "DEEP_ARCHIVE",
}
storage_class = storage_class_map.get(target_tier, "STANDARD")
savings = self.calculate_carbon_savings(asset, target_tier)
try:
# Copia con nuova storage class (immutabile: crea nuovo oggetto S3)
self._s3.copy_object(
CopySource={"Bucket": bucket, "Key": asset.key},
Bucket=bucket,
Key=asset.key,
StorageClass=storage_class,
MetadataDirective="COPY",
)
logger.info(
"Tier transition: %s -> %s | CO2 saved: %.2fg/month",
asset.current_tier.value, target_tier.value,
savings["monthly_savings_co2_g"]
)
return {"success": True, "savings": savings}
except Exception as e:
logger.error("Transition failed for %s: %s", asset.key, e)
return {"success": False, "error": str(e)}
# AWS S3 Lifecycle Policy come JSON (alternativa Infrastructure-as-Code)
S3_LIFECYCLE_POLICY = {
"Rules": [
{
"ID": "GreenDataLifecycle",
"Status": "Enabled",
"Transitions": [
{"Days": 30, "StorageClass": "STANDARD_IA"},
{"Days": 90, "StorageClass": "GLACIER_IR"},
{"Days": 365, "StorageClass": "DEEP_ARCHIVE"},
],
"Expiration": {"Days": 2555}, # 7 anni poi elimina
}
]
}
Sıkıştırma: En Az Değerlendirilen Desen
Veri sıkıştırma aynı anda depolama tüketimini azaltır (korunması gereken bayt sayısı azalır) ve ağ trafiği (aktarılacak daha az bayt). Tasarruf edilen enerji / harcanan enerji oranı sıkıştırma tipik olarak 10:1 ila 100:1, onu en popüler modellerden biri haline getiriyor enerji açısından avantajlıdır.
import zlib
import lz4.frame
import zstandard as zstd
import time
from dataclasses import dataclass
from typing import Callable
@dataclass(frozen=True)
class CompressionProfile:
"""Profilo immutabile di un algoritmo di compressione."""
name: str
compress_fn: Callable[[bytes], bytes]
decompress_fn: Callable[[bytes], bytes]
cpu_intensity: float # 1.0 = baseline, <1 = meno CPU, >1 = più CPU
best_for: str
def benchmark_compression(data: bytes, profile: CompressionProfile) -> dict:
"""Misura efficienza energetica di un algoritmo su dati reali."""
# Compressione
start = time.perf_counter()
compressed = profile.compress_fn(data)
compress_ms = (time.perf_counter() - start) * 1000
# Decompressione
start = time.perf_counter()
profile.decompress_fn(compressed)
decompress_ms = (time.perf_counter() - start) * 1000
ratio = len(data) / len(compressed)
# Energia stimata: CPU_time * intensità * 0.001 Wh per ms di CPU
compress_energy_mwh = compress_ms * profile.cpu_intensity * 0.001
storage_savings_pct = (1 - 1/ratio) * 100
return {
"algorithm": profile.name,
"ratio": round(ratio, 2),
"storage_savings_pct": round(storage_savings_pct, 1),
"compress_ms": round(compress_ms, 2),
"decompress_ms": round(decompress_ms, 2),
"compress_energy_mwh": round(compress_energy_mwh, 4),
"best_for": profile.best_for,
}
# Profili degli algoritmi principali
COMPRESSION_PROFILES = [
CompressionProfile(
name="zlib-6",
compress_fn=lambda d: zlib.compress(d, level=6),
decompress_fn=zlib.decompress,
cpu_intensity=1.0,
best_for="Compatibilità universale, HTTP responses"
),
CompressionProfile(
name="lz4",
compress_fn=lz4.frame.compress,
decompress_fn=lz4.frame.decompress,
cpu_intensity=0.15, # Molto veloce, meno CPU
best_for="Stream real-time, alta frequenza di accesso"
),
CompressionProfile(
name="zstd-3",
compress_fn=lambda d: zstd.ZstdCompressor(level=3).compress(d),
decompress_fn=lambda d: zstd.ZstdDecompressor().decompress(d),
cpu_intensity=0.4,
best_for="Bilanciamento ottimale ratio/velocità (raccomandato)"
),
CompressionProfile(
name="zstd-19",
compress_fn=lambda d: zstd.ZstdCompressor(level=19).compress(d),
decompress_fn=lambda d: zstd.ZstdDecompressor().decompress(d),
cpu_intensity=2.5, # Alta CPU per compressione massima
best_for="Archivio cold/archive, dati rari, batch notturno"
),
]
# Regola pratica per scelta del livello di compressione:
# HOT tier -> lz4 (latenza minima, decompress velocissimo)
# WARM tier -> zstd-3 (bilanciamento ottimale)
# COLD tier -> zstd-9 (ratio migliore, latenza tollerabile)
# ARCHIVE -> zstd-19 o brotli-11 (massimo risparmio storage)
Karbon Bilinçli Önbellekleme: Daha Az Hesaplayarak Daha Fazla Hizmet Verin
Önbelleğe alma muhtemelen yazılım emisyonlarını azaltmanın en güçlü modelidir: her önbellek isabeti, ilgili hesaplamanın enerji tüketimini tamamen ortadan kaldırır. Bir sistem %90 önbellek isabet oranı önbelleksiz hesaplamalara kıyasla hesaplamaların yalnızca 1/10'unu gerçekleştirir, orantılı enerji tasarrufu ile.
Ancak "karbon bilinçli önbelleğe alma" basit performans optimizasyonunun ötesine geçer: theşebekenin karbon yoğunluğu neyi ne kadara önceden yükleyeceğimize karar vermek verileri önbellekte tutma zamanı ve önbellek ısıtma işlemlerinin ne zaman gerçekleştirileceği.
Çok Düzeyli Önbellek Mimarisi
Önbellek Düzeyleri ve Enerji Etkisi
| Seviye | Teknoloji | Gecikme | Vuruş için Enerji | Enerji Tasarrufu ve DB |
|---|---|---|---|---|
| L1: İşlemde | HashMap, RAM'de LRU | < 0,1 ms | ~0,001 mWh | %99,9 tasarruf |
| L2: Dağıtılmış | Redis, Memcached | 0,1-1ms | ~0,01 mWh | %99 tasarruf |
| L3: CDN Kenarı | CloudFront, Hızlı, CF | 1-20ms | ~0,05 mWh | %95 tasarruf |
| Veritabanı Sorgusu | PostgreSQL, MySQL | 5-100ms | ~1-10 mWh | — taban çizgisi |
import Redis from "ioredis";
interface CacheEntry<T> {
readonly data: T;
readonly cachedAt: number;
readonly ttlMs: number;
readonly carbonIntensityAtCache: number; // gCO2/kWh quando cacheato
}
interface CarbonAwareCacheConfig {
readonly l1MaxEntries: number;
readonly l1DefaultTtlMs: number;
readonly l2DefaultTtlMs: number;
readonly lowCarbonThreshold: number; // gCO2/kWh
readonly highCarbonTtlMultiplier: number; // TTL più lungo quando carbon e alto
}
const DEFAULT_CONFIG: CarbonAwareCacheConfig = {
l1MaxEntries: 1000,
l1DefaultTtlMs: 60_000, // 1 minuto L1
l2DefaultTtlMs: 300_000, // 5 minuti L2
lowCarbonThreshold: 200, // <200 gCO2/kWh = energia verde
highCarbonTtlMultiplier: 3, // TTL 3x più lungo su energia sporca
};
class CarbonAwareMultiLevelCache<T> {
private readonly l1 = new Map<string, CacheEntry<T>>();
private readonly config: CarbonAwareCacheConfig;
private readonly redis: Redis;
private currentCarbonIntensity = 350; // Default, aggiornato periodicamente
// Metriche immutabili per reporting
private readonly metrics = {
l1Hits: 0,
l2Hits: 0,
misses: 0,
carbonSavedGrams: 0,
};
constructor(redisClient: Redis, config: Partial<CarbonAwareCacheConfig> = {}) {
this.redis = redisClient;
this.config = { ...DEFAULT_CONFIG, ...config };
}
async get(key: string): Promise<T | null> {
// L1: memoria locale (nessuna I/O, minima energia)
const l1Entry = this.l1.get(key);
if (l1Entry && !this.isExpired(l1Entry)) {
this.metrics.l1Hits++;
this.metrics.carbonSavedGrams += 0.005; // ~5mg CO2 risparmiati vs DB
return l1Entry.data;
}
// L2: Redis distribuito
try {
const raw = await this.redis.get(key);
if (raw) {
const entry: CacheEntry<T> = JSON.parse(raw);
if (!this.isExpired(entry)) {
// Promuovi in L1
this.setL1(key, entry.data, this.config.l1DefaultTtlMs);
this.metrics.l2Hits++;
this.metrics.carbonSavedGrams += 0.003; // ~3mg CO2 vs DB
return entry.data;
}
}
} catch (err) {
console.warn("L2 cache read failed, fallback to source:", err);
}
this.metrics.misses++;
return null;
}
async set(key: string, data: T): Promise<void> {
// TTL adattivo in base all'intensità carbonica corrente
// Quando l'energia e verde (low carbon), TTL più breve e accettabile
// Quando l'energia e "sporca" (high carbon), TTL più lungo per ridurre ricalcoli
const isHighCarbon = this.currentCarbonIntensity > this.config.lowCarbonThreshold;
const ttlMultiplier = isHighCarbon ? this.config.highCarbonTtlMultiplier : 1;
const l1TtlMs = this.config.l1DefaultTtlMs * ttlMultiplier;
const l2TtlMs = this.config.l2DefaultTtlMs * ttlMultiplier;
this.setL1(key, data, l1TtlMs);
// Scrivi in Redis in modo non bloccante
const entry: CacheEntry<T> = {
data,
cachedAt: Date.now(),
ttlMs: l2TtlMs,
carbonIntensityAtCache: this.currentCarbonIntensity,
};
await this.redis.set(key, JSON.stringify(entry), "PX", l2TtlMs);
}
private setL1(key: string, data: T, ttlMs: number): void {
// Evict LRU se pieno
if (this.l1.size >= this.config.l1MaxEntries) {
const firstKey = this.l1.keys().next().value;
if (firstKey) this.l1.delete(firstKey);
}
this.l1.set(key, {
data,
cachedAt: Date.now(),
ttlMs,
carbonIntensityAtCache: this.currentCarbonIntensity,
});
}
private isExpired(entry: CacheEntry<T>): boolean {
return Date.now() - entry.cachedAt > entry.ttlMs;
}
updateCarbonIntensity(intensityGCO2PerKWh: number): void {
this.currentCarbonIntensity = intensityGCO2PerKWh;
}
getMetrics(): Readonly<typeof this.metrics> {
const total = this.metrics.l1Hits + this.metrics.l2Hits + this.metrics.misses;
return {
...this.metrics,
hitRate: total ? ((this.metrics.l1Hits + this.metrics.l2Hits) / total * 100).toFixed(1) + "%" : "N/A",
} as any;
}
}
Karbon Bilinçli CDN: Yeşil Kenardan Hizmet Verme
Cloudflare veya Fastly gibi bir CDN, düzinelerce karbon yoğun bölgede uç düğümlere sahiptir farklı. Gecikme izin verdiğinde trafiği daha yeşil enerjiyle kenarlara yönlendirin, servis emisyonlarını azaltabilir %20-40.
// Cache invalidation: uno dei problemi più difficili del software
// Pattern carbon-aware: invalida in batch durante finestre di bassa carbon intensity
interface InvalidationJob {
readonly tags: readonly string[];
readonly priority: "immediate" | "carbon-optimal" | "batch-night";
readonly scheduledAt: Date;
readonly maxDelayMs: number;
}
class CarbonAwareCacheInvalidator {
private readonly pendingJobs: InvalidationJob[] = [];
private readonly carbonAwareSdk: any; // Carbon Aware SDK instance
async scheduleInvalidation(
tags: string[],
priority: InvalidationJob["priority"] = "carbon-optimal",
maxDelayMs: number = 3_600_000 // 1 ora di tolleranza
): Promise<{ jobId: string; scheduledFor: Date }> {
if (priority === "immediate") {
await this.executePurge(tags);
return { jobId: crypto.randomUUID(), scheduledFor: new Date() };
}
// Trova la finestra con minore carbon intensity nelle prossime maxDelayMs
const optimalTime = await this.findGreenWindow(maxDelayMs);
const job: InvalidationJob = {
tags: Object.freeze(tags),
priority,
scheduledAt: optimalTime,
maxDelayMs,
};
// Immutabile: non mutiamo pendingJobs esistenti, aggiungiamo nuovo
this.pendingJobs.push(job);
return {
jobId: crypto.randomUUID(),
scheduledFor: optimalTime,
};
}
private async findGreenWindow(maxDelayMs: number): Promise<Date> {
const windowEnd = new Date(Date.now() + maxDelayMs);
try {
// Carbon Aware SDK: trova il momento con minore intensità carbonica
const forecast = await this.carbonAwareSdk.getForecast({
location: "westeurope",
start: new Date(),
end: windowEnd,
duration: 15, // Job richiede ~15 minuti
});
return new Date(forecast.optimalWindow.start);
} catch {
// Fallback: esegui immediatamente se il forecast non e disponibile
return new Date();
}
}
private async executePurge(tags: string[]): Promise<void> {
// Cloudflare Cache Tag Purge API
await fetch("https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache", {
method: "POST",
headers: {
"Authorization": "Bearer CF_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ tags }),
});
}
}
Karbon Bilinçli Toplu İşleme: Enerji Yeşil Olduğunda Çalışın
Toplu işleme ideal adaydır zamansal kaymaprensip karbon yoğunluğunun olduğu zamanlarda esnek iş yüklerinin taşınmasından oluşur elektrik şebekesi daha düşüktür. Partilerini her gece çalıştıran bir sistem Rüzgar ve güneş enerjisi üretimi fazlası, ilgili emisyonları azaltabilir %30-70 sabit zamanlı bir yürütmeyle karşılaştırıldığında.
Parti Paradoksu 02:00'da
Birçok sistem "daha az trafik olduğu için" partileri saat 02:00'de planlar. Ama Avrupa'da var gece her zaman daha düşük karbon yoğunluğunun olduğu zaman değildir: birçok bölgede güneş ve geceleri yok ve rüzgar değişiyor. Bazı bölgelerde, 10:00-14:00 (güneş enerjisinin en yoğun olduğu saat) veya 02:00-06:00 arası (sabit rüzgar) çok daha düşük karbon yoğunluğuna sahiptir. Gerçek verileri kullanın Geçici genel kural yerine Carbon Aware SDK.
from celery import Celery
from celery.schedules import crontab
from datetime import datetime, timedelta
from typing import Optional, NamedTuple
import httpx
import asyncio
import logging
logger = logging.getLogger(__name__)
app = Celery("green_batch", broker="redis://localhost:6379/0")
class GreenWindow(NamedTuple):
"""Finestra di esecuzione ottimale per carbon footprint."""
start: datetime
end: datetime
carbon_intensity_g_kwh: float
savings_pct_vs_now: float
class CarbonAwareBatchScheduler:
"""Schedula batch job nelle finestre a minore carbon intensity."""
BASE_CARBON_API = "https://api.electricitymap.org/v3"
def __init__(self, api_token: str, location: str = "IT"):
self._token = api_token
self._location = location
async def get_optimal_window(
self,
job_duration_minutes: int,
max_delay_hours: int = 12,
min_savings_pct: float = 15.0
) -> Optional[GreenWindow]:
"""
Trova la finestra ottimale per eseguire un batch job.
Ritorna None se nessuna finestra con risparmio sufficiente trovata.
"""
headers = {"auth-token": self._token}
async with httpx.AsyncClient() as client:
# Intensità carbonica attuale
current_resp = await client.get(
f"{self.BASE_CARBON_API}/carbon-intensity/latest",
params={"zone": self._location},
headers=headers
)
current_data = current_resp.json()
current_intensity = current_data["carbonIntensity"]
# Forecast delle prossime ore
forecast_resp = await client.get(
f"{self.BASE_CARBON_API}/carbon-intensity/forecast",
params={"zone": self._location},
headers=headers
)
forecast = forecast_resp.json()
now = datetime.utcnow()
deadline = now + timedelta(hours=max_delay_hours)
best_window: Optional[GreenWindow] = None
min_intensity = current_intensity
for slot in forecast["forecast"]:
slot_time = datetime.fromisoformat(slot["datetime"].replace("Z", "+00:00"))
slot_time = slot_time.replace(tzinfo=None)
if slot_time < now or slot_time > deadline:
continue
intensity = slot["carbonIntensity"]
if intensity < min_intensity:
min_intensity = intensity
savings_pct = (current_intensity - intensity) / current_intensity * 100
if savings_pct >= min_savings_pct:
best_window = GreenWindow(
start=slot_time,
end=slot_time + timedelta(minutes=job_duration_minutes),
carbon_intensity_g_kwh=intensity,
savings_pct_vs_now=round(savings_pct, 1),
)
return best_window
async def schedule_green(
self,
task_name: str,
job_duration_minutes: int,
task_kwargs: dict,
max_delay_hours: int = 12
) -> dict:
"""Schedula un Celery task nella finestra più verde."""
window = await self.get_optimal_window(
job_duration_minutes=job_duration_minutes,
max_delay_hours=max_delay_hours
)
if window:
delay_seconds = (window.start - datetime.utcnow()).total_seconds()
task = app.send_task(
task_name,
kwargs=task_kwargs,
countdown=max(0, int(delay_seconds))
)
logger.info(
"Scheduled '%s' for %s (%.1f%% CO2 savings, %.0fg/kWh)",
task_name, window.start.isoformat(),
window.savings_pct_vs_now, window.carbon_intensity_g_kwh
)
return {
"task_id": task.id,
"scheduled_for": window.start.isoformat(),
"carbon_intensity": window.carbon_intensity_g_kwh,
"co2_savings_pct": window.savings_pct_vs_now,
}
else:
# Nessuna finestra verde disponibile: esegui ora
task = app.send_task(task_name, kwargs=task_kwargs)
logger.warning("No green window found for '%s', executing immediately", task_name)
return {"task_id": task.id, "scheduled_for": "now", "co2_savings_pct": 0}
# Definizione dei task Celery con metriche carbon
@app.task(name="batch.nightly_report", bind=True)
def nightly_report_batch(self, report_date: str) -> dict:
"""
Genera report notturni. Non time-critical: ideale per temporal shifting.
Carbon savings tipici: 20-60% spostando dalle 02:00 alla finestra verde.
"""
logger.info("Generating report for %s (carbon-optimal execution)", report_date)
# ... logica report
return {"status": "completed", "report_date": report_date}
@app.task(name="batch.data_sync", bind=True)
def data_sync_batch(self, source: str) -> dict:
"""
Sincronizzazione dati tra sistemi. Tollerante a ritardi di alcune ore.
"""
# ... logica sync
return {"status": "synced", "source": source}
# Scheduler che usa carbon-awareness invece di crontab fisso
async def schedule_nightly_jobs():
scheduler = CarbonAwareBatchScheduler(
api_token="your_electricity_maps_token",
location="IT" # Italia
)
# Report: accetta fino a 12h di ritardo
await scheduler.schedule_green(
task_name="batch.nightly_report",
job_duration_minutes=45,
task_kwargs={"report_date": datetime.now().strftime("%Y-%m-%d")},
max_delay_hours=12
)
# Data sync: accetta fino a 6h di ritardo
await scheduler.schedule_green(
task_name="batch.data_sync",
job_duration_minutes=20,
task_kwargs={"source": "salesforce"},
max_delay_hours=6
)
Doğru Boyutlandırma ve Otomatik Ölçeklendirme: Görünmez Atığı Durdurma
2025 Gartner araştırması şunu tahmin ediyor: Kurumsal bulut kaynaklarının %35-40'ı aşırı provizyon: %10-15 CPU ile çalışan sunucular, %90 kullanılmayan belleğe sahip veritabanları, Lambda, 256MB yeterli olduğunda ayrılan 3GB RAM ile çalışıyor. Bu "boşta bilgi işlem" saf enerji israfı.
Sistematik doğru boyutlandırma — gereksinimleri karşılamak için kaynakları gereken minimum düzeye indirmek performans - genellikle tek müdahaledir en iyi enerji yatırım getirisi. Kodun yeniden yazılmasını gerektirmez; bu bir yapılandırma ve izleme meselesidir.
Karbon Azaltımı için Doğru Boyutlandırma Stratejileri
| Strateji | Tipik Tasarruflar | Karmaşıklık | Risk |
|---|---|---|---|
| Aşırı sağlanan örneklerin boyutunu azaltın | %20-40 | Düşük | Düşük (kolay geri alma) |
| Agresif otomatik ölçeklendirme | %30-60 | Ortalama | Orta (soğuk başlatma gecikmesi) |
| Aralıklı iş yükleri için sunucusuz | %50-90 | Yüksek | Orta (soğuk başlangıç, satıcıya bağlılık) |
| Grup başına Spot/Öncelikli örnekler | %60-80 | Yüksek | Yüksek (kesintiler) |
| Çalışma saatleri sonrasında kapatmayı planlayın (geliştirme/aşama) | %40-70 | Düşük | Boş (üretim dışı ortamlar) |
import boto3
from datetime import datetime
from dataclasses import dataclass
from typing import Sequence
@dataclass(frozen=True)
class ScalingPolicy:
"""Politica di scaling immutabile con carbon awareness."""
min_capacity: int
max_capacity: int
target_cpu_pct: float
scale_in_cooldown_sec: int
green_hour_min_capacity: int # capacità minima durante ore verdi
low_traffic_scale_in_factor: float # Fattore aggressivo in ore basse traffico
def create_carbon_aware_scaling_policies(
asg_name: str,
policy: ScalingPolicy,
region: str = "eu-west-1"
) -> dict:
"""
Configura Auto Scaling Group con politiche carbon-aware.
Scale-in più aggressivo durante ore di basso traffico (di notte)
dove l'energia potrebbe essere più verde E il traffico e basso.
"""
autoscaling = boto3.client("autoscaling", region_name=region)
# Policy principale: target tracking su CPU
main_policy = autoscaling.put_scaling_policy(
AutoScalingGroupName=asg_name,
PolicyName=f"{asg_name}-carbon-aware-cpu",
PolicyType="TargetTrackingScaling",
TargetTrackingConfiguration={
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageCPUUtilization"
},
"TargetValue": policy.target_cpu_pct,
"ScaleInCooldown": policy.scale_in_cooldown_sec,
"ScaleOutCooldown": 120,
"DisableScaleIn": False,
}
)
# Scheduled action: scale down notturno aggressivo
# Combina basso traffico + potenziale energia verde
autoscaling.put_scheduled_update_group_action(
AutoScalingGroupName=asg_name,
ScheduledActionName=f"{asg_name}-night-scaledown",
Recurrence="0 22 * * *", # Ogni giorno alle 22:00 UTC
MinSize=policy.green_hour_min_capacity,
MaxSize=policy.max_capacity,
DesiredCapacity=policy.green_hour_min_capacity,
)
# Scheduled action: scale up mattutino prima del traffico
autoscaling.put_scheduled_update_group_action(
AutoScalingGroupName=asg_name,
ScheduledActionName=f"{asg_name}-morning-scaleup",
Recurrence="0 7 * * MON-FRI", # Lun-Ven alle 07:00 UTC
MinSize=policy.min_capacity,
MaxSize=policy.max_capacity,
DesiredCapacity=policy.min_capacity + 2, # Pre-warm prima del traffico
)
return {
"asg_name": asg_name,
"main_policy_arn": main_policy["PolicyARN"],
"estimated_monthly_co2_reduction_pct": 35, # Tipico per questo pattern
}
# Lambda Right-sizing: trova la memoria ottimale
# Principio: RAM in eccesso = costo carbonio inutile
def optimize_lambda_memory(function_name: str, region: str = "eu-west-1") -> dict:
"""
Analizza l'utilizzo memoria di una Lambda e suggerisce il right-size.
AWS Lambda Power Tuning (tool open source) automatizza questo processo.
"""
lambda_client = boto3.client("lambda", region_name=region)
cloudwatch = boto3.client("cloudwatch", region_name=region)
# Recupera configurazione attuale
config = lambda_client.get_function_configuration(FunctionName=function_name)
current_memory_mb = config["MemorySize"]
# Recupera metriche CloudWatch: max memory used negli ultimi 7 giorni
# (in produzione usare AWS Lambda Power Tuning per analisi completa)
metrics = cloudwatch.get_metric_statistics(
Namespace="AWS/Lambda",
MetricName="MaxMemoryUsed",
Dimensions=[{"Name": "FunctionName", "Value": function_name}],
StartTime=datetime.utcnow().replace(hour=0, minute=0) - __import__("datetime").timedelta(days=7),
EndTime=datetime.utcnow(),
Period=86400,
Statistics=["Maximum"],
)
if not metrics["Datapoints"]:
return {"status": "insufficient_data"}
max_used_mb = max(dp["Maximum"] for dp in metrics["Datapoints"])
# Buffer di sicurezza: 30% sopra il massimo osservato
recommended_mb = min(int(max_used_mb * 1.3 / 64 + 1) * 64, 10240)
co2_reduction_pct = max(0, (current_memory_mb - recommended_mb) / current_memory_mb * 100)
return {
"current_memory_mb": current_memory_mb,
"max_observed_mb": int(max_used_mb),
"recommended_mb": recommended_mb,
"potential_co2_reduction_pct": round(co2_reduction_pct, 1),
"annual_cost_savings_usd": (current_memory_mb - recommended_mb) / 1024 * 0.0000166667 * 3_600_000 * 12,
}
Sürdürülebilir Veritabanı Modelleri: Daha Az Sorgu, Daha Az Karbon
Veritabanı genellikle aşağıdakileri içeren bileşendir: daha fazla enerji tüketimi bir sistemin kurumsal. Her sorgu, disk G/Ç'sini, RAM arabellek tahsisini, ayrıştırma için CPU döngülerini, planlama ve yürütme. Sorguları optimize etmek yalnızca performansla ilgili değildir: Emisyonlarda doğrudan azalma.
Model 1: Yeniden Hesaplamaları Azaltmak için Gerçekleştirilmiş Görünümler
Gerçekleştirilmiş görünümler, pahalı toplu sorguları ortadan kaldırmak için en etkili modeldir. sürekli olarak yeniden yürütülür. Her istekte karmaşık SUM, COUNT, JOIN'i yeniden hesaplamak yerine, sonuç önceden hesaplanır ve periyodik olarak veya tetikleyiciler aracılığıyla güncellenir.
-- PROBLEMA: Query aggregata pesante eseguita 1000x al giorno
-- Ogni esecuzione: 2-5 secondi, 100-500ms CPU, I/O intensivo
-- Stima: ~500mWh/giorno solo per questa query
-- Query pesante PRIMA (eseguita ad ogni richiesta)
SELECT
c.category_id,
c.name AS category_name,
COUNT(DISTINCT o.order_id) AS total_orders,
SUM(oi.quantity * oi.unit_price) AS total_revenue,
AVG(oi.quantity * oi.unit_price) AS avg_order_value,
COUNT(DISTINCT o.customer_id) AS unique_customers
FROM categories c
JOIN products p ON p.category_id = c.category_id
JOIN order_items oi ON oi.product_id = p.product_id
JOIN orders o ON o.order_id = oi.order_id
WHERE o.created_at >= NOW() - INTERVAL '30 days'
GROUP BY c.category_id, c.name;
-- SOLUZIONE: Materialized view con refresh in finestra verde
CREATE MATERIALIZED VIEW mv_category_metrics_30d AS
SELECT
c.category_id,
c.name AS category_name,
COUNT(DISTINCT o.order_id) AS total_orders,
SUM(oi.quantity * oi.unit_price) AS total_revenue,
AVG(oi.quantity * oi.unit_price) AS avg_order_value,
COUNT(DISTINCT o.customer_id) AS unique_customers,
NOW() AS last_refreshed
FROM categories c
JOIN products p ON p.category_id = c.category_id
JOIN order_items oi ON oi.product_id = p.product_id
JOIN orders o ON o.order_id = oi.order_id
WHERE o.created_at >= NOW() - INTERVAL '30 days'
GROUP BY c.category_id, c.name
WITH DATA;
-- Indice per query O(1)
CREATE UNIQUE INDEX idx_mv_category_metrics ON mv_category_metrics_30d (category_id);
-- Refresh schedulato: CONCURRENT permette letture durante il refresh
-- Schedulare nelle finestre verdi (es. con pg_cron + carbon intensity check)
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_category_metrics_30d;
-- Con pg_cron (schedulare refresh ogni ora nelle ore diurne con solare)
SELECT cron.schedule(
'refresh-category-metrics',
'0 * * * *', -- Ogni ora; logica carbon-aware nell'applicazione
'REFRESH MATERIALIZED VIEW CONCURRENTLY mv_category_metrics_30d'
);
-- Query dopo: O(1) sul materialized view
-- Stima risparmio: 99% della computazione originale
SELECT * FROM mv_category_metrics_30d ORDER BY total_revenue DESC;
-- Pattern 2: Read Replica per separare carichi
-- Letture analitiche (intensive) -> read replica
-- Scritture -> primary (minimo carico)
-- Pattern 3: Partial Indexes per ridurre I/O
-- INVECE DI: indice su tutti i 50M ordini
CREATE INDEX idx_orders_status_all ON orders(status, created_at);
-- MEGLIO: indice solo sui 500K ordini attivi (1% del totale)
-- 99% meno I/O, 99% meno spazio, maintenance molto più veloce
CREATE INDEX idx_orders_status_active ON orders(status, created_at)
WHERE status IN ('pending', 'processing', 'shipped');
-- Pattern 4: Connection Pooling per ridurre overhead
-- PgBouncer: max_client_conn=1000, pool_size=20
-- Riduce: TCP handshakes, SSL negotiation, process fork overhead
-- Stima risparmio: 30-50% CPU PostgreSQL su workload high-concurrency
Sorgu Optimizasyonu: N+1 Problem ve İstekli Yükleme
N+1 sorunu, emisyonlar açısından en yaygın ve maliyetli anti-örüntülerden biridir: gerekli tüm verileri alan tek bir sorgu yerine N+1 ayrı sorgu çalıştırırsınız. N=1000 sipariş ile 1 yerine 1001 sorgu oluşturulur ve karbon ayak izi 1000 ile çarpılır Operasyonun.
from sqlalchemy import select
from sqlalchemy.orm import selectinload, joinedload, Session
from typing import Sequence
# ANTI-PATTERN: N+1 queries - EVITARE
def get_orders_naive(session: Session, limit: int = 100) -> list:
"""
PROBLEMATICO: per 100 ordini genera 101 query.
100 ordini -> 1 query
100 customer -> 100 query separate
Stima: ~2mWh per 100 ordini. Su 10.000 richieste/giorno = 20Wh/giorno.
"""
orders = session.execute(select(Order).limit(limit)).scalars().all()
# Ogni accesso a order.customer triggersa una nuova query! (lazy loading)
return [{"id": o.id, "customer": o.customer.email} for o in orders]
# PATTERN GREEN: Eager loading con selectin
def get_orders_green(session: Session, limit: int = 100) -> list:
"""
OTTIMIZZATO: 2 query totali invece di N+1.
Query 1: tutti gli ordini
Query 2: tutti i customer in una sola query IN
Stima: 0.02mWh per 100 ordini. Risparmio: 99%.
"""
stmt = (
select(Order)
.options(selectinload(Order.customer)) # 2 query totali
.limit(limit)
)
orders = session.execute(stmt).scalars().all()
return [{"id": o.id, "customer": o.customer.email} for o in orders]
# Ancora meglio: joinedload per 1 sola query
def get_orders_single_query(session: Session, limit: int = 100) -> list:
"""
ULTRA-OTTIMIZZATO: 1 sola query con JOIN.
Ideale quando il numero di relazioni e basso.
Stima: 0.01mWh per 100 ordini. Risparmio: 99.5%.
"""
stmt = (
select(Order)
.options(joinedload(Order.customer)) # JOIN: 1 sola query
.limit(limit)
)
orders = session.execute(stmt).unique().scalars().all()
return [{"id": o.id, "customer": o.customer.email} for o in orders]
# Pattern: Projection - seleziona solo i campi necessari
def get_order_summary(session: Session, order_ids: list[int]) -> list[dict]:
"""
Seleziona SOLO i campi necessari, non SELECT *.
Su tabelle con 50+ colonne, SELECT * trasferisce 10-20x più dati.
"""
stmt = (
select(
Order.id,
Order.total_amount,
Order.status,
# Solo 3 campi invece di 50+
)
.where(Order.id.in_(order_ids))
)
rows = session.execute(stmt).all()
return [{"id": r.id, "total": float(r.total_amount), "status": r.status} for r in rows]
Ağ Verimliliği: Aktarılan Her Baytın Bir Karbon Maliyeti Vardır
Veri aktarmanın gerçek bir enerji maliyeti vardır: GB başına 0,06-0,1 kWh trafik için internet (omurga + son mil). Günde 10 TB sıkıştırılmamış veri aktaran uygulama yalnızca iletim için yaklaşık 600-1000 kWh tüketir, bu da günde 200-400 kg CO₂'ye eşdeğerdir (Ortalama Avrupa yoğunluğu 350 gCO₂/kWh).
HTTP/3 ve QUIC: Protokol Düzeyinde Verimlilik
QUIC'li HTTP/3, hat başı engellemeyi ortadan kaldırır ve bağlantılar kurun. Çok sayıda eş zamanlı isteğin bulunduğu uygulamalar için HTTP/3, gecikme süresi %15-30 ve dolayısıyla sunucuların aktif CPU zamanı.
import express, { Request, Response, NextFunction } from "express";
import compression from "compression";
import { createBrotliCompress, createGzip } from "zlib";
import { pipeline } from "stream/promises";
const app = express();
// Compressione intelligente: scegli il miglior algoritmo
// Brotli: migliore ratio (15-25% superiore a gzip), supportato da tutti i browser moderni
// Gzip: fallback per client vecchi
app.use(compression({
// Comprimi solo se il risparmio e significativo
threshold: 1024, // Min 1KB per comprimere
level: 6, // Bilanciamento CPU/ratio
filter: (req, res) => {
// Non comprimere immagini già compresse (jpeg, webp, png)
const contentType = res.getHeader("Content-Type") as string || "";
if (contentType.includes("image/")) return false;
return compression.filter(req, res);
},
}));
// Middleware: risposta minima con field selection
// Invece di returnare tutto l'oggetto, risponde solo con i campi richiesti
function fieldSelectionMiddleware(req: Request, res: Response, next: NextFunction): void {
const originalJson = res.json.bind(res);
res.json = (body: any) => {
const fields = req.query["fields"];
if (!fields || typeof fields !== "string" || !body) {
return originalJson(body);
}
const requestedFields = fields.split(",").map(f => f.trim());
// Filtra solo i campi richiesti (non muta body originale)
const filtered = Array.isArray(body)
? body.map(item => pickFields(item, requestedFields))
: pickFields(body, requestedFields);
return originalJson(filtered);
};
next();
}
function pickFields(obj: Record<string, unknown>, fields: string[]): Record<string, unknown> {
return fields.reduce<Record<string, unknown>>((acc, field) => {
if (field in obj) {
return { ...acc, [field]: obj[field] };
}
return acc;
}, {});
}
app.use(fieldSelectionMiddleware);
// Esempio: GET /api/users?fields=id,name,email
// Invece di returnare 50 campi, restituisce solo 3
// Riduzione payload tipica: 60-90%
// Cache-Control headers: riduce richieste ripetute
function addCacheHeaders(res: Response, maxAgeSeconds: number): void {
res.setHeader("Cache-Control", `public, max-age={maxAgeSeconds}, stale-while-revalidate=60`);
res.setHeader("Vary", "Accept-Encoding, Accept");
}
app.get("/api/products/:id", async (req: Request, res: Response) => {
const product = await getProduct(req.params["id"]);
// Dati statici: cache aggressiva
if (product?.isStatic) {
addCacheHeaders(res, 86400); // 1 giorno
} else {
addCacheHeaders(res, 300); // 5 minuti
}
res.json(product);
});
// ETag per validazione cache efficiente
// Invece di re-scaricare, il client verifica se il dato e cambiato
app.get("/api/catalog", async (req: Request, res: Response) => {
const catalog = await getCatalog();
const etag = require("crypto")
.createHash("md5")
.update(JSON.stringify(catalog))
.digest("hex");
// Se ETag non cambiato: risponde 304 (0 bytes di payload!)
if (req.headers["if-none-match"] === etag) {
res.status(304).end();
return;
}
res.setHeader("ETag", etag);
res.json(catalog);
});
async function getProduct(id: string): Promise<any> {
return { id, isStatic: true, name: "Product" };
}
async function getCatalog(): Promise<any[]> {
return [];
}
Sürdürülebilir Ön Uç Kalıpları: Müşteri Sorunun Bir Parçasıdır
Ön uç genellikle yazılım emisyon analizinde en çok gözden kaçırılan bileşendir. ancak milyonlarca cihazda yoğun JavaScript çalıştırmanın toplamda çok büyük bir etkisi vardır. Bir senaryo 1 milyon cihazda çalışan 500 KB JS'nin çalışması yaklaşık olarak sürer 50 GWh yıllık enerji kullanıcı cihazlarında — sunucularımızda değil.
Görüntü Optimizasyonu: En Hızlı Kazanç
Görüntüler ortalama olarak şunları temsil eder: Ağırlığın %50-70'i bir web sayfasının. Geçmek JPEG'den WebP/AVIF'ye veri aktarımında eşdeğer bir azalmayla boyutu %25-50 oranında azaltır ve kullanıcının cihazındaki kod çözme süresinde.
// angular.json - abilita image optimization built-in di Angular
// Angular 17+ ha NgOptimizedImage integrato
import { NgOptimizedImage } from '@angular/common';
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-product-card',
standalone: true,
imports: [NgOptimizedImage],
changeDetection: ChangeDetectionStrategy.OnPush, // Riduce change detection cycles
template: `
<!-- NgOptimizedImage: lazy loading + srcset automatico + WebP -->
<img
ngSrc="products/laptop-pro.jpg"
[width]="400"
[height]="300"
[priority]="isAboveFold"
loading="lazy"
decoding="async"
alt="Laptop Pro - Vista frontale"
/>
`
})
export class ProductCardComponent {
isAboveFold = false;
}
// build pipeline: conversione automatica a WebP/AVIF con sharp
// scripts/optimize-images.mjs
import sharp from 'sharp';
import { readdir, stat } from 'fs/promises';
import path from 'path';
async function optimizeImages(inputDir: string, outputDir: string): Promise<void> {
const files = await readdir(inputDir);
const imageFiles = files.filter(f => /\.(jpg|jpeg|png)$/i.test(f));
const results = await Promise.all(imageFiles.map(async (file) => {
const inputPath = path.join(inputDir, file);
const baseName = path.basename(file, path.extname(file));
// Genera WebP (supporto universale 2025)
const webpPath = path.join(outputDir, `{baseName}.webp`);
await sharp(inputPath)
.webp({ quality: 80, effort: 6 })
.toFile(webpPath);
// Genera AVIF (compressione superiore, browser moderni)
const avifPath = path.join(outputDir, `{baseName}.avif`);
await sharp(inputPath)
.avif({ quality: 65, effort: 7 })
.toFile(avifPath);
const [origSize, webpSize, avifSize] = await Promise.all([
stat(inputPath).then(s => s.size),
stat(webpPath).then(s => s.size),
stat(avifPath).then(s => s.size),
]);
return {
file,
origKB: (origSize / 1024).toFixed(1),
webpKB: (webpSize / 1024).toFixed(1),
avifKB: (avifSize / 1024).toFixed(1),
webpSaving: ((1 - webpSize/origSize) * 100).toFixed(1) + '%',
avifSaving: ((1 - avifSize/origSize) * 100).toFixed(1) + '%',
};
}));
console.table(results);
}
OLED'de Karanlık Mod ve Enerji Tasarrufu
Karanlık Mod: OLED Ekranlar Üzerinde Gerçek Etki
OLED ekranlarda (2024'ten itibaren tüm premium akıllı telefonlarda ve birçok dizüstü bilgisayarda mevcuttur), siyah pikseller resmen tüketiyorlar 0 enerji (OLED pikseli kapatıldığında ışık yaymaz). OLED'de saf siyah arka plana sahip (#000000) bir arayüz, %60-80 daha az enerji ekranın beyaz bir arka plana karşı (#FFFFFF) görünümü. Milyarlarca OLED cihazıyla iyi uygulanmış bir karanlık mod sunmak, önemli kullanıcı tarafı emisyonları.
/* Dark mode: usa true black (#000000) per massimizzare risparmio OLED */
/* Anche #0d0d0d e sufficiente e più gradevole esteticamente */
:root {
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
--text-primary: #1a1a1a;
--surface: #ffffff;
}
/* Auto dark mode: si attiva in base alla preferenza di sistema */
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #000000; /* True black per OLED */
--bg-secondary: #0d0d0d; /* Quasi-nero, più comfort visivo */
--text-primary: #e8e8e8;
--surface: #111111;
}
}
/* Classe manuale per toggle */
.dark-theme {
--bg-primary: #000000;
--bg-secondary: #0d0d0d;
--text-primary: #e8e8e8;
--surface: #111111;
}
/* Ridurre motion: riduce animazioni = meno GPU = meno energia */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Font system: evita Google Fonts quando possibile */
/* Font di sistema = 0 download, rendering istantaneo */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
}
/* Se font custom e necessario: usa font-display: swap per evitare blocking */
@font-face {
font-family: "CustomFont";
src: url("/fonts/custom.woff2") format("woff2");
font-display: swap; /* Non blocca rendering mentre scarica */
font-weight: 400 700; /* Variable font: 1 file per tutti i pesi */
}
API Sürdürülebilir Tasarım: Daha Az Gidiş-Dönüş, Daha Az Karbon
API tasarımının emisyonlar üzerinde doğrudan etkisi vardır: kötü tasarlanmış API'ler gerektirir birden çok gidiş-dönüş, gereğinden fazla veri aktarımı ve istemcileri bunu yapmaya zorlama İhtiyaç duydukları bilgileri almak için daha fazla istek.
Verimli Sayfalandırma: İmleç ve Ofset
OFFSET ile sayfalandırma enerji açısından pahalıdır: 20 öğeli 100. sayfayı almak, veritabanı 2000 satırı yinelemeli ve atmalıdır (OFFSET 2000). İmleç tabanlı sayfalandırmayla veritabanı Bir indeks kullanarak doğrudan doğru noktaya atlar ve yalnızca sayıyla orantılı enerji tüketir atlananlar değil, geri dönen satırların sayısı.
import { Pool } from "pg";
interface PaginationResult<T> {
readonly data: readonly T[];
readonly nextCursor: string | null;
readonly totalCount?: number;
}
// ANTI-PATTERN: Offset pagination
// Costo query: O(offset + limit) - cresce con la profondità di paginazione
async function getProductsOffset(
db: Pool,
page: number,
pageSize: number = 20
): Promise<PaginationResult<any>> {
const offset = page * pageSize;
// COSTOSO: il DB scorre 'offset' righe per poi scartarle
const result = await db.query(
"SELECT * FROM products ORDER BY id LIMIT $1 OFFSET $2",
[pageSize, offset]
);
// Esempio pagina 1000 con 20 items: DB scorre 20.020 righe
// Stima: 50x più lenta e 50x più dispendiosa di cursor pagination
return { data: result.rows, nextCursor: null };
}
// PATTERN GREEN: Cursor-based pagination
// Costo query: O(limit) - costante, usa indice
async function getProductsCursor(
db: Pool,
cursor: string | null,
pageSize: number = 20
): Promise<PaginationResult<any>> {
let query: string;
let params: any[];
if (cursor) {
// Decode cursor: contiene l'ID dell'ultimo elemento visto
const lastId = parseInt(Buffer.from(cursor, "base64").toString("utf-8"));
query = `
SELECT id, name, price, category_id
FROM products
WHERE id > $1
ORDER BY id ASC
LIMIT $2
`;
params = [lastId, pageSize + 1]; // +1 per sapere se c'è una pagina successiva
} else {
query = `
SELECT id, name, price, category_id
FROM products
ORDER BY id ASC
LIMIT $1
`;
params = [pageSize + 1];
}
const result = await db.query(query, params);
const rows = result.rows;
const hasMore = rows.length > pageSize;
const data = hasMore ? rows.slice(0, pageSize) : rows;
const lastItem = data[data.length - 1];
// Encode next cursor
const nextCursor = hasMore && lastItem
? Buffer.from(String(lastItem.id)).toString("base64")
: null;
return {
data,
nextCursor,
// Non calcola totalCount (costoso): usa solo se necessario con estimate
};
}
// GraphQL vs REST: quando ognuno e più efficiente
// REST: efficiente per risorse semplici e ben definite, cache HTTP nativa
// GraphQL: efficiente per UI complesse con molti componenti che richiedono dati diversi
// Pattern GraphQL carbon-aware: DataLoader per batch N+1
import DataLoader from "dataloader";
import { GraphQLResolveInfo } from "graphql";
// Senza DataLoader: 1000 resolver -> 1000 query SQL separate
// Con DataLoader: 1000 resolver -> 1 query SQL con WHERE IN
const productLoader = new DataLoader<number, any>(
async (productIds: readonly number[]) => {
const result = await db.query(
"SELECT * FROM products WHERE id = ANY($1)",
[[...productIds]]
);
// Mappa per mantenere l'ordine corretto
const productMap = new Map(result.rows.map(p => [p.id, p]));
return productIds.map(id => productMap.get(id) || null);
},
{ batch: true, maxBatchSize: 100 } // Max 100 per query per sicurezza
);
declare const db: Pool;
Karbon İzleme: İyileştirmeye Yönelik Önlem
"Ölçülemeyen şey iyileştirilemez." düzeyinde karbon izleme hizmet, en fazla emisyona sahip bileşenleri tanımlamanıza, zaman içindeki ilerlemeyi izlemenize, ve hesaplayın SCI puanı (Yazılım Karbon Yoğunluğu) şirket KPI'sı olarak.
from prometheus_client import Counter, Histogram, Gauge, start_http_server
from functools import wraps
import time
import httpx
import logging
from typing import Callable, Any
logger = logging.getLogger(__name__)
# Metriche Prometheus custom per carbon monitoring
CARBON_INTENSITY_GAUGE = Gauge(
"carbon_intensity_g_co2_per_kwh",
"Current grid carbon intensity in gCO2/kWh",
["region"]
)
ENERGY_CONSUMED_COUNTER = Counter(
"energy_consumed_wh_total",
"Total energy consumed in Wh",
["service", "endpoint", "method"]
)
CARBON_EMITTED_COUNTER = Counter(
"carbon_emitted_gco2_total",
"Total carbon emitted in gCO2",
["service", "endpoint", "method"]
)
REQUEST_DURATION_HISTOGRAM = Histogram(
"http_request_duration_seconds",
"HTTP request duration",
["service", "endpoint", "method"],
buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5]
)
SCI_SCORE_GAUGE = Gauge(
"sci_score_mgco2_per_request",
"Software Carbon Intensity score in mgCO2 per functional unit",
["service"]
)
# Stima consumo energetico per tipo di risorsa
# Basato su benchmark empirici per server tipici (TDP ~200W, utilization 20%)
ENERGY_ESTIMATES_WH = {
"cpu_second": 0.011, # ~11 mWh per secondo di CPU @ 200W TDP, 20% util
"memory_gb_second": 0.000375, # ~0.375 mWh per GB*s RAM
"ssd_read_gb": 0.0002, # ~0.2 mWh per GB letto da SSD
"ssd_write_gb": 0.0004, # ~0.4 mWh per GB scritto su SSD
"network_gb": 0.1, # ~100 mWh per GB trasferito (rete)
}
class CarbonMetricsCollector:
"""Raccoglie metriche carbon per Prometheus + Grafana."""
def __init__(self, service_name: str, region: str = "IT"):
self._service = service_name
self._region = region
self._current_intensity = 350.0 # Default gCO2/kWh
async def update_carbon_intensity(self) -> None:
"""Aggiorna l'intensità carbonica dalla grid in tempo reale."""
try:
async with httpx.AsyncClient(timeout=5) as client:
resp = await client.get(
"https://api.electricitymap.org/v3/carbon-intensity/latest",
params={"zone": self._region},
headers={"auth-token": "YOUR_TOKEN"}
)
data = resp.json()
self._current_intensity = data["carbonIntensity"]
CARBON_INTENSITY_GAUGE.labels(region=self._region).set(self._current_intensity)
except Exception as e:
logger.warning("Carbon intensity fetch failed: %s", e)
def track_request(self, endpoint: str, method: str):
"""Decorator per tracciare carbon footprint di ogni endpoint."""
def decorator(func: Callable) -> Callable:
@wraps(func)
async def wrapper(*args: Any, **kwargs: Any) -> Any:
start = time.perf_counter()
result = await func(*args, **kwargs)
duration_s = time.perf_counter() - start
# Stima energia: basata sulla durata (proxy per CPU+I/O)
estimated_energy_wh = duration_s * ENERGY_ESTIMATES_WH["cpu_second"]
# Carbon emessa
carbon_g = estimated_energy_wh * self._current_intensity / 1000
# Aggiorna metriche Prometheus
labels = {"service": self._service, "endpoint": endpoint, "method": method}
ENERGY_CONSUMED_COUNTER.labels(**labels).inc(estimated_energy_wh * 1000) # in mWh
CARBON_EMITTED_COUNTER.labels(**labels).inc(carbon_g * 1000) # in mgCO2
REQUEST_DURATION_HISTOGRAM.labels(**labels).observe(duration_s)
return result
return wrapper
return decorator
def update_sci_score(self, total_carbon_mgco2: float, functional_units: int) -> None:
"""
Aggiorna il SCI score: mgCO2 per functional unit (es. per request).
Formula SCI: (E * I + M) / R
E = energia, I = intensità, M = embodied carbon, R = functional units
"""
if functional_units > 0:
sci = total_carbon_mgco2 / functional_units
SCI_SCORE_GAUGE.labels(service=self._service).set(sci)
# Grafana dashboard query examples (PromQL):
#
# Carbon footprint per endpoint (top 10 più emissivi):
# topk(10, sum by (endpoint) (rate(carbon_emitted_gco2_total[5m])))
#
# SCI score nel tempo:
# sci_score_mgco2_per_request{service="api-gateway"}
#
# Risparmio carbon da cache hits:
# rate(cache_hits_total[5m]) * 0.005 # 5mg CO2 per hit risparmiato
#
# Carbon intensity corrente vs soglia verde:
# carbon_intensity_g_co2_per_kwh > 300 # Alert se supera soglia
Örnek Olay İncelemesi: Günde 1 Milyon Ziyaret ile E-Ticaret — -%45 Karbon Ayak İzi
Tüm bu kalıpların somut bir durumda nasıl bütünleştiğini görelim. 1 milyonluk e-ticaret günlük ziyaretler, günde 50.000 sipariş ve optimize edilmemiş eski bir mimari. Takım var bu makalede anlatılan kalıpları 6 aylık bir projede uyguladık. Sonuçlar konuşuyor yalnız.
Optimizasyondan Önce Mimari Temel Çizgi
| Bileşen | Önce Yapılandırma | Sorun | Karbon/ay (tahmini) |
|---|---|---|---|
| Web sunucuları | 20x c5.2xlarge her zaman açık | Ortalama CPU %8, %92 boşta | 450 kg CO₂ |
| Veritabanları | db.r5.4xlarge, SELECT * her yerde | N+1 sorgu, dizin yok | 280 kg CO₂ |
| S3 Depolama | Hepsi Standart katmanda | Sıcak depolamada 5 yıllık günlüklere asla erişilmiyor | 90 kg CO₂ |
| Toplu işler | 02:00 UTC'de düzeltildi | Karbon farkındalığı yok | 120 kg CO₂ |
| CDN/Önbellek | Önbellek isabet oranı %35 | TTL'ler çok kısa, kenar önbelleğe alma yok | 180 kg CO₂ |
| Başlangıç aşaması | Paket JS 2,8MB, JPEG | Kod bölme yok, optimize edilmemiş resimler | 200 kg CO₂ |
| Toplam | 1.320 kg CO₂/ay |
Optimizasyon Sonrası Sonuçlar (6 Ay)
| Araya girmek | Desen Uygulandı | Karbon azaltma | Uygulama Süresi |
|---|---|---|---|
| Otomatik ölçeklendirme + doğru boyutlandırma EC2 | Agresif, spot örnek kümesinin ölçeğini küçültün | -195 kg CO₂/ay (%43) | 2 hafta |
| Sorgu optimizasyonu + gerçekleştirilmiş görünümler | N+1 düzeltme, projeksiyon, kısmi indeksler | -140 kg CO₂/ay (%50) | 4 hafta |
| S3 yaşam döngüsü politikaları | Katmanlı depolama: sıcak/sıcak/soğuk/arşiv | -72 kg CO₂/ay (%80) | 1 hafta |
| Karbona duyarlı toplu planlama | Carbon Aware SDK, yeşil pencereler | -48 kg CO₂/ay (%40) | 3 hafta |
| Çok düzeyli önbellek (Redis + CDN) | Önbellek isabet oranı: %35 -> %87 | -126 kg CO₂/ay (%70) | 6 hafta |
| Ön uç: WebP/AVIF + kod bölme | Paket 2,8MB -> 380KB, WebP görselleri | -110 kg CO₂/ay (%55) | 3 hafta |
| Toplam kaydedilen | -691 kg CO₂/ay (-52%) | toplam 6 ay |
Nihai sonuç, bir azalmadır Aylık karbon ayak izinin %52'si (1.320 kg'dan 629 kg CO₂'ye kadar), yıllık yaklaşık tasarrufla 8,3 ton CO₂ eşdeğer. Bu, bir yıl boyunca yaklaşık 4 arabanın yollardan çekilmesine eşdeğerdir. Ve bu değil Uygulamayı sıfırdan yeniden yazmak gerekiyordu: tüm müdahaleler artımlıydı.
Vaka Çalışmasından Önemli Ders
En iyi yatırım getirisine (karbon tasarrufu / çaba) sahip iki müdahale şunlardı:
- S3 yaşam döngüsü politikaları (%80 azalma, 1 haftalık çalışma): birkaçı yeterli Kod Olarak Altyapı yapılandırma satırları. Etki/çaba oranına sahip modeldir şimdiye kadarki en yüksek.
- Otomatik ölçeklendirme ve doğru boyutlandırma (%43 azalma, 2 hafta): ezici olanı Çoğu sistem aşırı provizyona sahiptir. Provizyonu gereken minimum düzeye indirin Otomatik ölçeklendirmenin anında ve ölçülebilir bir etkisi vardır.
Ahlaki: Her zaman en basit kalıplarla başlayın. Kazancın yüzde 80'i yüzde 20'den geliyor müdahaleler.
Kaçınılması Gereken Anti-Desen: En Yaygın 5 Tuzak
Anti-Desen #1: Agresif Çalışma Dışı Önbellek Isınma Yeşili
Karbon yoğunluğunun zirve yaptığı zamanlarda (genellikle akşam, akşam) önbelleği büyük oranda önceden yükleyin. güneş enerjisi olmadığında ve gaz talebi karşıladığında) olduğundan daha fazla enerji tüketir birazını sakla. Önbellek ısıtmanın Carbon Aware SDK tarafından tanımlanan yeşil pencerelerde planlanması gerekir.
Anti-Model #2: Sıcak Depolamada Ayrıntılı Günlükler
S3 Standard'ta (sıcak katman) yıllarca hata ayıklama günlükleri tutmak para ve enerji israfıdır. 30 günden eski günlüklere nadiren erişilir; 90 gün sonra neredeyse hiç. Uygula Yaşam döngüsü politikaları herhangi bir eski sistemde gerçekleştirilecek ilk eylemdir.
Anti-Model #3: Aralıklı İş Yükleri için Her Zaman Açık
Günde 10 istek alan bir hizmetin, özel bir bulut sunucusunda 7/24 çalıştırılmasına gerek yoktur. Sıfıra ölçeklendirmeli Lambda, Cloud Run veya Fargate, aralıklı iş yükleri için yeşil seçimdir. Her zaman açık olan bir EC2 t3.sma örneği ayda ~15 kg CO₂ yayar; Günde 10 istek için Lambda ayda <0,001 kg CO₂ yayar. 15.000 kat daha az.
Anti-Desen #4: Üretimde SELECT *
SELECT * uygulama bunları kullanıyor olsa bile bir tablonun tüm alanlarını aktarır
sadece 2-3. 50'den fazla sütun ve milyonlarca satır içeren tablolarda bu, verileri 10-20 ile çarpar
eşdeğer enerji etkisiyle veri tabanından aktarılır. Her zaman açık yansıtmayı kullanın.
Anti-Model #5: Monolitik JavaScript Paketi
Günde 1 milyon kullanıcı tarafından indirilen ve görüntülenen 3 MB'lık bir JS paketi yaklaşık olarak Kullanıcı cihazlarında 2.000 kWh CPU enerjisi. Kod bölme ve tembel yükleme ile İlk paket 100-200 KB'a düşebilir, bu da güç tüketimini 15-30 kat azaltır müşteri tarafı. Kullanıcı cihazlarında tüketilen enerji Kapsam 3'ün bir parçasıdır yazılımın.
Kontrol Listesi: Bir Sonraki Sprintiniz için Sürdürülebilir Modeller
Yüksek Öncelik (Maksimum Etki, Düşük Karmaşıklık)
- Verileri otomatik olarak soğuk katmanlara taşımak için S3/GCS yaşam döngüsü politikalarını uygulayın
- EC2/GCE bulut sunucularının tedarikini analiz edin ve azaltın (AWS Compute Optimizer, GCP Recommender)
- İş saatleri dışındaki geliştirme/hazırlama ortamları için kapatma planı ekleyin
- Tüm HTTP uç noktalarında gzip/brotli sıkıştırmasını etkinleştirin (önceden etkinleştirilmemişse)
- Derleme hattında görüntüleri WebP/AVIF'e dönüştürün
- Tümünü kaldır
SELECT *bunları açık projeksiyonla değiştirmek
Orta Öncelik (Yüksek Etki, Orta Düzeyde Karmaşıklık)
- En çok erişilen kaynaklar için çok düzeyli önbelleğe alma (L1 işlem içi + L2 Redis) uygulayın
- Sık yürütülen toplu sorgular için somutlaştırılmış görünümler oluşturun
- Zaman açısından esnek toplu işleri Carbon Aware SDK zamanlamasına taşıyın
- İstekli yükleme veya DataLoader (GraphQL) ile N+1 sorunu çözün
- Ofset sayfalamanın yerine imleç tabanlı sayfalandırmayı uygular
- API yanıtlarına uygun ETag'leri ve Önbellek Kontrollerini ekleyin
Düşük Öncelikli (Artımlı İyileştirmeler)
- Gecikme hassasiyeti yüksek uygulamalar için HTTP/3'e geçmeyi düşünün
- REST uç noktalarında alan seçimini uygulayın (sorgu parametresi
?fields=) - Kullanıcıların OLED cihazlarındaki güç tüketimini azaltmak için gerçek siyahla karanlık modu ekleyin
- SCI puanı için Prometheus + Grafana kontrol paneliyle karbon izlemeyi yapılandırın
- Küçük veri alt kümelerinde filtrelenmiş sorgular için kısmi dizinler ekleyin
- Saatte 100'den az istek içeren iş yükleri için sunucusuz (Lambda, Cloud Functions) sistemleri değerlendirin
Sonuçlar: Standart Uygulama Olarak Sürdürülebilir Mimarlık
Sürdürülebilir mimari desenler bütçeli büyük teknoloji şirketlerine ayrılmış bir lüks değil sürdürülebilirlik için: bunlar aynı anda gelişen iyi mühendislik uygulamalarıdır performans, maliyetler ve çevresel etki. Katmanlı depolama, verimli önbelleğe alma, toplu Karbon farkındalığına sahip planlama, doğru boyutlandırma ve sorgu optimizasyonu daha verimli sistemler üretir her boyutun altında.
Vaka çalışması, eski bir sistemin karbon ayak izini azaltmanın mümkün olduğunu gösteriyor Mimariyi sıfırdan yeniden yazmak zorunda kalmadan, artımlı müdahalelerle 6 ayda %50+. Anahtar etkisi yüksek ve karmaşıklığı düşük müdahalelerle başlamaktır (yaşam döngüsü politikaları, doğru boyutlandırma, sorgu optimizasyonu), sonuçları SCI metrikleriyle ölçün ve ardından şu adıma geçin: Daha karmaşık optimizasyonlar.
La CSRD Direktifi (Kurumsal Sürdürülebilirlik Raporlaması Direktifi) 2025'ten itibaren büyük Avrupalı şirketler ve 2026'dan itibaren orta ölçekli şirketler için çok sayıda kuruluşlar Kapsam 3 emisyonlarının bir parçası olarak yazılım emisyonlarını raporlayacak Sürdürülebilir mimari artık sadece etik bir seçim değil: bir gereklilik haline geliyor düzenleyici ve rekabetçi.
Serideki Sonraki Makale
Onuncu ve son makale Yeşil Yazılım serisini tamamlayacak. Yazılım Ekipleri için ESG, CSRD ve Uyumluluk: raporlama nasıl yapılandırılır zorunlu yazılım sürümleri, düzenleyiciler için denetim izleri oluşturmak ve Kurumsal yönetim süreçlerinde SCI metrikleri. Ayrıca pratik araçları da ele alacağız raporlama için: Sera Gazı Protokolü, ESRS E1 ve GSF Etki çerçevesi.
Ek Kaynaklar
- Karbon Bilinçli SDK (Yeşil Yazılım Vakfı): github.com/Green-Software-Foundation/karbon-aware-sdk
- Elektrik Haritaları API'sı: 50'den fazla bölge için gerçek zamanlı karbon yoğunluğu verileri
- AWS Bilgi İşlem Optimize Edici: Otomatik doğru boyutlandırma önerileri
- Etki Çerçevesi (GSF): Bileşene göre SCI hesaplaması
- Web Almanağı 2024 (HTTP Arşivi): Web performansı ve boyutuna ilişkin gerçek istatistikler
- Bulut Karbon Ayak İzi: Çoklu sağlayıcı bulut emisyonlarını ölçmek için açık kaynaklı araç







