# Dosya İşlemede Atomik Yayınlama

> Dosya yayınında atomik rename, görünürlük ve kalıcılık aynı garanti değildir; güvenilir akış geçici dosya, fsync, dizin kalıcılığı ve NFS görünürlüğünü ayrı ele alır.

- Author: Muhammet Ali Köker
- Language: tr
- Canonical: https://alikoker.com.tr/dosya-islemede-atomik-yayinlama
- Translation: https://alikoker.com.tr/en/atomic-publication-in-file-processing
- Published: 2026-08-03T14:00:00+03:00
- Modified: 2026-09-08T12:55:00+03:00
- Verified: 2026-08-07T11:00:00+03:00
- Type: article

## Üç Garantiyi Birbirine Karıştırmamak Gerekir

Dosya yayınında atomiklik, görünürlük ve kalıcılık farklı problemlerdir. Aynı dosya sistemi içindeki `rename`, okuyucunun yarım hedef adı görmesini engelleyebilir; fakat bu işlem tek başına verinin güç kaybından sonra kalacağını veya bütün NFS istemcilerinin yeni adı aynı anda göreceğini garanti etmez.

Pratik sözleşme bu nedenle aşamalıdır: veri geçici ada tamamlanır, gerekli olduğunda dosya `fsync` ile kalıcılaştırılır, hedef ada atomik olarak taşınır ve çökme modeli gerektiriyorsa dizin üstverisi de kalıcılaştırılır. Ağ dosya sistemlerinde istemci önbelleği ve görünürlük ayrıca değerlendirilir. Bu ayrım [idempotentlik](/wiki/idempotency) ve [yeniden deneme](/kritik-sistemlerde-guvenli-retry-tasarimi) tasarımını da doğrudan etkiler.

Dosya tabanlı iş akışlarında en tehlikeli an çoğu zaman veriyi üretirken değil, “hazır” dosyayı başka süreçlerin görebileceği ada taşırken ortaya çıkar. Okuyucu kısmen yazılmış dosyayı açabiliyorsa üretici tarafındaki başarılı `write()` çağrıları uçtan uca tutarlılık sağlamaz.

Bu nedenle kullandığım temel sözleşme veriyi hedef ad altında yazmamak, aynı dosya sistemi üzerinde geçici ada tamamlamak ve yalnız içerik kapanıp gerektiğinde kalıcılaştırıldıktan sonra atomik `rename`/move ile yayımlamaktır. Bu yaklaşım tek başına bütün dayanıklılık problemlerini çözmez; dosya sistemi, `fsync`, dizin üstverisi ve çökme semantiği ayrıca hesaba katılmalıdır.

Yerel dosya sisteminde bu işlem çoğu zaman yeterli görünür. Aynı düzen NFS üzerinde kullanıldığında ise üç ayrı garanti birbirine karışabilir:

atomiklik
görünürlük
kalıcılık

Atomiklik, ad değişiminin ara durum göstermeden gerçekleşmesini ifade eder. Görünürlük, diğer istemcilerin yeni dosyayı ne zaman gördüğüyle ilgilidir. Kalıcılık ise sunucu veya istemci çöktüğünde verinin depolama üzerinde korunup korunmadığını belirler.

Bir "rename" işleminin atomik olması, dosyanın bütün NFS istemcilerinde aynı anda görünür hale geldiğini veya elektrik kesintisinden sonra kesin olarak korunacağını göstermez. Güvenilir dosya yayını bu garantileri ayrı ayrı kurmalıdır.

## Geçici dosyadan hedef dosyaya geçiş

Doğrudan hedef dosyaya yazılan bir süreç şu ara durumları dışarı açar:

0 baytlık dosya
kısmen yazılmış JSON
tamamlanmamış UTF-8 dizisi
kapanmamış JSON nesnesi
eski dosyanın üzerine kısmi yazım

Okuyucu dosyayı yazım sürerken açarsa geçerli bir dizin girdisi bulur, fakat içerik henüz tamamlanmamıştır. Dosya boyutu denetimi de yeterli değildir. Yazım ilerledikçe boyut değişebilir ve belirli bir anda makul görünen içerik yine eksik olabilir.

