AWS Lambda Memory Allocation ve Performance Tuning: Kapsamlı Rehber
AWS Lambda performance tuning: memory-CPU modeli, Power Tuning ile benchmarking, maliyet analizi ve adaptive allocation pattern'ları.
Serinin ilk bölümünde cold start’ları optimize ettikten sonra, sıradaki zorluk Lambda function’larınızın warm olduklarında verimli çalışmasını sağlamak. Memory allocation, etki alanı en geniş konfigürasyon ayarı; tek bir değer aynı anda CPU payını, execution süresini ve faturayı belirliyor.
Pratik varsayılan: 1024 MB ile başlayın, sonra yalnızca ölçülmüş bir eğrinin gösterdiği yöne doğru hareket edin. 128 MB varsayılanı hesaplama yapan hiçbir function için doğru değil; doğrudan 3008 MB’ye sıçrama refleksi ise çoğunlukla tek thread’li kodun harcayamayacağı bir kapasite satın alır.
Memory boyutu yalnızca bir girdi. Taşımaya değer model şu: ayrılan memory, onunla birlikte gelen CPU ve ikisinden doğan GB-saniye faturası.
Lambda’nın Memory-CPU Modeli#
CPU’nun Memory ile Ölçeklenmesi#
Lambda tek bir kaynak ayarı sunuyor. Memory değeri aynı zamanda function’ın ne kadar CPU alacağını belirliyor:
| Memory | Yaklaşık vCPU | Ne kazandırır |
|---|---|---|
| 128 MB | ~0,07 | En yavaş execution; CPU-bound işler sürünür |
| 512 MB | ~0,29 | Hafif API handler’ları için yaygın baseline |
| 1024 MB | ~0,58 | Çoğu workload için dengeli başlangıç |
| 1769 MB | 1,00 | Tam bir vCPU |
| 3008 MB | ~1,70 | Birden fazla core; yalnızca paralel kod faydalanır |
| 10240 MB | ~5,79 | Maksimum allocation |
CPU, memory ile baştan sona orantılı ölçekleniyor; vCPU sütunu memory değerinin 1769’a bölünmüş hali. Bu çizginin ötesinde birden fazla core’a sahip olursunuz ama tek thread’li Node.js kodu hızlanmayı bırakır, milisaniye başına ücret artmaya devam eder.
Memory-Süre Eğrisi#
Her function’ın kendi eğrisi var; harekete geçmeye değer tek sayı, kendi ölçtüğünüz sayıdır. Lambda Power Tuning (yeni sekmede açılır), aynı payload’ı birkaç memory ayarında çağırıp her biri için süre ve maliyet raporlayan bir Step Functions state machine’i:
{
"lambdaARN": "arn:aws:lambda:us-east-1:123456789012:function:image-resize",
"powerValues": [128, 512, 1024, 1769, 3008],
"num": 50,
"payload": { "key": "sample.jpg" },
"parallelInvocation": true,
"strategy": "balanced"
}
Sık karşılaşılan iki şekil var. CPU-bound işler (image resize, sıkıştırma, hashing) bir vCPU’ya yaklaşana kadar dik biçimde düşer ve bu aralıkta invocation başına maliyet neredeyse sabit kalır: kısalan süre, artan milisaniye ücretini dengeler. I/O-bound işler çok daha erken düzleşir; dirseğin ötesinde aynı network çağrısını beklemek için milisaniye başına daha fazla ödersiniz.
1024 MB’yi başlangıç noktası yapan şey bu. Genelde maliyet eğrisinin düz kısmında durur ve CPU-bound koda bir core’un büyük bölümünü verir; oradan hangi yöne gideceğinizi ölçtüğünüz eğri söyler.
Benchmarking Framework’ü Kurmak#
Kapsamlı Performance Test Kurulumu#
Power Tuning konfigürasyonlar arasındaki eğriyi verir. Function içi ölçüm ise tek bir invocation’da zamanın nereye gittiğini gösterir:
// comprehensive-benchmark.ts
import { performance, PerformanceObserver } from 'perf_hooks';
interface BenchmarkResult {
memoryUsed: number;
executionTime: number;
coldStart: boolean;
gcEvents: number;
cpuIntensive: boolean;
}
// Module scope, aynı ortamdaki invocation'lar arasında yaşar
let isWarm = false;
export class LambdaBenchmark {
private results: BenchmarkResult[] = [];
private coldStart = !isWarm;
private gcCount = 0;
constructor() {
isWarm = true;
this.monitorGC();
}
private monitorGC() {
// PerformanceObserver, engine'in yaptığı her collection'ı raporlar.
// global.gc'yi patch'lemek yalnızca kendi tetiklediğiniz çağrıları sayar
// ve global.gc --expose-gc ister; Lambda bu flag'i açmaz.
const observer = new PerformanceObserver((list) => {
this.gcCount += list.getEntries().length;
});
observer.observe({ entryTypes: ['gc'] });
}
async benchmark<T>(
operation: () => Promise<T>,
label: string
): Promise<{ result: T; metrics: BenchmarkResult }> {
this.gcCount = 0;
const startMemory = process.memoryUsage();
const startTime = performance.now();
const result = await operation();
const endTime = performance.now();
const endMemory = process.memoryUsage();
const metrics: BenchmarkResult = {
memoryUsed: endMemory.heapUsed - startMemory.heapUsed,
executionTime: endTime - startTime,
coldStart: this.coldStart,
gcEvents: this.getGCEvents(),
cpuIntensive: this.detectCPUIntensiveOperation(endTime - startTime)
};
console.log(`Benchmark [${label}]:`, metrics);
this.results.push(metrics);
return { result, metrics };
}
private detectCPUIntensiveOperation(duration: number): boolean {
// >100ms süren operasyonlar muhtemelen CPU-bound
return duration > 100;
}
private getGCEvents(): number {
return this.gcCount;
}
}
// Lambda'nızda kullanım
export const handler = async (event: any) => {
const benchmark = new LambdaBenchmark();
const { result } = await benchmark.benchmark(async () => {
return await processLargeDataset(event.data);
}, 'data-processing');
return result;
};
Production Benchmarking Stratejisi#
Aynı taramayı state machine olmadan yapmak isterseniz, tek bir function’ı memory ayarları arasında gezdirin ve billed duration ile max memory used değerlerini taşıyan REPORT satırını okuyun:
for memory in 512 1024 1536 1769 3008; do
aws lambda update-function-configuration \
--function-name image-resize \
--memory-size "${memory}" > /dev/null
# Konfigürasyon güncellemesi asenkron; erken çağırırsanız eski değere denk gelirsiniz
aws lambda wait function-updated-v2 --function-name image-resize
echo "== ${memory} MB =="
aws lambda invoke \
--function-name image-resize \
--cli-binary-format raw-in-base64-out \
--payload file://test-payload.json \
--log-type Tail \
--query 'LogResult' --output text \
"response-${memory}.json" | base64 --decode | grep REPORT
done
Ayar başına tek invocation gürültü ölçer. Sıralamaya güvenmeden önce döngüyü tekrarlayıp medyan alın.
Memory Optimizasyon Stratejileri#
Strateji 1: Workload Tipine Göre Right-Sizing#
Farklı workload’ların farklı optimal memory allocation’ları var:
// Workload tipine göre memory allocation
const workloadOptimization = {
// API Gateway proxy function'ları
simpleAPI: {
memoryMB: 512,
reason: "Düşük CPU, hızlı response time önceliği"
},
// Database operasyonları
databaseIntensive: {
memoryMB: 1024,
reason: "Query processing + connection overhead için balanced CPU"
},
// Image/file processing
fileProcessing: {
memoryMB: 1769,
reason: "CPU-intensive, tam vCPU'dan faydalanır"
},
// ML inference
machineLearning: {
memoryMB: 3008,
reason: "Model için memory + inference için multi-core"
},
// Data transformation
dataETL: {
memoryMB: 1769,
reason: "CPU-bound operasyonlar, optimal maliyet/performans"
}
};
Strateji 2: Memory Leak Önleme#
Arka planda dönen bir watchdog burada yanlış çözüm. Lambda, invocation’lar arasında execution environment’ı dondurur; bu yüzden setInterval yalnızca bir istek işlenirken tıklar ve tıkladığı an hangi invocation çalışıyorsa ona denk gelir. Bunun yerine handler içinde bilinen noktalarda örnekleme yapın:
// Invocation içinde memory basıncı örneklemesi
export class MemoryManager {
private readonly allocatedBytes =
parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE || '512', 10) * 1024 * 1024;
private readonly threshold = 0.8; // Ayrılan memory'nin %80'i
check(label: string): void {
const usage = process.memoryUsage();
// Lambda tüm sandbox üzerinden faturalandırır ve öldürür; REPORT
// satırındaki "Max Memory Used" ile eşleşen değer heapUsed değil rss
const ratio = usage.rss / this.allocatedBytes;
if (ratio > this.threshold) {
console.warn('Yüksek memory kullanımı tespit edildi:', {
label,
rss: Math.round(usage.rss / 1024 / 1024) + 'MB',
heapUsed: Math.round(usage.heapUsed / 1024 / 1024) + 'MB',
external: Math.round(usage.external / 1024 / 1024) + 'MB',
usage: Math.round(ratio * 100) + '%'
});
}
}
}
// Kullanım pattern'i
const memoryManager = new MemoryManager();
export const handler = async (event: any) => {
memoryManager.check('invocation-start');
const result = await processEvent(event);
memoryManager.check('invocation-end');
return result;
};
Leak kendini, aynı ortamdaki ardışık isteklerde invocation-start değerinin tırmanmasıyla belli eder. Tek seferlik pahalı bir istek ise yalnızca invocation-end içinde görünür.
Strateji 3: Garbage Collection Optimizasyonu#
NODE_OPTIONS V8 flag’lerinin yalnızca küçük bir alt kümesini kabul eder. --expose-gc, --gc-interval ve --optimize-for-size reddedilir; runtime bunları görmezden gelmek yerine başlamayı reddeder. Yani if (global.gc) ile korunan her satır, managed runtime’da ölü koddur:
// Lambda environment variable olarak ayarlanır
const gcOptimizations = {
NODE_OPTIONS: [
'--max-old-space-size=1024', // heap'i ayarlanan memory'nin altında tut
'--max-semi-space-size=32' // daha büyük young generation, daha az scavenge
].join(' ')
};
// Manuel collection aramak yerine çalışma setini sınırla
const processLargeDataset = async (data: any[]) => {
const results = [];
for (const chunk of chunkArray(data, 1000)) {
results.push(await processChunk(chunk));
// Yukarıdaki hiçbir satır bir önceki chunk'ın ara sonuçlarına
// referans tutmaz; collector bunları kendiliğinden geri alır
}
return results;
};
Chunking transform adımını sınırlar, girdiyi değil. Sandbox’ı dolduran şey veri setinin kendisiyse, S3’ten indirip dilimlemek yerine stream edin.
Maliyet Analizi Framework’ü#
Memory Allocation’ın Gerçek Maliyeti#
Tüm değişkenleri hesaba katan kapsamlı bir maliyet analizi kurun:
// cost-calculator.ts
interface LambdaCostParams {
memoryMB: number;
avgExecutionMs: number;
invocationsPerMonth: number;
region: 'us-east-1' | 'us-west-2' | 'eu-west-1';
}
interface CostBreakdown {
computeCost: number;
requestCost: number;
totalMonthlyCost: number;
costPerInvocation: number;
performanceRating: number;
}
export class LambdaCostCalculator {
// x86 on-demand ücretleri; bu üç region'da aynı değerler geçerli.
// arm64 GB-saniye başına daha ucuz, yani mimari de bir kaldıraç.
private pricing: Record<LambdaCostParams['region'], {
computePerGBSecond: number;
requestPer1M: number;
}> = {
'us-east-1': { computePerGBSecond: 0.0000166667, requestPer1M: 0.20 },
'us-west-2': { computePerGBSecond: 0.0000166667, requestPer1M: 0.20 },
'eu-west-1': { computePerGBSecond: 0.0000166667, requestPer1M: 0.20 }
};
calculateCost(params: LambdaCostParams): CostBreakdown {
const { memoryMB, avgExecutionMs, invocationsPerMonth, region } = params;
const pricing = this.pricing[region];
// Memory'yi GB'ye ve execution time'ı saniyeye çevir
const memoryGB = memoryMB / 1024;
const executionSeconds = avgExecutionMs / 1000;
// Compute cost hesapla
const gbSeconds = memoryGB * executionSeconds * invocationsPerMonth;
const computeCost = gbSeconds * pricing.computePerGBSecond;
// Request cost hesapla
const requestCost = (invocationsPerMonth / 1000000) * pricing.requestPer1M;
const totalMonthlyCost = computeCost + requestCost;
const costPerInvocation = totalMonthlyCost / invocationsPerMonth;
// Performance rating (düşük execution time = yüksek rating)
const performanceRating = Math.max(1, 10 - (avgExecutionMs / 100));
return {
computeCost,
requestCost,
totalMonthlyCost,
costPerInvocation,
performanceRating
};
}
findOptimalMemory(
baseParams: Omit<LambdaCostParams, 'memoryMB'>,
performanceProfile: { memory: number; executionMs: number }[]
): { memory: number; cost: number; savings: number } {
const scenarios = performanceProfile.map(profile => ({
...profile,
cost: this.calculateCost({
...baseParams,
memoryMB: profile.memory,
avgExecutionMs: profile.executionMs
})
}));
// En iyi cost-performance oranına sahip konfigürasyonu bul
const optimal = scenarios.reduce((best, current) =>
(current.cost.totalMonthlyCost / current.cost.performanceRating) <
(best.cost.totalMonthlyCost / best.cost.performanceRating)
? current : best
);
const baseline = scenarios[0]; // İlkinin baseline olduğunu varsayıyor
const savings = baseline.cost.totalMonthlyCost - optimal.cost.totalMonthlyCost;
return {
memory: optimal.memory,
cost: optimal.cost.totalMonthlyCost,
savings
};
}
}
// Kullanım örneği
const calculator = new LambdaCostCalculator();
// Süreler bu sayfadan değil, kendi Power Tuning çalıştırmanızdan gelir
const performanceData = [
{ memory: 512, executionMs: 2100 },
{ memory: 1024, executionMs: 1300 },
{ memory: 1769, executionMs: 900 },
{ memory: 3008, executionMs: 800 }
];
const optimal = calculator.findOptimalMemory({
avgExecutionMs: 0, // Override edilecek
invocationsPerMonth: 1000000,
region: 'us-east-1'
}, performanceData);
console.log(`Optimal konfigürasyon: ${optimal.memory}MB`);
console.log(`Aylık tasarruf: $${optimal.savings.toFixed(2)}`);
Gelişmiş Performance Pattern’ları#
Pattern 1: Adaptive Memory Allocation#
Mevcut memory’ye göre processing’i dinamik olarak ayarlayın:
// adaptive-processing.ts
export class AdaptiveProcessor {
private availableMemoryMB: number;
private processingStrategy: 'small' | 'medium' | 'large';
constructor() {
this.availableMemoryMB = parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE || '512');
this.processingStrategy = this.determineStrategy();
}
private determineStrategy(): 'small' | 'medium' | 'large' {
if (this.availableMemoryMB >= 3008) return 'large';
if (this.availableMemoryMB >= 1024) return 'medium';
return 'small';
}
async processData(data: any[]): Promise<any[]> {
switch (this.processingStrategy) {
case 'large':
// Paralel operasyonlarla her şeyi memory'de işle
return await this.parallelProcessing(data);
case 'medium':
// Orta memory kullanımıyla batch processing
return await this.batchProcessing(data, 1000);
case 'small':
// Memory kullanımını minimize etmek için stream processing
return await this.streamProcessing(data, 100);
}
}
private async parallelProcessing(data: any[]): Promise<any[]> {
// Node tek thread çalışır: bu I/O'yu üst üste bindirir, ekstra core kullanmaz.
// 1769 MB üstünde CPU-bound iş için ikinci core'a worker_threads ile ulaşılır.
const chunks = this.chunkArray(data, Math.ceil(data.length / 4));
const promises = chunks.map(chunk => this.processChunk(chunk));
const results = await Promise.all(promises);
return results.flat();
}
private async batchProcessing(data: any[], batchSize: number): Promise<any[]> {
const results = [];
for (let i = 0; i < data.length; i += batchSize) {
const batch = data.slice(i, i + batchSize);
const processed = await this.processChunk(batch);
results.push(...processed);
}
return results;
}
private async streamProcessing(data: any[], chunkSize: number): Promise<any[]> {
const results = [];
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
const processed = await this.processChunk(chunk);
results.push(...processed);
}
return results;
}
private chunkArray<T>(array: T[], size: number): T[][] {
return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
array.slice(i * size, i * size + size)
);
}
private async processChunk(chunk: any[]): Promise<any[]> {
// Gerçek processing mantığınız buraya
return chunk.map(item => ({ ...item, processed: true }));
}
}
Pattern 2: Memory-Aware Caching#
Mevcut memory’ye göre caching davranışını uyarlayın:
// memory-aware-cache.ts
export class MemoryAwareCache {
private cache = new Map<string, any>();
private maxMemoryUsage = 0.6; // Cache için mevcut memory'nin maksimum %60'ını kullan
private availableMemoryBytes: number;
constructor() {
this.availableMemoryBytes = parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE || '512') * 1024 * 1024;
}
set(key: string, value: any): void {
const currentUsage = process.memoryUsage().heapUsed;
const maxCacheMemory = this.availableMemoryBytes * this.maxMemoryUsage;
if (currentUsage < maxCacheMemory) {
this.cache.set(key, {
value,
timestamp: Date.now(),
size: this.estimateObjectSize(value)
});
} else {
// Cache dolu, LRU eviction implement et
this.evictLeastRecentlyUsed();
this.cache.set(key, {
value,
timestamp: Date.now(),
size: this.estimateObjectSize(value)
});
}
}
get(key: string): any {
const entry = this.cache.get(key);
if (entry) {
// LRU için timestamp güncelle
entry.timestamp = Date.now();
return entry.value;
}
return null;
}
private evictLeastRecentlyUsed(): void {
let oldestKey = '';
let oldestTime = Date.now();
for (const [key, entry] of this.cache.entries()) {
if (entry.timestamp < oldestTime) {
oldestTime = entry.timestamp;
oldestKey = key;
}
}
if (oldestKey) {
this.cache.delete(oldestKey);
}
}
private estimateObjectSize(obj: any): number {
// Memory'deki object boyutunun kaba tahmini
return JSON.stringify(obj).length * 2; // Kaba yaklaşım
}
getCacheStats(): {
entries: number;
estimatedMemoryMB: number;
memoryUsagePercent: number;
} {
let totalSize = 0;
for (const entry of this.cache.values()) {
totalSize += entry.size;
}
return {
entries: this.cache.size,
estimatedMemoryMB: totalSize / 1024 / 1024,
memoryUsagePercent: (totalSize / this.availableMemoryBytes) * 100
};
}
}
Production Monitoring ve Profiling#
Gelişmiş CloudWatch Custom Metric’leri#
Lambda’nın sizin için yayınlamadığı sayıları takip edin. Her putMetricData, invocation yolunda senkron bir API çağrısıdır; saniyede birkaç isteğin üstüne çıkınca bunun yerine stdout’a CloudWatch Embedded Metric Format satırları yazın ve metric’leri log agent çıkarsın:
// performance-monitor.ts
import { CloudWatch } from '@aws-sdk/client-cloudwatch';
export class PerformanceMonitor {
private cloudWatch: CloudWatch;
private functionName: string;
constructor() {
this.cloudWatch = new CloudWatch({});
this.functionName = process.env.AWS_LAMBDA_FUNCTION_NAME || 'unknown';
}
async trackPerformanceMetrics(
executionTime: number,
memoryUsed: number,
cpuIntensive: boolean
): Promise<void> {
const metrics = [
{
MetricName: 'ExecutionTime',
Value: executionTime,
Unit: 'Milliseconds',
Dimensions: [
{ Name: 'FunctionName', Value: this.functionName },
{ Name: 'MemorySize', Value: process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE || '512' }
]
},
{
MetricName: 'MemoryUtilization',
Value: memoryUsed,
Unit: 'Bytes',
Dimensions: [
{ Name: 'FunctionName', Value: this.functionName }
]
},
{
MetricName: 'CPUIntensiveOperations',
Value: cpuIntensive ? 1 : 0,
Unit: 'Count',
Dimensions: [
{ Name: 'FunctionName', Value: this.functionName }
]
}
];
await this.cloudWatch.putMetricData({
Namespace: 'Lambda/Performance',
MetricData: metrics
});
}
async trackCostMetrics(estimatedCost: number): Promise<void> {
await this.cloudWatch.putMetricData({
Namespace: 'Lambda/Cost',
MetricData: [
{
MetricName: 'EstimatedCost',
Value: estimatedCost,
Unit: 'None',
Dimensions: [
{ Name: 'FunctionName', Value: this.functionName }
]
}
]
});
}
}
X-Ray Performance Profiling#
Detaylı performance insight’ları için X-Ray kullanın:
// x-ray-profiling.ts
import * as AWSXRay from 'aws-xray-sdk-core';
export const handler = async (event: any) => {
// Facade segment'i Lambda sizin için açar. captureAsyncFunc, sarmalanmış
// bir handler döndürmek yerine gövdeyi module load anında çalıştırırdı.
const segment = AWSXRay.getSegment();
// Memory allocation tracking
const memorySubsegment = segment?.addNewSubsegment('memory-tracking');
const initialMemory = process.memoryUsage();
memorySubsegment?.addAnnotation('initial_memory_mb', Math.round(initialMemory.heapUsed / 1024 / 1024));
try {
// Subsegment'lerle business logic
const processingSegment = segment?.addNewSubsegment('data-processing');
const result = await processData(event.data);
processingSegment?.close();
// Processing sonrası memory kullanımı
const finalMemory = process.memoryUsage();
memorySubsegment?.addAnnotation('final_memory_mb', Math.round(finalMemory.heapUsed / 1024 / 1024));
memorySubsegment?.addAnnotation('memory_delta_mb', Math.round((finalMemory.heapUsed - initialMemory.heapUsed) / 1024 / 1024));
return result;
} finally {
memorySubsegment?.close();
}
};
const processData = async (data: any) => {
const segment = AWSXRay.getSegment();
const subsegment = segment?.addNewSubsegment('data-transformation');
try {
// Performance analizi için metadata ekle
subsegment?.addMetadata('input_size', JSON.stringify(data).length);
subsegment?.addAnnotation('cpu_intensive', true);
const result = await heavyProcessingOperation(data);
subsegment?.addMetadata('output_size', JSON.stringify(result).length);
return result;
} finally {
subsegment?.close();
}
};
Pratik Dersler: Memory Optimizasyonu Yanlış Gittiğinde#
Over-Allocation Tuzağı#
Bir tuning turunun faturaya geri tepmesinin en yaygın yolu over-allocation. “Daha fazla her zaman daha iyidir” varsayımıyla bir function’ı 1024 MB’den 3008 MB’ye taşımak, milisaniye başına ücreti neredeyse üçe katlar. Bu ancak süre de yaklaşık aynı oranda düşerse kendini amorti eder; function bir vCPU’yu geçtikten ve tek thread’de kaldıktan sonra düşmez.
Kural: her performance değişikliğinden sonra GB-saniyeyi yeniden hesaplayın. Tek başına süre grafiği, faturayı yükselten bir değişikliği onaylar.
Ölçekte Ortaya Çıkan Memory Leak#
Leak’ler düşük concurrency’de saklanır. Eşzamanlı her istek kendi execution environment’ını alır ve her biri bağımsız olarak birikir; bu yüzden logging veya tracing kütüphanesindeki yavaş bir leak on invocation’da görünmez, on bin invocation’da öldürücü olur.
Sinyal, REPORT satırındaki Max Memory Used. Aynı ortamın ardışık invocation’larında oturmak yerine tırmanıyorsa, bir şey istekler arasında referans tutuyordur.
// OOM kill function içinde yakalanamaz: runtime fırlatılmaz, sonlandırılır.
// Bunun yerine pahalı yolu başlamadan önce koruyun.
class MemoryGuard {
private readonly allocatedBytes =
parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE || '512', 10) * 1024 * 1024;
private readonly ceiling = 0.75;
async run<T>(operation: () => Promise<T>): Promise<T> {
const { rss } = process.memoryUsage();
if (rss / this.allocatedBytes > this.ceiling) {
// Yazma işleminin ortasında ölmek yerine retry edilebilir bir hata ver
throw new Error(
`Memory tavanına ulaşıldı: ${Math.round(rss / 1024 / 1024)}MB zaten kullanımda`
);
}
return operation();
}
}
Az Memory Vermenin Bedeli#
Bunun aynadaki karşılığı, topluca memory kısmak. Memory’yi yarıya indirmek milisaniye başına ücreti de yarıya indirir; ama süre iki katından fazla artarsa GB-saniye yükselir ve daha yavaş bir function için fazladan ödemiş olursunuz. Az memory ayrıca her invocation’ı timeout’a yaklaştırır ve retry tetikleyen bir timeout aynı işi iki kez faturalandırır.
Sonraki Adım: Production Monitoring Derinlemesine#
Bir memory ayarı, ancak onu şekillendiren trafik sürdüğü sürece geçerlidir. Serinin sonraki bölümü, eğrinin ayağınızın altından kaydığını size söyleyen monitoring, error tracking ve debugging tekniklerini ele alıyor.
O bölümde ele alınanlar:
- Gelişmiş CloudWatch dashboard’ları ve alert’leri
- X-Ray trace analizi ve performance insight’ları
- Error handling ve circuit breaker pattern’ları
- Production debugging araçları ve teknikleri
Varsayılan ve Sınırları#
Arkasında ölçülmüş bir eğri olan 1024 MB, Node.js function’larının çoğunu karşılar: API handler’ları, veritabanı okumaları, orta ölçekli transform’lar. Bunu geçersiz kılmayı üç durum haklı çıkarır. Süresi büyük ölçüde bir downstream çağrısına bağlı ince bir proxy’de aşağı inin; ağı beklerken fazladan CPU hiçbir şey kazandırmaz. 1769 MB’nin üstüne yalnızca iş gerçekten paralelse (worker thread’ler veya kendi içinde thread açan native kütüphaneler) ya da bir model veya veri seti memory’ye sığmak zorundaysa çıkın. Daha geniş memory’ye geçmeden önce arm64’ü deneyin: Graviton, aynı memory ayarında GB-saniye başına yaklaşık %20 daha ucuz.
Bir bağımlılık yükseltmesinden, runtime değişiminden veya payload boyutundaki kaymadan sonra Power Tuning’i tekrar çalıştırın. Hedef mümkün olan en hızlı execution değil; latency bütçenizi hâlâ karşılayan en düşük GB-saniye değeri.
Kaynaklar#
- Configure Lambda function memory (yeni sekmede açılır) - 128 MB - 10.240 MB bellek aralığı ve orantılı CPU tahsis modelini açıklayan resmi referans.
- Memory and computing power - AWS Lambda (yeni sekmede açılır) - Lambda’nın bellekle orantılı olarak vCPU tahsis etme şekli; 1.769 MB tam bir vCPU’ya eşittir.
- Profiling functions with AWS Lambda Power Tuning (yeni sekmede açılır) - En uygun bellek ayarını bulmak için açık kaynaklı Lambda Power Tuning (Step Functions tabanlı) aracının kullanımı.
- Lambda cost and performance optimization - Serverless Applications Lens (yeni sekmede açılır) - Bellek tahsisi ve ARM/Graviton işlemci seçenekleriyle maliyet-performans dengesini ele alan Well-Architected rehberi.
- Best practices for working with AWS Lambda functions (yeni sekmede açılır) - Başlatma desenleri, bağlantı yeniden kullanımı ve bağımlılık şişmesinden kaçınma üzerine resmi Lambda en iyi uygulamaları.
- Using CloudWatch metrics with Lambda (yeni sekmede açılır) - Bellek kullanımı, süre ve çağrı desenlerini izlemek için CloudWatch’ta kullanılabilen Lambda metrikleri.
- AWS Lambda pricing (yeni sekmede açılır) - Mimariye göre GB-saniye ve istek başına ücretler; yukarıdaki maliyet modelinde kullanılan x86 ile arm64 farkı dahil.
AWS Lambda Production Rehberi: 5 Yıllık Gerçek Dünya Deneyimi
5+ yıllık production deneyimine dayalı kapsamlı AWS Lambda rehberi. Cold start optimizasyonu, performans ayarlama, monitoring ve maliyet optimizasyonu ile gerçek savaş hikayeleri ve pratik çözümler.
Bu serideki tüm yazılar
İlgili yazılar
Global uygulamalar için AWS edge computing çözümlerini seçme ve uygulama üzerine pratik örnekler ve maliyet optimizasyonu stratejileri içeren kapsamlı teknik rehber.
aws · cloudfront · lambda +6
Production ortamlarından öğrenilen AWS Lambda cold start optimizasyon stratejileri. Runtime seçimi, provisioned concurrency ve pratik optimizasyon teknikleri.
lambda · serverless · cold-start +3
Advanced AWS Lambda pattern'leri ve maliyet optimizasyonu: Lambda Layers, VPC konfigürasyonu, cross-account execution ve mimari kararlar.
lambda · serverless · cost-optimization +5
İç servis katmanı kurmadan önce kurup kurmayacağınıza karar verin. Katmanın çağrı başına maliyeti, VPC Lattice'in kazandığı hacim ve direct invoke'un hâlâ kazandığı an.
aws · aws-cdk · lambda +4
Private REST API gRPC’yi yapısal olarak taşıyamaz ve gRPC konuşan her AWS yüzeyi Lambda hedeflerini dışlar. gRPC’den neyi tutmalı, neyi bırakmalı.
aws · aws-cdk · lambda +4