12 poin oleh GN⁺ 2025-05-27 | 1 komentar | Bagikan ke WhatsApp
  • Saat melakukan percobaan koneksi berulang untuk memeriksa status server web dalam skrip Bash, bisa muncul masalah ketika server secara tak terduga masuk ke loop tak terbatas
  • timeout, alat untuk mengatasi hal ini, menetapkan batas waktu eksekusi perintah dan saat terlampaui akan mengirim sinyal untuk mencoba menghentikan proses
  • Karena tidak bisa diterapkan langsung pada shell built-in seperti until, hal ini dapat diatasi dengan membungkus proses bash atau memisahkan skrip

Menunggu server web di skrip Bash dan masalah loop tak terbatas

  • Dalam praktik, skrip Bash digunakan untuk menyiapkan server web dan memeriksa statusnya
  • Strukturnya menunda pekerjaan berikutnya sampai server aktif, dan pada dasarnya berjalan tanpa masalah
  • Namun, jika terjadi crash saat server sedang memulai, skrip bisa masuk ke loop tak terbatas sehingga perlu solusi

Contoh penggunaan until dan keterbatasannya

  • Pengecekan health server web diulang dengan sintaks seperti berikut
    until curl --silent --fail-with-body 10.0.0.1:8080/health; do  
    	sleep 1  
    done  
    
  • Saat server gagal, kondisi sleep 1 berulang selamanya dapat terjadi

Pengenalan utilitas timeout

  • Perintah timeout akan menghentikan perintah dengan mengirim sinyal (SIGTERM dan sebagainya) jika tidak selesai dalam waktu yang ditentukan
  • Contoh: pada timeout 1s sleep 5, setelah 1 detik akan dicoba menghentikan proses sleep
  • Saat dihentikan, perintah mengembalikan kode keluar abnormal (misalnya 124)

Upaya menggabungkan timeout dan until serta masalahnya

  • Secara alami muncul upaya menggabungkan timeout dan until seperti di bawah ini
    timeout 1m until curl ...; do  
    	sleep 1  
    done  
    
  • Namun timeout dapat mengirim sinyal ke proses, sedangkan until adalah kata kunci bawaan shell sehingga tidak bisa diterapkan langsung

Solusi: membungkus proses Bash atau memakai skrip eksternal

  • Jika seluruh loop until dibungkus dengan bash -c lalu dijalankan sebagai proses terpisah, timeout bisa diterapkan
    timeout 1m bash -c "until curl ...; do sleep 1; done"  
    
  • Atau bagian loop dipisahkan ke skrip Bash eksternal, lalu timeout diterapkan ke skrip tersebut
    timeout 1m ./until.sh  
    
  • Meskipun timeout tidak bisa diterapkan langsung ke shell built-in, perilaku yang diinginkan tetap bisa dicapai dengan cara di atas