Geçici dosya kullanıldığında hedef ad yalnızca içerik tamamlandıktan sonra yayımlanır:

report.json.tmp
 ↓
report.json

Linux "rename" işlemi başarılı olduğunda hedef adın değiştirilmesini atomik olarak gerçekleştirir. Hedef zaten varsa işlem sırasında başka bir sürecin hedef adı bulamadığı bir boşluk oluşmaz. Kaynak ve hedef farklı bağlı dosya sistemlerindeyse işlem "EXDEV" hatası verebilir. Bu nedenle geçici dosyanın hedef dosyayla aynı dizinde oluşturulması, aynı dosya sistemi üzerinde kalmayı sağlayan en sade yöntemdir.

Java tarafında karşılığı şöyledir:

```java
final Path temporary = target.resolveSibling(target.getFileName() + ".tmp");
write(temporary, content);
Files.move(
    temporary,
    target,
    StandardCopyOption.ATOMIC_MOVE,
    StandardCopyOption.REPLACE_EXISTING
);
```

"ATOMIC_MOVE", taşımanın dosya sistemi tarafından tek bir atomik işlem olarak yapılmasını ister. Sağlayıcı bunu desteklemiyorsa "AtomicMoveNotSupportedException" üretir. Atomik seçenek kullanılmadan yapılan taşıma hata verirse kaynak ve hedef dosyanın son durumu tanımlı olmayabilir. Java API bu nedenle atomik taşımanın sessizce varsayılmasına izin vermez.

Güvenilir bir uygulama "ATOMIC_MOVE" başarısız olduğunda doğrudan normal taşımaya geçmemelidir. Böyle bir geri dönüş, hedef dosyanın tamamlanmış çıktı anlamına geldiği sözleşmeyi bozar. Atomik taşıma desteklenmiyorsa işlem başarısız sayılabilir veya ayrı bir commit protokolü kullanılabilir.

## Atomiklik kalıcılık değildir

Geçici dosyanın hedef ada atomik biçimde taşınması, hedef adın işlem sırasında tutarlı görünmesini sağlar. Verinin fiziksel depolamaya ulaştığını tek başına garanti etmez.

İşletim sistemi yazılan veriyi önce sayfa önbelleğinde tutabilir:

uygulama
→ işletim sistemi önbelleği
→ dosya sistemi
→ depolama denetleyicisi
→ kalıcı ortam

"write" çağrısının tamamlanması, bu zincirin sonuna ulaşıldığı anlamına gelmez. "close" da her ortamda kalıcı depolama garantisi olarak yorumlanmamalıdır.

Linux "fsync", dosyanın değiştirilen verisini ve ilişkili üstveri bilgisini depolama aygıtına aktarmayı ister. Ancak dosyaya uygulanan "fsync", dosyanın dizin girdisinin de kalıcı hale geldiğini garanti etmez. Yeni dosya oluşturma veya "rename" sonrasında dizin girdisinin korunması için üst dizinin de ayrıca senkronize edilmesi gerekir.

Yerel bir dosya sistemi için güçlü yayın sırası şu şekilde kurulabilir:

1. Geçici dosyayı oluştur
2. İçeriği yaz
3. Geçici dosyayı fsync et
4. Geçici dosyayı hedef ada atomik taşı
5. Üst dizini fsync et

Üçüncü adım dosya içeriğini korur. Beşinci adım yeni hedef adın veya değişen dizin girdisinin çökme sonrasında kaybolma riskini azaltır.

Java "FileChannel.force(true)" dosya içeriği ve üstveri için senkronizasyon isteği gönderebilir. Dizin senkronizasyonu ise standart Java API içinde bütün platformlarda aynı davranışı garanti eden taşınabilir bir işlem değildir. Linux üzerinde dizini "FileChannel" ile açma gibi yöntemler kullanılabilse de bunların geçerliliği dosya sistemi sağlayıcısına ve işletim sistemine bağlıdır. Uygulamanın gerektirdiği garanti, yalnızca Java kodundan değil, hedef platformdaki gerçek dosya sistemi davranışından doğrulanmalıdır.

