Mersin’de 280 araçlık bir lojistik şirketi, 22 Azure Function App ile araç GPS, müşteri sipariş, teslimat onay, fatura entegrasyonu, EDI mesaj işleme, SMS bildirim gibi event-driven süreçleri yönetiyor. 8 ayda Consumption plan’dan başlayıp, hibrit Consumption + Premium + Flex Consumption modeline geçtik. Bu yazı sahadaki teknik notlar.
Plan Seçimi Karar Matrisi
| Plan | Cold start | Max execution | Aylık baz | Use case |
|---|---|---|---|---|
| Consumption | Var (1-5 sn) | 5/10 dk | $0 + execution | Düşük volume, batch |
| Premium | Yok (warm instance) | 30+ dk | $155+/ay | Yüksek volume, latency-sensitive |
| Flex Consumption | Az (always-ready) | 30 dk | $0 + execution + always-ready instances | Modern öneri, esnek |
| Dedicated (App Service Plan) | Yok | Sınırsız | VM bazlı | Predictable, uzun süreli |
22 Function App dağılımı:
- 14 düşük-orta volume (1-100/dk) → Consumption
- 5 yüksek volume + latency-critical (GPS pozisyon, müşteri tracking) → Premium
- 3 batch işlem (gece raporlama) → Flex Consumption
Trigger Tipleri Sahada
HTTP Trigger
Müşteri portalından “siparişimi takip et” çağrısı:
[Function("OrderTracking")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
string orderId)
{
var order = await _cosmosClient
.GetContainer("orders", "tracking")
.ReadItemAsync<Order>(orderId, new PartitionKey(orderId));
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(order.Resource);
return response;
}
Service Bus Trigger
EDI mesaj işleme: müşteriden gelen sipariş Service Bus queue’ya düşer, function işler, ERP’ye yazar:
[Function("ProcessEdiOrder")]
public async Task Run(
[ServiceBusTrigger("edi-orders", Connection = "ServiceBusConnection")]
EdiMessage message,
FunctionContext context)
{
var validated = _validator.Validate(message);
if (!validated.IsValid)
throw new EdiValidationException(validated.Errors);
await _erpClient.CreateOrderAsync(message.ToOrder());
await _notifier.SendAsync($"Order received from {message.CustomerCode}");
}
Hata durumunda otomatik retry, max retry sonrası dead-letter queue. Manuel inceleme için DLQ monitoring.
Event Grid Trigger
Storage’a yüklenen yeni teslimat fotoğrafı → blob created event → function fotoğrafı işler (resize, watermark, AI vision ile içerik tanıma):
[Function("ProcessDeliveryPhoto")]
public async Task Run(
[EventGridTrigger] EventGridEvent eventGridEvent)
{
var blobUrl = eventGridEvent.Data.ToObject<StorageBlobCreatedEventData>().Url;
var photo = await _blobClient.DownloadAsync(blobUrl);
// Resize
var thumbnail = ImageProcessor.Resize(photo, 800);
await _blobClient.UploadAsync($"thumbnails/{Path.GetFileName(blobUrl)}", thumbnail);
// AI Vision: paket hasarı tespiti
var visionResult = await _visionClient.AnalyzeImageAsync(blobUrl);
if (visionResult.Tags.Any(t => t.Name == "damaged"))
await _alerts.SendAsync($"Possible damage in delivery photo: {blobUrl}");
}
Cosmos DB Change Feed Trigger
Yeni sipariş Cosmos’a yazıldığında change feed function tetiklenir, müşteriye SMS atılır, dispatcher’a uyarı gider:
[Function("OnNewOrder")]
public async Task Run(
[CosmosDBTrigger(
databaseName: "logistics",
containerName: "orders",
Connection = "CosmosDBConnection",
LeaseContainerName = "leases")]
IReadOnlyList<Order> orders)
{
foreach (var order in orders)
{
await _smsClient.SendAsync(order.CustomerPhone,
$"Siparişiniz alındı: {order.Id}, tahmini teslimat: {order.EstimatedDelivery}");
await _dispatcher.AssignAsync(order);
}
}
Timer Trigger
Günlük gece 02:00 raporlama, 06:00 müşteri özet email’i:
[Function("DailyReport")]
public async Task Run(
[TimerTrigger("0 0 2 * * *")] TimerInfo timer)
{
var report = await _reportingService.GenerateDailyAsync();
await _emailClient.SendToManagementAsync(report);
}
Durable Functions: Long-Running Workflow
Müşteri siparişi gelince: validate → ERP’ye yaz → ödeme onayı bekle → kargo şirketine bildir → müşteriye SMS. Bu adımlar saatler/günler sürebilir, durable orchestrator ile yönetilir.
[Function("OrderOrchestrator")]
public async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var order = context.GetInput<Order>();
// 1. Validate
var validated = await context.CallActivityAsync<bool>("ValidateOrder", order);
if (!validated) return;
// 2. ERP'ye yaz
await context.CallActivityAsync("CreateInErp", order);
// 3. Ödeme onayı bekle (max 48 saat)
using (var timer = context.CreateTimer(TimeSpan.FromHours(48), CancellationToken.None))
{
var paymentEvent = context.WaitForExternalEvent<PaymentEvent>("PaymentReceived");
var winner = await Task.WhenAny(timer, paymentEvent);
if (winner == timer) {
await context.CallActivityAsync("CancelOrder", order);
return;
}
}
// 4. Kargo şirketine bildir
await context.CallActivityAsync("NotifyShipping", order);
// 5. SMS gönder
await context.CallActivityAsync("SendSms", order);
}
Cold Start Mücadelesi
Consumption plan’ında ilk request 2-5 sn cold start. Latency-sensitive function’lar için:
- Premium plan’a taşı (always-warm)
- Always-ready instances 1-2 ayarla (Flex Consumption)
- Health check endpoint + ping (keep-warm strategy)
- Dependency injection ve large object’leri lazy init
Maliyet Optimizasyonu
Consumption plan: ilk 1M execution + 400.000 GB-second ücretsiz/ay. Sonrası $0.20/M execution + $0.000016/GB-second.
22 function dağılımı, aylık cost:
| Plan | Function sayısı | Aylık execution | Aylık cost (USD) |
|---|---|---|---|
| Consumption | 14 | ~~3.2M | ~~$45 |
| Premium (EP1) | 5 | ~~28M | ~~$420 (plan + execution) |
| Flex Consumption | 3 | ~~120K (batch) | ~~$22 |
| Toplam | 22 | — | ~~$487 |
Karşılaştırma: 22 function VM’de host edilse Standard_B4ms × 4 instance × ~$140 = $560/ay + management overhead. Functions ucuz + zero-ops.
Monitoring: Application Insights
Her function App Insights’a bağlı. Kustom KQL sorguları:
// En yavaş function
requests
| where timestamp > ago(24h)
| where cloud_RoleName startswith "func-"
| summarize
Count=count(),
AvgDuration=avg(duration),
P95=percentile(duration, 95)
by name
| order by P95 desc
// Hata oranı yüksek function
requests
| where timestamp > ago(24h)
| where cloud_RoleName startswith "func-"
| summarize
Total=count(),
Failed=countif(success == false)
by name
| extend FailRate = round(100.0 * Failed / Total, 2)
| where FailRate > 5
| order by FailRate desc
Sahada Düşülen Üç Tuzak
- Tüm function’ları aynı plan’a koymak: Düşük volume Consumption, latency-critical Premium. Hibrit yaklaşım gerekli.
- Stateless varsayımı kırmak: Function instance’ı bir sonraki çağrıda farklı olabilir. Static field’larda state tutma, distributed cache (Redis) kullan.
- Long-running iş için durable yerine async retry: 5 dk timeout aşılınca patlıyor. Durable functions veya queue-based long-poll pattern kullan.
CloudSpark olarak Azure Functions mimarisi, event-driven entegrasyon, Durable Functions, plan seçimi ve cold start optimizasyonu için danışmanlık veriyoruz.



