12 puan yazan GN⁺ 2025-05-27 | 1 yorum | WhatsApp'ta paylaş
  • Bash betiklerinde web sunucusu durum kontrolü için tekrarlı bağlantı denemeleri yapılırken, sunucu beklenmedik şekilde sonsuz döngüye girebilir
  • Bunu çözmek için kullanılan timeout, bir komut için yürütme süresi sınırı belirler ve süre aşılırsa sinyal göndererek süreci sonlandırmayı dener
  • until gibi shell built-in yapılarında doğrudan kullanılamaz; bunun yerine bash sürecini sarmalama veya betiği ayırma yöntemleriyle çözülebilir

Bash betiklerinde web sunucusunu bekleme ve sonsuz döngü sorunu

  • Gerçek iş ortamında Bash betikleriyle web sunucusu kurulumu ve durum kontrolü yapılıyor
  • Yapı, sunucu ayağa kalkana kadar sonraki işleri bekletiyor ve normalde sorunsuz çalışıyor
  • Ancak sunucu başlarken bir çökme yaşanırsa sonsuz döngüye girme durumu ortaya çıkıyor ve bunun çözülmesi gerekiyor

until kullanım örneği ve sınırları

  • Aşağıdaki gibi bir yapıyla web sunucusu health check işlemi tekrarlanıyor
    until curl --silent --fail-with-body 10.0.0.1:8080/health; do  
    	sleep 1  
    done  
    
  • Sunucu başarısız olduğunda sleep 1 sonsuza kadar tekrar ediyor

timeout yardımcı aracının devreye alınması

  • timeout komutu, belirlenen süre içinde bir komut tamamlanmazsa sonlandırmak için sinyal (SIGTERM vb.) gönderir
  • Örnek: timeout 1s sleep 5 durumunda, 1 saniye geçtikten sonra sleep sürecini sonlandırma girişiminde bulunur
  • Sonlandırıldığında normal dışı bir çıkış kodu döndürür (ör. 124)

timeout ile until birlikte kullanma denemesi ve sorunlar

  • Doğal olarak timeout ile until şu şekilde birlikte kullanılmak isteniyor
    timeout 1m until curl ...; do  
    	sleep 1  
    done  
    
  • Ancak timeout yalnızca süreçlere sinyal gönderebilir; until ise shell içindeki yerleşik bir anahtar sözcük olduğu için doğrudan uygulanamaz

Çözüm: Bash sürecini sarmalama veya harici betik kullanma

  • Tüm until döngüsünü bash -c ile sarıp ayrı bir süreçte çalıştırırsanız timeout uygulanabilir
    timeout 1m bash -c "until curl ...; do sleep 1; done"  
    
  • Ya da döngü kısmını harici bir Bash betiğine ayırıp, bu betiğe timeout uygulayabilirsiniz
    timeout 1m ./until.sh  
    
  • shell built-in yapılara doğrudan timeout uygulanamaz, ancak bu yöntemlerle istenen davranış elde edilebilir

