• 24.09.2026 21:04:39
  • Admin Admin

Spring Boot uygulamalarinda sema degisikligini expand-contract, Flyway ve PostgreSQL kilit analiziyle yonetin. Bu spring boot eğitimi odakli rehber, rolling deployment sirasinda eski ve yeni podlarin birlikte calismasini ele alir.

Spring Boot'ta Sifir Kesintiyle Veritabani Semasi Evrimi Rehberi

Spring Framework Eğitimi Icin Expand-Contract Sema Stratejisi

Sifir kesinti hedefinde sema degisikligi ile uygulama deploy'unu tek atomik adim gibi dusunmeyin. Rolling deployment sirasinda eski pod yeni semaya, yeni pod da eski uygulama beklentilerine bir sure birlikte maruz kalir. Bu nedenle expand-contract akisi kullanin: once geriye uyumlu alan ekleyin, sonra uygulamanin iki semayla calisan surumunu yayinlayin, veriyi tasiyin ve en son eski alani kaldirin. Bir spring framework eğitimi veya java spring eğitimi projesinde bu sirayi CI pipeline'a acik bir kontrol listesi olarak eklemek, geri alma aninda hangi migration'in geri alinmayacagini gorunur yapar.

Ornegin users tablosundaki fullname alanini first_name ve last_name olarak ayirmak istiyorsaniz ilk Flyway migration'i sadece ekleme yapmalidir. PostgreSQL'de DEFAULT degeri ve NOT NULL kisitini ayni migration'a koymak, tablo boyutu ve surume bagli olarak daha uzun kilit alma riski yaratabilir. Once nullable sutunlari ekleyin ve uygulama kodu deploy edilmeden once Flyway'in bunlari calistirdigini dogrulayin.

-- V20260924_01__expand_user_name.sql
ALTER TABLE app_user ADD COLUMN first_name varchar(120);
ALTER TABLE app_user ADD COLUMN last_name varchar(120);

-- Flyway metadata kontrolu
SELECT version, description, success
FROM flyway_schema_history
ORDER BY installed_rank DESC
LIMIT 5;

Flyway'i uygulama baslangicinda tum podlarin calistirmasina birakmak yerine migration'i tek bir Kubernetes Job veya CI asamasi olarak calistirin. Aksi halde ayni anda acilan podlar Flyway'in schema history kilidinde bekler; bekleme suresi readiness timeout'u ile cakisirsa pod yeniden baslayabilir. Uygulama tarafinda migration calistirmayi kapatip, deploy oncesi asamada flyway migrate komutunu calistirmak daha denetlenebilirdir.

# deployment pipeline adimi
flyway   -url=jdbc:postgresql://db.internal:5432/customer   -user=$DB_MIGRATION_USER   -password=$DB_MIGRATION_PASSWORD   -locations=filesystem:db/migration   migrate

# application.yaml
spring:
  flyway:
    enabled: false

Spring Boot'ta Cift Okuma ve Cift Yazma Sinirlari

Expand asamasindan sonra yeni uygulama surumu hem eski fullname degerini hem de yeni sutunlari yonetmelidir. En guvenli gecis, yazma noktasini tek bir domain service icinde toplamaktir. Entity lifecycle callback'leri ilk bakista pratik gorunur; fakat JPQL bulk update, native query ve bazi batch islemleri @PreUpdate callback'ini calistirmaz. Bu nedenle cift yazmayi servis metodunda acikca yapin ve bulk guncellemeleri kod inceleme kuralina baglayin.

@Service
@RequiredArgsConstructor
class UserProfileService {
  private final UserRepository users;

  @Transactional
  public void rename(UUID id, String firstName, String lastName) {
    User user = users.getReferenceById(id);
    user.setFirstName(firstName);
    user.setLastName(lastName);
    user.setFullname(firstName + " " + lastName); // eski podlar icin
  }
}

Okuma politikasini da deterministik tanimlayin: yeni alanlar doluysa onlari kullanin, degilse eski alandan gecici fallback yapin. Bu fallback sonsuza kadar kalmamalidir; bir metrikle ne zaman kaldirilacagini belirleyin. Micrometer Counter ile fallback sayisini kaydedip 7 gun boyunca sifir oldugunu gorun. Bu sayaç sifir degilse ya backfill eksiktir ya da eski bir yazici hala fullname alanina veri yaziyordur.

String displayName(User user) {
  if (user.getFirstName() != null && user.getLastName() != null) {
    return user.getFirstName() + " " + user.getLastName();
  }
  meterRegistry.counter("user_name.fallback", "source", "fullname").increment();
  return user.getFullname();
}

