5 puan yazan GN⁺ 2023-07-23 | 1 yorum | WhatsApp'ta paylaş
  • Birden çok fiziksel çekirdeğin aynı anda saati okuduğu modern sistemlerde nanosaniye zaman damgaları bile kolayca çakışır; 4 fiziksel çekirdekte eşzamanlı ölçümde toplam örneklerin yaklaşık %5’i çakışır
  • Ham zaman damgalarını benzersiz tanımlayıcı gibi kullanan tasarımlar risklidir ve çakışma sıklığı işletim sistemi ile çalıştırma biçimine göre değişir
  • Go’nun time.Now() fonksiyonu mutlak zamanı ve monoton saat tabanlı göreli zamanı birlikte kaydeder; böylece ardışık çağrılar arasındaki fark ile mutlak zaman damgası tekrarları ayrı ayrı incelenebilir
  • Linux’ta tek iş parçacığında zaman her zaman arttı ve en küçük artış aralığı 32ns idi; ancak iş parçacıkları ayrıldığında aynı mutlak zaman gözlemlendi
  • Mac OS X’te mutlak zamanın mikrosaniye çözünürlüğünde olması nedeniyle çakışmalar çok daha fazladır ve tek iş parçacığında bile monoton saatin artmadığı durumlar sıkça görülür

Eşzamanlı okumalarda ortaya çıkan çakışma sıklığı

  • Temel soru, modern sistemlerde nanosaniye zaman damgası çakışmalarının gerçekte ne kadar sık meydana geldiğidir
  • 4 fiziksel çekirdekte aynı anda saat okunursa toplam örneklerin yaklaşık %5’i çakışır
  • 4 çekirdekli bir sistemde yalnızca 2 iş parçacığı kullanılsa bile zaman damgalarının yaklaşık %2’si çakışır
  • Bu nedenle yalnızca ham nanosaniye zaman damgalarıyla benzersiz ID üretilebileceğini varsaymak güvenli değildir

Test yöntemi ve işletim sistemine göre farklar

  • Test programı Go ile yazılmıştır
  • Go’nun time.Now() fonksiyonu her çağrıda mutlak zaman ile monoton saat tabanlı göreli zamanı kaydeder
    • Test, ardışık zaman damgaları arasındaki göreli farkı karşılaştırır
    • Mutlak zaman damgasının kendisindeki tekrarlar da birlikte kontrol edilir
  • Linux

    • Tek iş parçacığında mutlak zaman ve monoton zaman her zaman artar
    • Ölçüm yapılan sistemde en küçük artış aralığı 32ns idi
    • İş parçacıkları arasında mutlak zamanın başka bir iş parçacığıyla tamamen aynı olduğu durumlar yaklaşık %5 oranında görüldü
  • Mac OS X

    • Mutlak zaman mikrosaniye çözünürlüğünde olduğu için aynı testte çok sayıda çakışma meydana gelir
    • Tek iş parçacığında bile monoton saatin artmadığı durumlar sıkça gözlemlenir