NFS üzerinde kalıcılık daha fazla katman içerir. NFS protokolü istemci ve sunucu arasındaki yazma işlemlerinde verinin kararlı depolamaya ulaşıp ulaşmadığını ayrıca ele alır. NFSv3, "UNSTABLE" yazımların daha sonra "COMMIT" işlemiyle kararlı depolamaya alınmasına izin verir. NFSv4 tarafında "FILE_SYNC4", sunucunun veri ile gerekli üstveri bilgisini kararlı depolamaya aktarmadan başarılı cevap vermemesini gerektirir.

Uygulama çoğu zaman bu protokol ayrıntılarını doğrudan yönetmez. NFS istemcisi, çekirdek, mount seçenekleri ve sunucu birlikte davranır. "fsync" çağrısı bu katmanlara kalıcılık talebini iletir, fakat gerçek garanti sunucu uygulamasına ve depolama altyapısına da bağlıdır.

Sunucunun yazma önbelleği, RAID denetleyicisi veya sanal depolama katmanı veriyi kalıcı kabul edip gerçekte uçucu bellekte tutuyorsa uygulama düzeyindeki çağrılar tek başına elektrik kesintisine karşı koruma sağlayamaz. Kalıcılık sözleşmesi altyapı seviyesinde de doğrulanmalıdır.

## NFS istemcilerinde görünürlük

Bir istemci hedef dosyayı yayımladıktan sonra başka bir istemcinin dosyayı ne zaman göreceği, yalnızca "rename" atomikliğine bağlı değildir. NFS istemcileri veri ve dosya özniteliklerini yerel olarak önbelleğe alır.

Linux NFS istemcisinin varsayılan davranışı close-to-open consistency olarak adlandırılır. Yazıcı dosyayı kapatırken bekleyen değişiklikleri sunucuya gönderir. Başka bir istemci dosyayı açarken önbelleğindeki özniteliklerin güncelliğini denetleme fırsatı bulur. Bu model, bir istemcinin kapattığı dosyanın sonraki açılışta diğer istemci tarafından görülmesini hedefler. Yine de istemcilerdeki öznitelik önbellekleri tam anlamıyla eş zamanlı değildir ve bazı koşullarda eski veri görülebilir.

NFSv4, "RENAME" işleminin istemci açısından atomik olmasını ister. Okuyucu eski adı veya yeni adı görebilir, fakat protokol düzeyinde yarım bir ad değişimi gözlemlememelidir. Bu garanti, farklı istemcilerin dizin önbelleklerini aynı anda yenileyeceği anlamına gelmez.

Bir okuyucu dosyayı sürekli açık tutuyorsa yeni hedef dosyanın içeriğine otomatik olarak geçmesi de beklenmemelidir. "rename", mevcut dosya adını yeni inode veya dosya nesnesiyle ilişkilendirir. Önceden açılmış dosya tanıtıcısı eski nesneyi okumayı sürdürebilir. Yeni sürümü görmek isteyen okuyucu dosyayı yeniden açmalıdır.

Güvenli tüketici akışı şu biçimde kurulabilir:

dizin veya manifest değişikliğini algıla
hedef dosyayı yeniden aç
içeriği baştan sona oku
dosyayı kapat

Dosyayı sürekli açık tutup aynı tanıtıcı üzerinden yeni yayınları izlemek, atomik değiştirme modeline uygun değildir.

Dizin listeleme de ayrı bir önbellek davranışı gösterebilir. Bir istemci kısa süre önce bulunmayan bir dosyayı aradıysa negatif lookup sonucu önbellekte kalabilir. Attribute önbellek sürelerini azaltmak görünürlüğü hızlandırabilir, fakat sunucuya gönderilen üstveri çağrılarını artırır. "noac" veya "actimeo=0" gibi seçenekler performans maliyeti taşır ve bütün tutarlılık sorunlarını genel biçimde çözmez. Linux NFS belgeleri, öznitelik önbelleğinin performans ile güncellik arasında denge kurduğunu açıkça belirtir.