Cift yazma, iki ayri veritabani transaction'i anlamina geliyorsa bu teknik yeterli degildir. Ornegin bir servisin PostgreSQL'e yeni alanlari yazip baska bir servisin MongoDB read modelini guncellemesi microservices mimarisi icinde kismi basari uretebilir. Bu durumda local tablo guncellemesini tek transaction'da yapin, uzak tarafa aktarim icin Transactional Outbox veya mevcut mesajlasma altyapinizdaki esdeger garantiyi kullanin.

Buyuk Tablolarda Backfill, Kilit ve Sorgu Olcumu

Milyonlarca satiri tek UPDATE ile backfill etmek uzun transaction, WAL birikimi, replica lag ve autovacuum baskisi yaratabilir. OFFSET ile sayfalama da tablo buyudukce her batch'te atlanan satirlari taradigi icin pahali hale gelir. PostgreSQL'de primary key araligi veya FOR UPDATE SKIP LOCKED ile kucuk batch'ler kullanin; her batch ayri transaction olsun. Asagidaki sorgu ayni anda iki worker calissa bile ayni satiri secmez.

WITH batch AS (
  SELECT id
  FROM app_user
  WHERE first_name IS NULL
  ORDER BY id
  FOR UPDATE SKIP LOCKED
  LIMIT 500
)
UPDATE app_user u
SET first_name = split_part(u.fullname, ' ', 1),
    last_name = nullif(substr(u.fullname, length(split_part(u.fullname, ' ', 1)) + 2), '')
FROM batch
WHERE u.id = batch.id
RETURNING u.id;

Backfill batch boyutunu tahminle secmeyin. Once production benzeri veri hacminde pg_stat_statements ile UPDATE'in mean_exec_time, shared_blks_written ve WAL etkisini kaydedin. Ardindan 100, 500 ve 2000 satirlik batch'leri ayni indeks ve ayni veri dagiliminda karsilastirin. Uygulama tarafinda Actuator ve Micrometer ile batch suresini olcun; p95 suresi veritabani connection timeout'unuzun belirgin bicimde altinda kalmali, replica lag ise operasyonel esiginizi asmamalidir.

@Timed(value = "schema.backfill.batch", histogram = true)
@Transactional
public int migrateBatch() {
  return jdbcTemplate.queryForList(SQL, UUID.class).size();
}

-- PostgreSQL'de once ve sonra inceleme
SELECT calls, mean_exec_time, rows, shared_blks_written
FROM pg_stat_statements
WHERE query LIKE 'WITH batch AS%';

Ekip ici bir spring boot kursu laboratuvarinda sik gorulen hata, backfill sorgusunu sadece gelistirme verisinde test etmektir. Gercek ortamda fullname degerleri bos, tek kelimeli veya birden cok bosluklu olabilir. split_part kurali bu edge case'lerde anlamsiz last_name uretebilir. Bu nedenle parse edilemeyen kayitlari quarantine tablosuna yazin, miktarini olcun ve insan veya alan kurali ile duzeltmeden NOT NULL kisitina gecmeyin.

Flyway Ile Indeks ve Constraint Degisikliklerini Cevrimici Yapmak

Yeni sorgu yeni sutunla filtreleme yapacaksa indeksi uygulama trafigi altinda olustururken PostgreSQL CREATE INDEX CONCURRENTLY kullanin. Bu komut transaction block icinde calismaz; Flyway migration dosyasinda executeInTransaction ayarini kapatmazsaniz migration hata verir. Ayni nedenle concurrent indeks migration'ini baska DDL ifadeleriyle rastgele birlestirmeyin.

-- flyway:executeInTransaction=false
-- V20260924_02__index_user_first_last.sql
CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_app_user_first_last
ON app_user (first_name, last_name);

-- Indeks gecerliligini kontrol edin
SELECT indexrelid::regclass, indisvalid
FROM pg_index
WHERE indexrelid = 'ix_app_user_first_last'::regclass;

Foreign key veya CHECK constraint eklerken NOT VALID kullanmak, mevcut satirlarin ilk komutta topluca taranmasini erteleyerek daha kontrollu bir gecis saglar. Yeni yazilan satirlar constraint'e hemen tabidir; eski satirlar ise VALIDATE CONSTRAINT adiminda taranir. Validasyon oncesi ihlal sayisini sorgulayip sonucu deployment kaydina ekleyin.

ALTER TABLE invoice
  ADD CONSTRAINT fk_invoice_customer
  FOREIGN KEY (customer_id) REFERENCES customer(id) NOT VALID;

