Bu makale, Spring Boot, Hibernate ORM ve Spring Data JPA kullanırken flush maliyetini ölçme, async-profiler/JFR ile CPU/latency profilleme ve idempotent yazma desenlerini uygulamalı gösterir. Java backend geliştirme ve java eğitimi bağlamında pratik örnekler içerir.
Spring Boot ile Tutarlı Okuma, Flush ve Profiling: Java backend geliştirme
Hibernate ORM ve Flush maliyetini ölçme: Spring Data JPA uygulamalı rehber
Flush operasyonlarının maliyetini anlamak için önce ölçün: uygulamayı p6spy ile sarın veya datasource-proxy kullanarak SQL sayısını ve sürelerini elde edin. application.properties içine p6spy konfigürasyonu ekleyin ve logları saymak için:
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6spyDriver
logging.level.p6spy=DEBUG Sonra bir yük testi çalıştırın (örneğin wrk veya Gatling) ve p6spy çıktısını grep ile sayın: grep -i "select" /var/log/app/p6spy.log | wc -l—bu size flush sırasında tetiklenen SQL sayısını gösterir.Neden flush maliyeti yükselir: JPA persist edilen entityleri flush ederken birim çalışma (unit-of-work) içinde dirty-checking ve cascade ilişkileri için birçok SELECT/UPDATE/INSERT tetiklenir. Bu mekanizma object graph traversal ve PreparedStatement execution içerir; dolayısıyla batch boyutu, jdbc batch ayarı, ve EntityManager.clear() davranışı doğrudan maliyeti etkiler. Ölçmek için Hibernate statistics aktif edin:
spring.jpa.properties.hibernate.generate_statistics=true
// runtime: SessionFactory sessionFactory = entityManager.unwrap(Session.class).getSessionFactory();
System.out.println(sessionFactory.getStatistics().getEntityInsertCount());Somut optimizasyon adımları ve karşılaştırma yöntemi: önce baseline ölçün (p6spy select count ve JFR/async-profiler snapshot). Ardından application.properties'te batch ayarlarını değiştirin ve tekrar ölçün:
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true Değişiklikten sonra p6spy üzerinden SQL sayısını ve JFR CPU/Wall zamanını karşılaştırın. Beklenen sonuç: daha az PreparedStatement execute sayısı ve daha düşük I/O bekleme süresi. Karşılaştırmayı nümerik hale getirmek için test başına toplam SQL sayısı ve 95. yüzdelik latency değerini not edin.Edge case ve best practice: bulk update gerektiren durumlarda EntityManager ile tek tek entity okumak yerine JPQL/Criteria bulk update kullanın ve ardından EntityManager.clear() ile cache temizliği yapın. Aksi halde Hibernate persistent context yeniden senkronize olmaya çalışıp bellek patlaması veya fazla SQL üretir. Örnek:
@Transactional
public int bulkPriceUpdate(BigDecimal delta){
int updated = em.createQuery("UPDATE Product p SET p.price = p.price + :d")
.setParameter("d", delta)
.executeUpdate();
em.clear();
return updated;
}Spring Boot profiling: async-profiler, JFR ve nasıl ölçüp karşılaştırırsınız
Doğru araç seti: CPU ve safepoint tarafı için async-profiler, JVM olayları ve safepoint-olmayan uzun beklemeler için Java Flight Recorder (JFR). Çalıştırma örnekleri: async-profiler ile 30s örnekleme:
./profiler.sh -d 30 -e cpu -f /tmp/flame-cpu.svg
# veya duvar zamanı için
./profiler.sh -d 30 -e wall -f /tmp/flame-wall.svg
# JFR başlatmak için
jcmd JFR.start name=profile filename=/tmp/recording.jfr duration=60s settings=profile Profil sonuçlarını baseline/optimize/after şeklinde saklayın ve her test için aynı yük şartlarını (wrk/Gatling script) kullanın.Neden iki araç: async-profiler sampling tabanlı, native frame ve Java frame entegrasyonu iyi; JFR ise GC, safepoint ve lock-wait gibi JVM-level olayları sağlar. Örneğin endpoint latency spike'ı gördüğünüzde async-profiler CPU hot path gösterirken JFR 'Safepoint' veya 'Monitor Wait' eventleri ile nedenin lock contention olduğunu gösterebilir. Bu yüzden her zaman hem CPU hem de JVM events ile karşılaştırma yapın.
Uygulamalı tuning örneği: Spring @Async veya ThreadPoolTaskExecutor pool'unda çekirdek ve maksimum boyut ayarlayın, ve queue davranışını bounded yapın. application.yml örneği:
spring:
task:
execution:
pool:
core-size: 8
max-size: 40
queue-capacity: 500
thread-name-prefix: async-
Baseline altında queue büyüklüğünü ve 95. yüzdelik latency'yi not edin. Queue capacity'yi 50'ye çekip max-size'i 100'e çıkarın; tekrar profilleyin. Ölçü: 95p latency, thread utilization ve CPU user time. Mekanizma: bounded queue+larger pool, I/O-bound işlerde latency'yi düşürebilir; ancak CPU saturasyonu riskine karşı async-profiler ile CPU hot path kontrolü zorunludur.Edge case: thread pool'u artıp da GC/CPU saturasyonu oluyorsa, thread sayısını azaltıp I/O concurrency'yi reaktif WebClient veya Netty ile çözmeyi değerlendirin. Bu tip geçişte ölçüt olarak heap allocation rate (JFR allocation profiler) ve full GC sıklığını izleyin.
Idempotency ve Transaction tasarımı: Redis SETNX, outbox ve performans ölçümü
Pratik idempotency anahtarı uygulaması için Redis SETNX veya Redisson kullanın. Spring Data Redis ile basit kontrol:
@PostMapping("/charge")
public ResponseEntity> charge(@RequestHeader("Idempotency-Key") String key){
Boolean ok = redisTemplate.opsForValue().setIfAbsent(key, "1", Duration.ofMinutes(5));
if (Boolean.FALSE.equals(ok)) return ResponseEntity.status(409).body("Duplicate");
// iş mantığı ve DB yazma
return ResponseEntity.ok("ok");
}
Ölçüm: Prometheus ile duplicate counter ekleyin (Micrometer) ve gerçek duplicate oranını sorgulayın: sum(increase(app_idempotent_duplicates_total[5m]))
# Bu size 5 dakikadaki duplicate sayısını verir.Neden Redis SETNX yetersiz olabilir: Redis tek başına API-gateway katmanında race durumunu önler, fakat veri tabanına yazma sırasında uygulama çökerse dış sistem tekrar edilirse tutarsızlık olabilir. Bu durumda transaction içinde outbox pattern kullanın: tek DB transaction içinde hem domain değişikliğini hem de outbox event'ini yazın. Örnek SQL/ JDBC:
connection.setAutoCommit(false);
// 1) insert business row
// 2) insert outbox row
connection.commit(); Sonra ayrı bir sender process outbox'u okuyup dış servise gönderir. Metrik: outbox lag (max(event_time - sent_time)) ölçün.Performans karşılaştırması: baseline = sadece DB insert + external call sync; alternatif = outbox ile asenkron gönderim. Ölçüm yöntemi: uçtan uca latency ve 99p değerini wrk ile ölçün; ayrıca database tx sürelerini psql/slow query log ile karşılaştırın. Beklenen: outbox, API latency'yi düşürürken toplam event gönderim gecikmesini artırabilir; karar SLAdan verilmeli.
Edge case: outbox polling performansı için batch boyutunu, sleep intervalini ve işlem başına DELETE yerine tombstone flag kullanmayı deneyin. Büyük batch delete'ler transactional log'u büyütür; bunun yerine DELETE yerine sent=true güncellemesi ve arşiv işlemi uygulayın.
Spring AI ve Model Context Protocol (MCP) ile güvenli araç çağrıları: entegrasyon ve ölçümler
Model Context Protocol (MCP) kullanırken bağlam meta-verisini request ile tutarlı şekilde taşımak kritik. Spring WebClient ile MCP header'larını ekleme örneği:
WebClient webClient = WebClient.builder()
.baseUrl("https://model.example")
.defaultHeader("MCP-Context", contextJson)
.build();
Mono r = webClient.post()
.uri("/v1/complete")
.bodyValue(request)
.retrieve()
.bodyToMono(ModelResponse.class);
Kullanılacak kütüphane: spring-ai (veya vendor SDK) + spring-webflux. Her çağrı için token usage ve latency'yi Micrometer ile ölçün: Counter.builder("model.calls").tag("model","gptx").register(meterRegistry);
Timer.builder("model.latency").register(meterRegistry);
Güvenlik: prompt injection'ı azaltmak için gelen stringleri sanitaze edin, ve MCP context içinde izin verilen kontrol alanlarını whitelist'leyin. Ayrıca araç çağrıları için circuit breaker (resilience4j) ve rate limiting uygulayın. application.yml örnek direnç yapılandırması:
resilience4j.circuitbreaker.instances.modelService.register-health-indicator=true
resilience4j.circuitbreaker.instances.modelService.slidingWindowSize=50
Maliyet ve token sınırlaması: model çağrılarında token tüketimini sınırlamak için hem model parametreleri (max_tokens) hem de istemci tarafı kesme mekanizması kullanın. WebClient zaman aşımı ve body limit ayarı örneği:
TcpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(10)));
// ve max in-memory size
.webClient()
.codecs(config -> config.defaultCodecs().maxInMemorySize(2*1024*1024));
Ölçüm: model çağrısı başına ortalama token sayısını metrikle izleyin ve aylık maliyeti tahmin etmek için multiply edin.Edge case: MCP context çok büyükse header sınırlarını aşabilirsiniz; bu durumda context'i kısa bir ID ile taşın ve context store (Redis veya DB) ile eşleyin. Bu, header limitleri ve ağ gecikmesi sorunlarını çözer.
İlgili Eğitim
Sık Sorulan Sorular
Java eğitimi alırken Hibernate flush maliyetini nasıl pratikte ölçerim?
Pratik ölçüm: uygulamanızı p6spy veya datasource-proxy ile sarmalayın, baseline yük testi çalıştırın (wrk/Gatling). p6spy.log içindeki SQL satırını sayın: grep -i "select" p6spy.log | wc -l. Ardından application.properties'te spring.jpa.properties.hibernate.jdbc.batch_size=50 gibi ayarlar yapıp testi tekrar çalıştırın ve SQL sayısı ile 95. yüzdelik latency'yi karşılaştırın. Ek olarak JFR veya async-profiler ile CPU ve safepoint olaylarını yakalayın.
spring boot eğitimi sırasında hangi profilleme araçlarını öğretmeliyim (async-profiler, JFR vs)?
En az async-profiler (flamegraph üretimi için) ve Java Flight Recorder (JVM event'leri, allocation ve safepoint) öğretilmeli. Komut örnekleri: ./profiler.sh -d 30 -e cpu -f /tmp/flame.svg
java kursu kapsamında idempotency nasıl uygulanır, Redis mi outbox mı tercih edilmeli?
Redis SETNX, API gateway katmanında duplicate requestleri engellemek için hızlıdır: redisTemplate.opsForValue().setIfAbsent(key, "1", ttl). Ancak DB yazma sırasında uygulama çökmesi riskine karşı outbox pattern daha tutarlıdır: aynı transaction içinde domain + outbox yazılır, sonra ayrı sender gönderir. Karar: düşük gecikme SLA'sı gerekiyorsa Redis+SETNX; kesin veri tutarlılığı gerekiyorsa outbox.
spring data jpa ile bulk update sonrası konsistent okuma nasıl sağlanır?
Bulk JPQL/SQL update yaptıktan sonra EntityManager içerisindeki first-level cache stale kalır; bu yüzden em.clear() çağırın veya ilgili entity'leri refresh edin. Örnek: int updated = em.createQuery("UPDATE ...").executeUpdate(); em.clear(); Böylece takip eden okuma işlemleri DB'den güncel veriyi alır.
spring ai ve model context protocol entegrasyonu yaparken hangi ölçümleri tutmalıyım?
Her model çağrısı için en az: çağrı sayısı (Counter), latency (Timer), token tüketimi (Gauge veya Distribution Summary) ve hata/timeout sayıları. Micrometer ile kayıt örneği: Counter.builder("model.calls").register(mr); Timer.builder("model.latency").register(mr). Bu metrikler aylık maliyet ve SLA kararları için kullanılır.
AI / LLM Discovery
Bu makale Opendart Akademi Java eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.