1 komentar

 
GN⁺ 2025-05-27
Komentar Hacker News
  • Trik yang paling saya sukai dan kurang dikenal adalah penggunaan fault injection strace untuk menguji kegagalan berbagai system call

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

    Penjelasan lebih rinci tersedia di tautan terkait

    • Ada yang berbagi pengalaman bahwa fitur ini benar-benar menakjubkan dan berharap sudah mengetahuinya sejak dulu
      Karena tidak ada cara untuk menguji cabang kegagalan, ia biasa mengganti sebagian fungsi dengan kode sementara, tetapi dengan trik ini pendekatannya bisa jauh lebih ringkas

    • Ada pendapat bahwa cara ini tampak sangat berguna
      Juga muncul rasa penasaran apakah ada fitur serupa di Windows

  • Untuk health check layanan, ada usulan bahwa cara terbaik adalah menetapkan baik waktu timeout maksimum maupun jumlah retry maksimum
    Biasanya mencoba retry sampai X kali, lalu memutuskan gagal jika belum berhasil dalam maksimal Y waktu
    Ditekankan perlunya memutuskan kegagalan secepat mungkin alih-alih menunggu terlalu lama
    Pada layanan yang standar, health check baru dimulai setelah dependensi kontainer benar-benar terjamin dan siap beroperasi
    Di Kubernetes lihat Init Container, di AWS ECS lihat dependsOn, dan di Docker Compose lihat pengaturan depends_on
    Disertakan contoh skrip shell POSIX
    Namun disebutkan bahwa curl sendiri sudah memiliki fitur seperti ini, jadi bisa dipakai tanpa skrip tambahan seperti di bawah

    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
    
  • Ada yang berbagi pengalaman mencoba berbagai cara untuk membuat timeout hanya dengan builtin bash karena perintah timeout tidak tersedia secara bawaan di Mac
    Dijelaskan bahwa perintah sleep adalah standar POSIX sehingga bisa digunakan
    Disertakan contoh implementasi fitur timeout seperti berikut

    # TIMEOUT SYSTEM(ringkasan)
    # function timeout <num_seconds> <command>
    # memicu <command> setelah waktu tertentu berlalu
    

    Timeout ditangani dengan fungsi bernama times_up
    Juga diberikan contoh pengujian dengan timeout 10 detik dan perulangan for sebanyak 20 kali

    • Ada yang berbagi pengalaman pernah membuat cara serupa 12 tahun lalu mengikuti saran di Stack Overflow
      Detailnya bisa dilihat di tautan referensi
      Ditekankan bahwa hanya shell builtins dan sleep yang digunakan, dan kode tersebut wajib kompatibel dengan POSIX
      Disebutkan bahwa sintaks bash {1..20} pada contoh bukan bagian dari POSIX sehingga perlu diperhatikan
      Perbaikan yang ia buat adalah mengembalikan true jika timeout tidak terjadi, dan false jika timeout terjadi, sehingga penanganan error dalam skrip menjadi lebih sederhana

    • Dibagikan juga cara yang sangat sederhana seperti di bawah, yaitu menjalankan perintah dan sleep secara paralel lalu menghentikan perintah dengan sinyal setelah waktu yang ditentukan

      <command> & sleep <timeout>; kill -SIGALRM %1
      
    • Ada yang membagikan contoh skrip dari 13 tahun lalu yang mengimplementasikan timeout menggunakan read -t
      Tautan

  • Dijelaskan bahwa curl sudah memiliki flag --retry-connrefused, sehingga fungsi ini bisa langsung dimanfaatkan tanpa loop shell

  • Jika perlu meneruskan variabel saat menggunakan bash -c, disarankan menambahkan argumen seperti berikut

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

    Dijelaskan alasan penggunaan "--" dan peran argv[0]
    Disebutkan juga bahwa printf %q bisa dipakai, tetapi pendekatan yang kompatibel dengan Bourne lebih disukai

    • Dijelaskan bahwa "--" memiliki makna yang sangat jelas sebagai penanda akhir opsi di bash dan sebagian besar CLI Unix/Linux
      Referensi terkait

    • Dibagikan bahwa Busybox menentukan program yang akan dijalankan berdasarkan nilai argv[0], sehingga bisa diisi dengan perintah yang diinginkan seperti ls, mv, atau cp

  • Saat membutuhkan logika retry, cara yang paling sering dipakai adalah seperti di bawah ini

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

    Memang tidak terlalu elegan, tetapi umumnya akurat, dan pada tahap yang lebih lanjut bisa diterapkan exponential backoff
    Disebutkan juga ada keuntungan dari sisi skalabilitas

    • shellcheck merekomendasikan penanganan kasus ini dengan menggunakan variabel _
      Tautan referensi

    • Ditekankan bahwa fungsi eventually_succeeds mungkin tetap memerlukan timeout atau defensive coding tambahan tergantung situasinya
      Ini sekaligus mengingatkan pentingnya selalu menulis kode yang defensif dalam konteks POSIX/proses/IO

  • Ada yang berbagi pengalaman bahwa saat anak-anaknya masih kecil, ia pernah memakai perintah berikut sebagai semacam parental control agar mereka hanya bisa menonton satu program selama 30 menit

    timeout 1800 mplayer show.mp4 ; sudo pm-suspend
    

    Ide tersebut dinilai sangat berguna dalam praktik

    • Ada tambahan pendapat bahwa ini adalah contoh penggunaan yang dijelaskan dengan paling keren
  • Ada yang menyebut dirinya kurang menyukai penggunaan perintah inline atau file skrip sementara ketika harus mengirim sinyal ke subproses
    Cara yang lebih disukai adalah membuat logika kompleks yang diinginkan sebagai fungsi, mengekspornya, lalu membungkusnya dengan timeout bash -c
    Ini terkait dengan cara aman meneruskan argumen yang disebutkan oleh aidenn0

    #!/usr/bin/env bash
    
    long_fn () { # implementasikan logika yang diinginkan
     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
    
    • Ditunjukkan bahwa "$@" wajib ditulis di bagian akhir
      Jika tidak, argumen yang mengandung spasi tidak akan diteruskan dengan benar
      Dibagikan juga contoh long_fn yang bisa dipakai untuk memverifikasi hal ini
  • Ada yang mengingat kembali posting blog lama yang pernah membahas timeout
    Jika ingin tahu lebih jauh tentang bahasa pemrograman umum atau cara kerja internalnya, bukan hanya shell, disarankan membaca blog terkait

  • Dibagikan pengalaman menambahkan command timeout dalam setup Kubernetes
    Disebutkan bahwa skrip shell POSIX seperti await-cmd.sh, await-http.sh, dan await-tcp.sh sudah cukup matang dan bisa sangat berguna dalam situasi tertentu
    Tautan proyek terkait