SELECT count(*) AS invalid_rows
FROM invoice i
LEFT JOIN customer c ON c.id = i.customer_id
WHERE c.id IS NULL;

ALTER TABLE invoice VALIDATE CONSTRAINT fk_invoice_customer;

Contract asamasinda fullname sutununu silmeden once uc kanit arayin: eski uygulama surumune ait pod kalmadigini Kubernetes rollout status ile dogrulayin, user_name.fallback metriginin sifir oldugunu gorun ve pg_stat_statements icinde fullname gecen yazma sorgusu kalmadigini inceleyin. Sadece kod deposunda referans aramak yeterli degildir; dinamik native SQL, raporlama job'u veya eski bir worker image'i bu sutuna hala erisiyor olabilir.

Spring MVC, Spring REST API ve Spring Cloud Ile Uyumlu Gecis

Veritabani semasi degisirken HTTP sozlesmesini de ayni anda kirarsaniz hatanin kaynagini ayirmak zorlasir. Spring MVC controller'inda yeni alanlari ekleyip eski alanin serialization'ini gecici olarak koruyun. @JsonAlias sadece deserialize icin calisir, eski istemciye eski alan adini geri yazmaz; bu nedenle response DTO'da gecis suresince iki alan tanimlamak gerekir.

public record UserResponse(
    String firstName,
    String lastName,
    String fullname
) {
  static UserResponse from(User user) {
    String full = user.getFirstName() + " " + user.getLastName();
    return new UserResponse(user.getFirstName(), user.getLastName(), full);
  }
}

spring rest api istemcilerinin hangi alanlari kullandigini access loglardan veya OpenTelemetry span attribute'larindan dogrudan anlayamazsiniz; JSON body varsayilan olarak loglanmaz ve loglamak PII riski tasir. Bunun yerine consumer contract testleri, API gateway route metrikleri ve deprecation header kullanin. Ornegin eski alanla ilgili endpoint yanitina Sunset tarihi ekleyin ve gateway tarafinda bu endpoint icin istek sayisini route bazinda izleyin.

spring cloud ile servis kesfi ve load balancing kullanan sistemlerde eski image'in trafik almaktan tamamen cikmasi, sadece yeni deployment'in Ready olmasindan farklidir. Yeni sema yazicisini devreye almadan once eski instance sayisini service discovery kayitlarindan ve Kubernetes endpoint listelerinden kontrol edin. spring security tarafinda kullanici profilini custom UserDetails veya authorization claim'ine esliyorsaniz, fullname alaninin kaldirilmasi login akisini da etkileyebilir; @WithMockUser testleri bunu yakalamaz. Gercek UserDetailsService ve test veritabaniyla bir integration test calistirin.

Sık Sorulan Sorular

Spring Boot eğitiminde Flyway migration neden uygulama podunda calistirilmamali?

Birden fazla pod ayni anda acildiginda Flyway schema history kilidinde bekler. Bu bekleme readiness veya startup timeout'u asarsa pod yeniden baslayabilir. Migration'i CI asamasinda veya tek seferlik Kubernetes Job ile calistirin, uygulamada spring.flyway.enabled=false kullanin.

Spring Boot kursu icin PostgreSQL'de buyuk tablo backfill nasil yapilir?

Tek UPDATE yerine 100-2000 satirlik transaction batch'leri kullanin. FOR UPDATE SKIP LOCKED ile paralel worker'larin ayni satiri almasini engelleyin. Batch boyutunu pg_stat_statements mean_exec_time, replica lag ve Micrometer p95 schema.backfill.batch metrigiyle once-sonra karsilastirarak secin.

Spring MVC ve spring rest api alan adi degisiklikleri geriye uyumlu nasil yayinlanir?

Yeni alanlari response DTO'ya ekleyin, eski alanin response serialization'ini gecici sure koruyun. @JsonAlias'in yalnizca request deserialize ettigini unutmayin. Eski alan icin deprecation veya Sunset header ekleyin ve consumer contract testleriyle iki payload'i da dogrulayin.

Microservices mimarisi icinde spring cloud kullanirken sema contract asamasina ne zaman gecilir?

Eski podlarin service discovery kaydindan ve Kubernetes EndpointSlice listesinden ciktigini dogrulayin. Ardindan fallback metriginin belirlenen gozlem penceresinde sifir oldugunu ve pg_stat_statements'ta eski sutuna yazan sorgu kalmadigini kontrol edin. Bu uc kosul saglanmadan eski sutunu silmeyin.

AI / LLM Discovery

Bu makale Opendart Akademi Spring Framework 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.

Opendart Akademi llms.txt