**Exponential Backoff Delay** [Napier / Dash Automation, LLC] So I needed an exponential backoff delay for reconnecting to our Websocket Server when our servers go down. I decided to implement it using a simple coroutine and the "redelay" command (reconnect delay). Exponential Backoff is used to schedule reconnects to a server so that when your server connection is re-established every instance of your scripts won't reconnect at the same exact time. If you want to learn more I found http://blog.johnryding.com/post/78544969349/how-to-reconnect-web-sockets-in-a-realtime-web-app%|%How to Reconnect Web Sockets in a Realtime Web App without Flooding the Server%|% to be very helpful. Basically it will generate a delay in ms for which is a random number between 1 second and the maximum delay. Each time you call the coroutine it will increase the maximum and return a new delay to use. You can set a name for the redelay or call it normally. You can also set the maximum seconds (default is 30) by using the second argument. '''Important: ''' You will want to make sure to kill the coroutine by giving any non empty value to it ($d cleanup, $d break, $d done - doesn't matter). ====== namespace eval redelay { variable count 0 } proc redelay { {name {}} {max 30} } { if {$name eq {}} { append name ::redelay::task [incr ::redelay::count] } return [coroutine $name ::redelay::delay $max] } proc ::redelay::delay { max } { yield [info coroutine] set attempts 1 while 1 { set interval [expr { min($max, (pow(2, $attempts) - 1)) * 1000 }] set delay [expr { int(rand()*($interval-1+1)+1) }] incr attempts if {[yield $delay] ne {}} { return } } } ====== Here is an example of it being used (your delays will vary from the ones shown here since it is using random) ====== set d [redelay] ; # ::redelay::task1 puts [$d] ; # 566 puts [$d] ; # 2071 puts [$d] ; # 1303 puts [$d] ; # 5375 puts [$d] ; # 24724 puts [$d] ; # 9369 puts [$d] ; # 11873 puts [$d] ; # 4104 $d break ====== Here is an example using a custom name and max time. It still returns the variable but giving it the name will create a command with that name (be careful of overlap) so you can also just use the name itself: ====== redelay delay 120 ; # delay puts [delay] ; # 105 puts [delay] ; # 428 puts [delay] ; # 4958 puts [delay] ; # 11442 puts [delay] ; # 9572 puts [delay] ; # 25054 puts [delay] ; # 90278 puts [delay] ; # 13372 delay break ====== If you don't like the coroutine flavor which allows you to have multiple instances and imo has a nicer syntax, you can also use this version of it which utilizes the count variable to increment your interval. Giving it "reset" as an argument will reset the counter to 0. ====== proc redelay { {max 30} } { variable ::redelay::count if {$max eq "reset"} { set count 0; return } set interval [expr { min($max, (pow(2, [incr count]) - 1)) * 1000 }] return [expr { int(rand()*($interval-1+1)+1) }] } ====== and an example: ====== redelay reset puts [redelay] puts [redelay] puts [redelay] puts [redelay] puts [redelay] puts [redelay] puts [redelay] ====== <>delay | exponential backoff