Bu nedenle mount ayarını değiştirmeden önce uygulama protokolünü düzeltmek daha güvenlidir. Okuyucu, tamamlanmış dosyanın biraz gecikmeli görünmesini tolere edebiliyorsa varsayılan önbellek davranışı daha yüksek işlem hacmi sağlar. Milisaniye düzeyinde anlık çapraz istemci görünürlüğü gerekiyorsa NFS dizin taraması uygun bildirim mekanizması olmayabilir.

## Commit dosyası ve manifest kullanımı

Tek veri dosyasının varlığı tamamlanmış yayın anlamına gelecekse atomik taşıma yeterli bir commit noktası olabilir:

daily.json.tmp
→ daily.json

Bir yayın birden fazla dosyadan oluşuyorsa sorun değişir:

schema.json
data.json
summary.json

Bu dosyalar ayrı ayrı atomik taşınsa bile okuyucu farklı sürümleri birlikte görebilir:

yeni schema.json
eski data.json
yeni summary.json

Dosya başına atomiklik, dosya kümesi için transaction sağlamaz.

Çözüm, sürümlü dizin veya manifest kullanmaktır:

/releases/104/schema.json
/releases/104/data.json
/releases/104/summary.json
/current.json

Yazar önce "104" sürümüne ait bütün dosyaları oluşturur. Dosyalar tamamlandıktan sonra küçük bir manifest yayımlar:

{
 "version": 104,
 "path": "/releases/104"
}

Okuyucu önce "current.json" dosyasını açar, ardından belirtilen sürüm dizinindeki dosyaları okur. Commit noktası yalnızca manifestin atomik değişimidir.

Bu model iki yarar sağlar. Birincisi, çoklu dosya yayını tek sürüm anahtarıyla tutarlı hale gelir. İkincisi, eski sürüm hemen silinmek zorunda kalmaz. Manifesti daha önce okuyan istemciler eski dizinden okumayı tamamlayabilir.

Eski sürümlerin temizlenmesi için güvenli bir bekleme süresi veya okuyucu referans modeli gerekir:

yayınla
manifesti değiştir
okuyucuların geçiş süresini bekle
eski sürümü temizle

Ayrı ".commit" dosyası da kullanılabilir:

data.json
data.json.commit

Okuyucu yalnızca iki dosya da varsa veriyi tamamlanmış sayar. Fakat commit dosyasının oluşturulması da aynı atomiklik ve görünürlük kurallarına tabidir. Çoklu dosya yapısında manifest, her dosya için ayrı commit işareti kullanmaktan daha açık bir sürüm sözleşmesi oluşturur.

## Çökme senaryoları

Yayın protokolü normal akıştan çok, her adımda oluşabilecek kesintiler üzerinden değerlendirilmelidir.

Geçici dosya yazılırken süreç kapanırsa hedef dosya değişmez. Kalan ".tmp" dosyası sonraki başlangıçta temizlenebilir veya üzerine yazılabilir.

Geçici dosya tamamlandıktan sonra taşıma öncesinde süreç kapanırsa yine eski hedef geçerlidir. Yeni veri yayımlanmamıştır.

Atomik taşıma tamamlandıktan sonra süreç kapanırsa okuyucular yeni hedefi görür. Ancak dosya ve dizin kalıcılığı sağlanmadıysa sunucu çökmesi sonrasında yeni adın veya içeriğin kaybolma ihtimali kullanılan dosya sistemi ve NFS sunucusunun davranışına bağlıdır.

Manifest değiştikten sonra veri dosyaları silinirse yayın bozulur. Bu nedenle yeni sürümdeki bütün dosyalar manifestten önce hazırlanmalı, eski sürüm ise manifest değişiminden hemen sonra kaldırılmamalıdır.