1 yorum

 
GN⁺ 2025-05-27
Hacker News görüşleri
  • En sevdiğim az bilinen numaralardan biri, çeşitli sistem çağrısı hatalarını test etmek için strace fault injection kullanma yöntemi

    $ strace -e trace=clone -e fault=clone:error=EAGAIN
    

    İlgili bağlantıda daha ayrıntılı açıklanıyor.

    • Bu özelliğin gerçekten müthiş olduğu, bunu daha önce bilmiş olmayı isterdim diye bir deneyim paylaşılmış.
      Hata dallarını test etmenin bir yolu olmadığı için bazen fonksiyonun bir kısmını geçici kodla değiştiriyordum; bu numara sayesinde daha sade bir yaklaşım mümkün görünüyor.

    • Bunun gerçekten faydalı göründüğü söylenmiş.
      Windows'ta da benzer bir özellik olup olmadığı merak ediliyor.

  • Servis health check için en iyi yöntemin hem azami zaman aşımı süresini hem de azami yeniden deneme sayısını birlikte ayarlamak olduğu öneriliyor.
    Genelde en fazla X kez yeniden denenir ve en fazla Y süre içinde başarısız kabul edilir.
    Çok uzun süre beklemektense mümkün olduğunca hızlı şekilde başarısız kararı vermek gerektiği vurgulanıyor.
    Standart servislerde health check, ancak container bağımlılıkları yeterince sağlanıp gerçekten çalışmaya hazır olduktan sonra başlamalı.
    Kubernetes'te Init Container, AWS ECS'te dependsOn, Docker Compose'ta depends_on ayarlarına bakılabilir.
    POSIX shell script örneği verilmiş.
    Ancak curl içinde bu işlev zaten yer aldığı için ayrıca script yazmadan aşağıdaki gibi kullanılabileceği belirtiliyor.

    curl --silent --fail-with-body --connect-timeout 5 --retry-all-errors --retry-delay 1 --retry-max-time 300 --retry 300 10.0.0.1:8080/health
    
  • Mac'te timeout komutu varsayılan olarak gelmediği için yalnızca bash built-in'leriyle zaman aşımı uygulamaya çalıştığı deneyimini paylaşmış.
    sleep komutunun POSIX standardında yer aldığı, dolayısıyla kullanılabildiği açıklanıyor.
    Aşağıdaki gibi zaman aşımı işlevi uygulama örneği verilmiş.

    # TIMEOUT SYSTEM(özet)
    # function timeout <num_seconds> <command>
    # belli bir süre geçince <command> tetiklenir
    

    times_up adlı bir fonksiyonla zaman aşımı ele alınıyor.
    10 saniyelik zaman aşımıyla for döngüsünün 20 kez çalıştırıldığı bir test örneği de verilmiş.

    • 12 yıl önce Stack Overflow tavsiyesine uyarak benzer bir yöntem uyguladığını paylaşmış.
      Referans bağlantısında ayrıntılar görülebilir.
      Yalnızca shell built-in'leri ve sleep kullanıldığı, ayrıca kodun POSIX uyumlu olmasının zorunlu olduğu vurgulanıyor.
      Örnekteki bash {1..20} sözdiziminin POSIX olmadığı, bu yüzden dikkat edilmesi gerektiği belirtiliyor.
      Kendi iyileştirmesinin, zaman aşımı olmazsa true, olursa false döndürmek olduğu; böylece script içinde hata işlemenin kolaylaştığı anlatılıyor.

    • Aşağıdaki gibi komut ile sleep'i paralel çalıştırıp, belirtilen sürenin sonunda sinyalle komutu sonlandıran çok basit bir yöntem de paylaşılmış.

      <command> & sleep <timeout>; kill -SIGALRM %1
      
    • 13 yıl önce read -t kullanarak zaman aşımı uygulayan bir script örneği paylaşılmış.
      Bağlantı

  • curl içinde zaten --retry-connrefused bayrağı bulunduğu, bu yüzden shell döngüsü kurmadan bu özelliğin doğrudan kullanılabileceği belirtiliyor.

  • bash -c kullanırken değişken aktarmak gerekiyorsa aşağıdaki gibi argüman ekleme yöntemi öneriliyor.

    bash -c 'some command "$1" "$2"' -- "$var1" "$var2"
    

    "--" kullanılma nedeni ve argv[0]'ın rolü açıklanıyor.
    printf %q de kullanılabilir ama Bourne uyumlu yaklaşımın tercih edildiği söyleniyor.

    • "--" işaretinin bash ve çoğu Unix/Linux CLI ortamında seçeneklerin bittiğini bildiren çok açık bir anlam taşıdığı açıklanıyor.
      İlgili referans

    • Busybox'un argv[0] değerine göre hangi programı çalıştıracağını belirlediği, dolayısıyla "ls", "mv", "cp" gibi istenen komutun atanabileceği paylaşılmış.

  • Yeniden deneme mantığı gerektiğinde genelde aşağıdaki yöntemi kullandığını söylüyor.

    for i in {0..60}; do
      true -- "$i"
      if eventually_succeeds; then break; fi
      sleep 1s
    done
    

    Çok şık olmasa da çoğu zaman doğru çalıştığı, daha ileri aşamada üstel backoff uygulanabileceği belirtiliyor.
    Ölçeklenebilirlik açısından da avantaj sağladığı söyleniyor.

    • shellcheck'in bu durumda _ değişkenini kullanmayı önerdiği belirtilmiş.
      Referans bağlantısı

    • eventually_succeeds fonksiyonunun duruma göre timeout ya da ek savunmacı kodlama gerektirebileceği vurgulanıyor.
      POSIX/process/IO alanlarında her zaman savunmacı kod yazma gereği hatırlatılıyor.

  • Geçmişte çocukları küçükken, 30 dakika boyunca yalnızca bir program izleyebilmeleri için aşağıdaki komutu bir tür ebeveyn kontrol aracı olarak kullandığını paylaşmış.

    timeout 1800 mplayer show.mp4 ; sudo pm-suspend
    

    Fikrin son derece kullanışlı olduğu söyleniyor.

    • Bunun kullanım amacını en havalı şekilde açıklayan örnek olduğu yorumu eklenmiş.
  • Alt süreçlere sinyal göndermesi gerektiği için komutu satır içine yazmayı veya geçici script dosyası kullanmayı pek sevmediğini söylüyor.
    Tercih ettiği yöntem, istenen karmaşık mantığı bir fonksiyon haline getirip export etmek ve ardından bunu timeout bash -c ile sarmalamak.
    Bunun, aidenn0'un bahsettiği güvenli argüman aktarma yöntemiyle de ilişkili olduğu belirtiliyor.

    #!/usr/bin/env bash
    
    long_fn () { # istediğiniz mantığı uygulayın
     sleep $1
    }
    to () {
     local duration="$1"; shift
     local fn_name="$1"; shift
     export -f "$fn_name"
     timeout "$duration" bash -c "$fn_name"' "$@"' _ $@
    }
    
    time to 1s long_fn 5
    
    • Sonda "$@" ifadesinin mutlaka kullanılması gerektiği belirtiliyor.
      Aksi halde boşluk içeren argümanlar doğru şekilde iletilmiyor.
      Bunu doğrulamak için bir long_fn örneği de paylaşılmış.
  • Geçmişte timeout hakkında yazdığı bir blog gönderisini hatırlatıyor.
    İlgili blogda, shell yerine genel programlama dilleriyle ya da iç çalışma mantığıyla daha çok ilgilenenler için ek bilgi öneriliyor.

  • Kubernetes kurulumlarında komut zaman aşımı eklediği deneyimini paylaşıyor.
    await-cmd.sh, await-http.sh, await-tcp.sh gibi POSIX shell script'lerinin olgunlaştığı ve belli durumlarda oldukça faydalı olabildiği belirtiliyor.
    İlgili proje bağlantısı