1 yorum

 
GN⁺ 2023-07-23
Hacker News yorumları
  • Bu tür sorunlardan kaçınmanın yolu, zaman bileşeniyle sıra numarasını birlikte içeren bir ID kullanmaktır
    Örneğin UUIDv7’de milisaniye düzeyinde bir zaman bileşeni, aynı milisaniye içindeki her olay için artan bir alan ve farklı makinelerde üretilen ID’ler arasında çakışma olasılığını astronomik ölçüde düşürecek kadar rastgele bit bulunur
    Elbette bit sayısı sonlu olduğundan aynı zaman aralığında çok fazla olay olursa sıra numarası taşabilir, makineler arası çakışmalar gerçekten yaşanabilir ve artırma işlemi CPU senkronizasyonu gerektirdiği için olay üretim hızı sınırlanabilir
    Yine de pratik ölçekte UUIDv7 çok iyi çalışır

    • Biraz tesadüfen zaman yolcusu olmuşum gibi hissediyorum; en az 10 yıl önce bir teknoloji buluşmasında birinin milisaniye başına 1000’den fazla UUID üretirken benzersizlik sorunları yaşadığını ve o zamanki seçeneklerden memnun olmadığını anlattığı bir konuşmayı hatırlıyorum
      UUIDv7’nin ne kadar eski olduğunu internette pek bulamıyorum
    • En başta zaman bileşenine neden ihtiyaç olduğunu anlamıyorum
      UUID içindeki bitleri tüketmekten başka bir işe yaramıyor ve entropiye pek katkı sağlamıyor
    • PostgreSQL gibi popüler veritabanlarında sıralama düzeniyle de iyi uyum sağlar
      Henüz çekirdeğe girmiş değil, ancak uygulama düzeyinde doğrudan kullanmanın yanı sıra uuidv7 sağlayan birkaç harika pg eklentisi var
    • UUID’nin sorunu tamamen okunamaz olmasıdır
      Anlaşılması zor olmanın ötesinde, gözle birbirinden ayırt etmek bile çok güçtür
      Bu yüzden bazı durumlarda, gereken asgari bilgi dışında hiçbir bilgi veya gürültü içermeyen bir tanımlayıcı kullanışlıdır
    • Kullanım senaryosuna bağlı olarak “aynı milisaniye” durumunu ele almaya bile gerek kalmadan birkaç döngü tasarruf edilebilir
      Herhangi bir biçimde taşan bir artırma sayacı ve biraz rastgele bit çoğu zaman yeterlidir; iyi tasarlanırsa ikisi de dallanma olmadan yapılabilir
  • Bununla bağlantılı olarak, eskiden Windows’un güvenlik olay günlüğünden sorumlu program yöneticisiydim
    Çok çekirdekli sistemlerde işler aynı anda ya da birbirine çok yakın zamanlarda gerçekleştiğinde, iş parçacığı zamanlaması gözlemlenen sonuçları büyük ölçüde etkileyebilir
    Örneğin iş parçacığının zaman dilimi, zaman damgasını alan sistem çağrısına ulaşmadan önce ya da daha sonra zaman damgası basmak üzere olayı kuyruğa koymak için tamponu teslim etmeden önce sona erebilir
    Gerçekten de 2000’lerde Windows’un çok işlemcili sistemlerinde olay günlüğü girdilerinin sırası değişmiş gibi görünmesi çok yaygındı ve günlük zaman damgası doğruluğuna da fazla hassas biçimde güvenilemezdi
    Güvenli alt sınır fiilen 1 saniyeydi; bazı bileşenlerin zaman damgalarını kestiğini ya da yuvarladığını hatırlıyorum

  • Benzersiz bir tanımlayıcı gerekiyorsa sürüm 4 UUID, yani rastgele UUID kullanın
    Çakışma olasılığı, kuantum dalgalanması yüzünden yetişkin bir dinozorun yatak odanızda aniden belirme olasılığına benzer

    • Bu riski göze alırım
      Biraz daha ciddi konuşursak, kullanılabiliyorsa eski usul artan değer muhtemelen en iyisidir
      Hızlı ve ucuzdur; özellikle veritabanlarında böyledir, ancak ID değerinden bilgi çıkarılabilmesi gibi gizlilik ve güvenlik sorunları vardır
      Bu tür durumlarda ya da dağıtık sistemlerle uğraşırken UUID daha iyidir
    • v7, v4’ün yerellik sorununu çözerken çakışma yaratma olasılığından çok daha yüksek bir olasılıkla piyango kazanacağınız için daha iyi görünüyor
    • “Dinozorun yatak odanızda aniden belirme olasılığı” hesabının nasıl yapıldığını görmek isterim
    • O zaman kötü bir şey olma olasılığı yaklaşık 2 katına çıkmış demektir; bunu kabul edemem
  • Çözünürlük nanosaniye olsa bile, gerçek bilgisayar saatinin hassasiyetinin ne düzeyde olduğunu merak ediyorum
    Gerçekten nanosaniye düzeyinde olacağını hayal etmek zor; fizik laboratuvarı dersinde öğrencilere ölçüm cihazının gösterdiği en küçük sayının doğrulukla aynı şey olmadığını sürekli vurguladığım zamanları hatırlatıyor

    • 1GHz veya üzerinde çalışan bir cihazda saatin her nanosaniyede bir artması gayet mümkündür
      Ancak bu, o düzeyde doğru olduğu anlamına gelmez; çok çekirdekli sistemlerde çekirdekler arasındaki saatler o seviyede senkronize olmayabilir
      ARMv8, saatin en az 1GHz hızında arttığını garanti eder, ancak Intel ve önceki ARM daha karmaşıktır
    • Gerçekten nanosaniyedir
  • Erlang/Elixir’in BEAM VM’i bu farkı çok net biçimde ortaya koyar. Bu, monoton artış ile katı monoton artış arasındaki ayrımdır
    https://www.erlang.org/doc/apps/erts/time_correction.html#mo...
    “Monoton artan bir değerler dizisinde, kendinden önce bir değer bulunan her değer, o önceki değerden büyük ya da ona eşittir”
    Bu, https://www.erlang.org/doc/man/erlang.html#monotonic_time-0 fonksiyonuyla kullanılabilir
    https://www.erlang.org/doc/apps/erts/time_correction.html#st...
    “Katı biçimde monoton artan bir değerler dizisinde, kendinden önce bir değer bulunan her değer, o önceki değerden büyüktür”
    Katı monoton değerler bir tür senkronizasyon ya da koordinasyon anlamına gelir ve çok sayıda eşzamanlı süreç olduğunda performans maliyeti getirir
    Bu özellik https://www.erlang.org/doc/man/erlang.html#unique_integer-1 fonksiyonuyla sağlanır; belgelerde de katı biçimde monoton artan değerlerin doğası gereği üretim maliyetinin yüksek olduğu ve iyi ölçeklenmediği uyarısı yapılıp, monotonic değiştiricisinin yalnızca gerçekten gerektiğinde geçirilmesi söylenir

    • Erlang’in kendi referans değerleri de katı monoton bir küresel üreteç ile oluşturulmaz; dahili olarak genel monoton tanımlayıcı ile istekte bulunan sürecin PID çiftinden oluşur
      Başka bir deyişle UUIDv1’e ya da https://en.wikipedia.org/wiki/Snowflake_ID’ye benzer
      Katı monoton küresel tanımlayıcılara gerçekten yalnızca anında tutarlı ilk/son yazan kazanır semantiğine ihtiyaç duyulduğunda gerek vardır
      Bunun yerine nihai tutarlı ilk/son yazan kazanır yaklaşımını kullanabiliyorsanız; örneğin yazma olayları ID’ye göre doğrusallaştırılan bir event store’a ya da kuyruğa giriyor ve “eşzamanlı” yazmalar arasında yalnızca ID önceliği en yüksek olanı bırakıp diğerlerini işleme sırasında veya okuma anında atabiliyorsanız, önce sıkıştırılmış bir (nodeID, seq) çifti düşünürdüm
      Küresel olay sıralaması gerekiyorsa, özellikle Snowflake ID biçimindeki (timestampMajor, nodeID, timestampMinor, seq) dikkate alınmaya değer
  • FreeBSD’de CLOCK_MONOTONIC_RAW olmadığı için yorum satırına aldım; iyi görünüyor
    Çakışma varsa bazı zaman damgalarının tekrarlanması gerektiğini sanmıştım, ama çakışma oluşturamıyorum
    clock_getres(CLOCK_REALTIME, ...)=1 ns, clock_getres(CLOCK_MONOTONIC, ...)=1 ns olarak çıkıyor ve 30 örnekte de fark yaklaşık 29~71 ns aralığında sürekli artmaya devam etti

    • Yazarın yaptığı gibi 4 çekirdekte aynı anda çalıştırılıp çalıştırılmadığı önemli
  • Sonuçta bir noktada bunun komut kümesi mimarisi meselesine indiğini düşünüyorum
    3 GHz’de çalışan bir CPU, nanosaniye başına 3 saat döngüsü elde eder
    Derleyici optimizasyonuyla saat kaydını okuyan assembly çağrılarının arka arkaya gelmesi oldukça olası görünüyor
    Ardışık time.Now() çağrıları 3 saat döngüsü içinde gerçekleşiyorsa, gerçekten benzersiz nanosaniye hassasiyeti beklemek adil mi, emin değilim

    • Linux’ta x86_64 RDTSC kullanır ve VDSO’dan okunan değerle düzeltme yapar; bu yüzden gerçekten çok hızlı gerçekleşebilir
    • Modern çiplerde cycle counter register okumak bile yaklaşık 20 döngü sürer
      Çakışmalar biraz nadir olsa bile, günde birkaç kez meydana gelmesi “neredeyse asla olmaz”dan çok daha kötüdür
  • Lotus Notes ile ilgili efsane aklıma geldi
    Eskiden benzersiz ID olarak 1 saniye çözünürlüklü zaman damgaları kullanıldığı söylenir
    Çakışma olursa sadece 1 saniye eklenirmiş; sonunda çakışmalar o kadar çoğalmış ki öğeler gelecek zamanlara sahip olmaya başlamış

  • Mutlak olarak doğru zaman bir güvenlik sorunudur
    CPU tasarımcıları, tam öngörülebilirliği engellemek için DEC’in Alpha dönemindeki gibi çok eskiden beri kasıtlı olarak saat jitter’ı eklemiştir
    x86’da da 3~4 kez çalıştırıp değerleri bir register’a kaydettikten sonra en sonda raporlarsanız, zaman farklarının tam olarak aynı olmadığını görebileceğinizi düşünüyorum

    • Bunun için bir kaynak var mı merak ediyorum
      Aramada pek bulamadım; eğer bu ifade erken dönem x86’yı da kapsıyorsa, doğru saatin güvenlik sorunlarının bu kadar erken fark edilmiş olması şaşırtıcı
      Kişisel olarak bu milenyumdan önce böyle bir sorunun bilinmediğini, gözlenen saat jitter’ının da kesmeler gibi şeylerle açıklanacağını tahmin ederdim
      Yanlış demiyorum, sadece daha fazlasını öğrenmek istiyorum
  • Milisaniye ya da mikrosaniye zaman damgası çakışmalarına şaşıran çok fazla kişi gördüm
    En akılda kalan ve en sevmediğim tür, zaman damgasını iki sistem çağrısıyla birleştirme yöntemiydi
    Biri üst basamaklar, diğeri alt basamaklar için çağrılıyor; ama süreç preemption’ı nedeniyle üst basamak okunduktan sonra alt basamak 99x’ten 00x’e geçerse, bir varlığı oluşturan nedenden daha erken bir zamana ait zaman damgası üretebilirsiniz
    Bu durumda bazı kodlar oldukça gösterişli biçimde bozulur ve en az iki kez sonsuz döngü gördüm
    Bunu her zaman kaçınılması gereken bir şey olarak ezberlemezseniz testler %99,5 oranında geçer ve örüntü yakalama sezgisi çok iyi olan birinin “aynı test bir buçuk ay boyunca haftada bir kez kırmızıya döndü” durumunu fark etmesi gerekir
    CI/CD kodunun içinde bir mantık bombası düzeltilmeden önce yaşayamayacak kadar uzun bir süredir bu

    • En akılda kalan örnek, destek konuşmasında “bir race condition var gibi görünüyor” dediğimde, “o iki olay tam olarak aynı anda gerçekleşti, dolayısıyla race condition olamaz” diye cevap verilmesiydi