Aynı hedefe birden fazla yazıcı yayın yapıyorsa atomik "rename" veri yarışını ortadan kaldırmaz. Son taşıma yapan yazıcı hedef adı kazanır:

writer A → version 104
writer B → version 105
writer A rename
writer B rename

Sonuç "105" olabilir. Zamanlama ters dönerse eski "104" sürümü yeni sürümün üzerine yazılabilir. Atomiklik yalnızca her ad değişiminin bölünmez olduğunu garanti eder. Sürüm sırasını garanti etmez.

Birden fazla yazıcı varsa yayın şu yöntemlerden biriyle tekilleştirilmelidir:

- Tek aktif üretici
- Dış kilit veya [lease](/wiki/lease)
- Artan sürüm ve compare-and-set benzeri manifest güncellemesi
- Her üreticinin ayrı sürüm dizinine yazması ve merkezi yayın kararının tek noktadan verilmesi

NFS dosya kilitleri kullanılabilir, fakat kilit kaybı, istemci çökmesi ve ağ bölünmesi senaryoları ayrıca ele alınmalıdır. Yalnızca hedef dosyayı atomik taşımak çoklu yazıcı koordinasyonu sağlamaz.

## Uygulanabilir yayın sözleşmesi

NFS üzerinde günlük JSON veya istatistik dosyası yayımlayan tek yazıcılı bir servis için sade sözleşme şu şekilde kurulabilir:

1. Geçici dosyayı hedefle aynı dizinde oluştur
2. İçeriği tam olarak yaz
3. Yazma ve kapatma hatalarını kontrol et
4. Gereken kalıcılık düzeyi için dosyayı senkronize et
5. ATOMIC_MOVE ile hedef ada taşı
6. Atomik taşıma desteklenmiyorsa yayını başarısız say
7. Okuyucuların dosyayı her sürümde yeniden açmasını sağla
8. Çoklu dosya yayınında sürümlü manifest kullan
9. Eski sürümleri gecikmeli temizle
10. Geçici dosyaları yeniden başlangıçta uzlaştır

Dosyanın varlığı işlenmiş bölüm göstergesi olarak kullanılacaksa hedef ad yalnızca başarılı commit sonrasında oluşmalıdır. ".tmp" dosyasının bulunması tamamlanmış çıktı sayılmamalıdır.

Okuyucu tarafında parse hatası görülürse bunu otomatik olarak yazarın kısmi dosya yayımladığı şeklinde yorumlamak da doğru değildir. Eski istemci önbelleği, yanlış sürüm manifesti, hatalı karakter kodlaması veya bozuk kaynak veri aynı belirtiyi üretebilir. Yayın sürümü, dosya boyutu ve içerik özeti kayıt altına alınırsa hata kaynağı daha kolay ayrılır:

version
size
sha256
publishedAt
schemaVersion

Hash değeri güvenlik doğrulaması için zorunlu olmayabilir. Yine de aktarım veya depolama bozulmasını tespit eden sabit bir bütünlük ölçüsü sağlar.

NFS üzerinde atomik dosya yayını tek bir "Files.move" çağrısından ibaret değildir. "rename" okuyucuya yarım dosya göstermeme problemini çözer. "fsync" ve NFS commit semantiği çökme sonrası kalıcılıkla ilgilenir. Önbellek politikası ise başka istemcilerin yeni sürümü ne zaman göreceğini belirler.

Atomik yayınlamada hedef, dosyanın “yazılmış olması” değil, okuyucunun yalnız iki kararlı durumdan birini görmesidir: eski sürüm veya tamamlanmış yeni sürüm. Geçici ad, kalıcılık sırası ve aynı dosya sistemi üzerindeki atomik yeniden adlandırma bu görünürlük sözleşmesinin parçalarıdır.

## İlgili Güvenilirlik Sınırları

Atomik yayınlama, güvenilir dosya işleme sözleşmesinin yalnız bir parçasıdır. Üretici tarafında bounded retry ve idempotent iş semantiği; tüketici tarafında ise kapasite aşıldığında backpressure gerekebilir.

İlgili kavramlar:

- [İdempotentlik](/wiki/idempotency)
- [Geri basınç](/wiki/backpressure)
- [Kritik Sistemlerde Güvenli Retry Tasarımı](/kritik-sistemlerde-guvenli-retry-tasarimi)
- [İşletim Sistemleri: Süreç, Bellek, Dosya ve Giriş Çıkış](/isletim-sistemleri-surec-bellek-dosya-giris-cikis)

Bu garantileri ayrı tutmak, atomik bir namespace işlemini yanlışlıkla kalıcılık, teslim garantisi veya exactly-once işlem gibi yorumlamayı engeller.

## Yeniden Başlatma ve Kalıcı Görünürlük

Atomik görünürlük, tekrar yürütmenin yan etkisini ve kesinti sonrası ilerlemenin nasıl sürdürüleceğini tek başına çözmez: [retry/idempotency](/kritik-sistemlerde-guvenli-retry-tasarimi), [backfill](/tarih-bolumlu-verilerde-dayanikli-backfill).

## Garanti–Kaynak Eşlemesi

Bu yazının temel iddiaları aynı kaynaktan gelmiyor. Dosya adının atomik değiştirilmesine ilişkin `rename()` semantiği ve kalıcılık için kullanılan `fsync()` ailesi POSIX/Open Group sözleşmesiyle ilgilidir. NFS tarafındaki davranış ise ayrı bir dağıtık dosya sistemi protokolüdür ve [RFC 1813](https://www.rfc-editor.org/rfc/rfc1813.html) kendi işlem/commit semantiğini tanımlar. Bu yüzden “rename atomiktir” cümlesinden doğrudan “veri güç kaybında kalıcıdır” veya “bütün istemciler aynı anda yeni adı görür” sonucu çıkarılamaz.

Yazıdaki crash senaryoları ve yayınlama sırası bu standartlardan türetilen mühendislik analizidir; belirli bir NFS sunucusu, mount seçeneği veya dosya sisteminin bütün uygulama ayrıntılarını temsil ettiği iddia edilmez. Üretimde aynı sözleşme; hedef işletim sistemi, dosya sistemi, NFS sürümü ve hata modeli altında yeniden sınanmalıdır.

## Atomiklikten Sonraki Sınır: Dayanıklılık

`rename` okuyucuların eski veya yeni adı görmesini atomik hale getirebilir; ancak güç kaybı sonrasında yeni dosyanın ve dizin girdisinin kalacağını tek başına garanti etmez. Dosya içeriğinin fsync edilmesi ile parent directory'nin fsync edilmesi farklı durability noktalarıdır. Filesystem journaling ve cihaz write cache davranışı da bu zincirin parçasıdır.

Bu ayrımın crash-consistency boyutunu [Kalıcı Yazmada Dayanıklılık Sınırları](/kalici-yazmada-dayaniklilik-sinirlari) başlığında fsync, directory fsync, journal, device cache ve NFS katmanları üzerinden ayrıca ele alıyorum.

## Kaynakça

- Brian Callaghan; Brian Pawlowski; Peter Staubach. (1995). NFS Version 3 Protocol Specification. RFC Editor. [doi:10.17487/RFC1813](https://doi.org/10.17487/RFC1813)

- The Open Group. (2018). The Open Group Base Specifications Issue 7, 2018 Edition - rename() and fsync(). The Open Group. [URL](https://pubs.opengroup.org/onlinepubs/9699919799/)

## Bu Çalışmaya Atıf

Köker, M. A. (2026). Dosya İşlemede Atomik Yayınlama. alikoker.com.tr. https://alikoker.com.tr/dosya-islemede-atomik-yayinlama

- BibTeX: https://alikoker.com.tr/dosya-islemede-atomik-yayinlama.bib
- RIS: https://alikoker.com.tr/dosya-islemede-atomik-yayinlama.ris
- CSL-JSON: https://alikoker.com.tr/dosya-islemede-atomik-yayinlama.csl.json
