I'm using the Golang gorilla/mux package, and one of the examples is as follows:
func main() {
var wait time.Duration
flag.DurationVar(&wait, "graceful-timeout", time.Second * 15, "the duration for which the server gracefully wait for existing connections to finish - e.g. 15s or 1m")
flag.Parse()
r := mux.NewRouter()
// Add your routes as needed
srv := &http.Server{
Addr: "0.0.0.0:8080",
// Good practice to set timeouts to avoid Slowloris attacks.
WriteTimeout: time.Second * 15,
ReadTimeout: time.Second * 15,
IdleTimeout: time.Second * 60,
Handler: r, // Pass our instance of gorilla/mux in.
}
// Run our server in a goroutine so that it doesn't block.
go func() {
if err := srv.ListenAndServe(); err != nil {
log.Println(err)
}
}()
c := make(chan os.Signal, 1)
// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
// SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
signal.Notify(c, os.Interrupt)
// Block until we receive our signal.
<-c
// Create a deadline to wait for.
ctx, cancel := context.WithTimeout(context.Background(), wait)
defer cancel()
// Doesn't block if no connections, but will otherwise wait
// until the timeout deadline.
srv.Shutdown(ctx)
// Optionally, you could run srv.Shutdown in a goroutine and block on
// <-ctx.Done() if your application should wait for other services
// to finalize based on context cancellation.
log.Println("shutting down")
os.Exit(0)
}
This seems simple enough, but my understanding is that defers do not run when os.Exit() is called (as per https://gobyexample.com/exit). I notice that there's a CancelFunc() returned by context.WithTimeout(), which is then deferred. My guess is that this is supposed to cancel the context created above if main() finishes before the deadline, but I don't see how that can happen with a call to os.Exit() at the end. Could anyone help me see what I'm missing?
You right, the deferred cancel function is never called. The author want probably point out that in real application, you should never forget to cancel your context.
Related
Helo All,
New to golang and was debugging timeout issues in a production environment. Before making a call to the server we add a timeout of 50ms to context and fire a server call. If the response is not received within 50 ms we expect the application to move on and not wait for the response.
But while debugging, I capture the duration between we fire a server call and the response received (or error out), to my surprise the value at the time is much higher than 50 ms.
Client syntax -
ctx, cancel := context.WithTimeout(ctx, e.opts.Timeout)
defer cancel()
fireServerCall(ctx)
..
..
def fireServerCall(ctx context:Context){
startTime:=time.Now()
//call to the server
res, err:=callToServer(ctx)
if err!=nil{
//capture failure latency
return ....
}
//capture success latency
return ....
}
Has anyone ever faced any similar issue? Is this expected behaviour? How did you handle such cases?
Am I doing something incorrectly? Suggestions are welcome :)
Edit:
I am passing context in my original code but forgot to mention it here, just added it. That mean, I am passing the same context on which my client is waiting for server to respond within 50 ms.
You should pass created context to fireServerCall and callToServer functions
callToServer should consider passed context and monitor ctx.Done() channel to stop its execution accordingly
Answering to comment by #Bishnu:
Don't think this is needed. Did a test and even without passing ctx to callToServer() it works. The behaviour is not as expected under high load. Can you kindly share some document/test what you have pointed here?
Context timeout just can't work without context passing and checking its Done() channel. Context is not some kind of magic — simplifying it is just a struct with done channel which is closed by calling cancel function or when timeout occurs. Monitoring this channel — is responsibility of the innermost function that accepts it.
Example:
package main
import (
"context"
"fmt"
"time"
)
func callToServer(ctx context.Context) {
now := time.Now()
select {
case <-ctx.Done(): // context cancelled via cancel or deadline
case <-time.After(1 * time.Second): // emulate external call
}
fmt.Printf("callToServer: %v\n", time.Since(now))
}
func callToServerContextAgnostic(ctx context.Context) {
now := time.Now()
select {
case <-time.After(2 * time.Second): // emulate external call
}
fmt.Printf("callToServerContextAgnostic: %v\n", time.Since(now))
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
callToServer(ctx)
ctx2, cancel2 := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel2()
callToServerContextAgnostic(ctx2)
}
Results:
callToServer: 100ms
callToServerContextAgnostic: 2s
You can launch it on Go Playground: https://go.dev/play/p/tIxjHxUzYfh
Note that many of the clients (from standard or third party libraries) monitors Done channel by themselves.
For example standard HTTP client:
c := &http.Client{} // client for all requests
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond*100))
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://google.com", nil)
if err != nil {
log.Fatal(err)
}
resp, err := c.Do(req) // will monitor `Done` channel for you
Some docs and articles:
https://pkg.go.dev/context
https://www.digitalocean.com/community/tutorials/how-to-use-contexts-in-go
I'm working on a Go project that require calling an initiation function (initFunction) in a separated goroutine (to ensure this function does not interfere with the rest of the project). initFunction must not take more than 30 seconds, so I thought I would use context.WithTimeout. Lastly, initFunction must be able to notify errors to the caller, so I thought of making an error channel and calling initFunction from an anonymous function, to recieve and report the error.
func RunInitGoRoutine(initFunction func(config string)error) error {
initErr := make(chan error)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Seconds)
go func() {
<-ctx.Done() // Line 7
err := initFunction(config)
initErr <-err
}()
select {
case res := <-initErr:
return res.err
case <-ctx.Done():
err := errors.New("Deadline")
return err
}
}
I'm quite new to Go, so I'm asking for feedbacks about the above code.
I have some doubt about Line 7. I used this to ensure the anonymous function is "included" under ctx and is therefore killed and freed and everything once timeout expires, but I'm not sure I have done the right thing.
Second thing is, I know I should be calling cancel( ) somewhere, but I can't put my finger around where.
Lastly, really any feedback is welcome, being it about efficency, style, correctness or anything.
In Go the pratice is to communicate via channels. So best thing is probably to share a channel on your context so others can consume from the channel.
As you are stating you are new to Go, I wrote a whole bunch of articles on Go (Beginner level) https://marcofranssen.nl/categories/golang.
Read from old to new to get familiar with the language.
Regarding the channel specifics you should have a look at this article.
https://marcofranssen.nl/concurrency-in-go
A pratical example of a webserver listening for ctrl+c and then gracefully shutting down the server using channels is described in this blog post.
https://marcofranssen.nl/improved-graceful-shutdown-webserver
In essence we run the server in a background routine
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
srv.l.Fatal("Could not listen on", zap.String("addr", srv.Addr), zap.Error(err))
}
}()
and then we have some code that is blocking the main routine by listening on a channel for the shutdown signal.
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
sig := <-quit
srv.l.Info("Server is shutting down", zap.String("reason", sig.String()))
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
srv.SetKeepAlivesEnabled(false)
if err := srv.Shutdown(ctx); err != nil {
srv.l.Fatal("Could not gracefully shutdown the server", zap.Error(err))
}
srv.l.Info("Server stopped")
This is very similar to your usecase. So running your init in a background routine and then consume the channel waiting for the result of this init routine.
package main
import (
"fmt"
"time"
)
type InitResult struct {
Message string
}
func main() {
initResult := make(chan InitResult, 0)
go func(c chan<- InitResult) {
time.Sleep(5 * time.Second)
// here we are publishing the result on the channel
c <- InitResult{Message: "Initialization succeeded"}
}(initResult)
fmt.Println("Started initializing")
// here we have a blocking operation consuming the channel
res := <-initResult
fmt.Printf("Init result: %s", res.Message)
}
https://play.golang.org/p/_YGIrdNVZx6
You could also add an error field on the struct so you could do you usual way of error checking.
Reusing parent context with context.WithTimeout with a new timeout
Hi there, I'm new to go. I was wondering if it's possible to reuse a parent context to create multiple context.withTimeout(). The rationale would be where I have to call multiple network requests in sequence and would like to set a timeout for each request at the same time using the parent's context.
Rationale
When the parent's context is cancelled, all the requests made would be cancelled too.
Problem
In the code below, it shows an example whereby LongProcess is the network request. However, the context is closed before the second LongProcess call can be made with a context deadline exceeded.
The documentation withDeadline states The returned context's Done channel is closed when the deadline expires, when the returned cancel function is called, or when the parent context's Done channel isclosed, whichever happens first.
So if that's the case, is there a way where I can reset the timer for withTimeout? Or do I have to create a new context context.Background() for every request? That would mean the parent context will not be passed. :(
// LongProcess refers to a long network request
func LongProcess(ctx context.Context, duration time.Duration, msg string) error {
c1 := make(chan string, 1)
go func() {
// Simulate processing
time.Sleep(duration)
c1 <- msg
}()
select {
case m := <-c1:
fmt.Println(m)
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func main() {
ctx := context.Background()
t := 2 * time.Second
ctx, cancel := context.WithTimeout(ctx, t)
defer cancel()
// Simulate a 2 second process time
err := LongProcess(ctx, 2*time.Second, "first process")
fmt.Println(err)
// Reusing the context.
s, cancel := context.WithTimeout(ctx, t)
defer cancel()
// Simulate a 1 second process time
err = LongProcess(s, 1*time.Second, "second process")
fmt.Println(err) // context deadline exceeded
}
It looks like the first call to context.WithTimeout shadow the parent context ctx. The later process re-use this already canceled context hence the error. You have to re-use the parent one. Here is the example updated:
func main() {
// Avoid to shadow child contexts
parent := context.Background()
t := 2 * time.Second
// Use the parent context.
ctx1, cancel := context.WithTimeout(parent, t)
defer cancel()
err := LongProcess(ctx1, 2*time.Second, "first process")
fmt.Println(err)
// Use the parent context not the canceled one.
ctx2, cancel := context.WithTimeout(parent, t)
defer cancel()
err = LongProcess(ctx2, 1*time.Second, "second process")
fmt.Println(err)
}
I have a Go RPC server that serves client requests. A client requests work (or task) from the server and the server assigns a task to the client. The server expects workers (or clients) to finish any task within a time limit. Therefore a timeout event callback mechanism is required on the server-side.
Here is what I tried so far.
func (l *Listener) RequestHandler(request string, reply string) error {
// some other work
// ....
_timer := time.NewTimer(time.Second * 5) // timer for 2 seconds
go func() {
// simulates a client not replying case, with timeout of 2 sec
y := <-_timer.C
fmt.Println("TimeOut for client")
// revert state changes becasue of client fail
}()
// set reply
// update some states
return nil
}
In the above snippet for each request from a worker (or a client) the handler in the server-side starts a timer and a goroutine. The goroutine reverts the changes done by the handler function before sending a reply to the client.
Is there any way of creating a "set of timers" and blocking wait on the "set of timers" ? Further, whenever a timer expires the blocking wait wakes up and provides us with the timer handles. Depending on the timer type we can perform different expiry handler functions in the runtime.
I am trying to implement a similar mechanism in Go that we can implement in C++ with timerfd with epoll.
Full code for the sample implementation of timers in Go. server.go and client.go.
I suggest you to explored the context package
it can be be done like this:
func main() {
c := context.Background()
wg := &sync.WaitGroup{}
f(c, wg)
wg.Wait()
}
func f(c context.Context, wg *sync.WaitGroup) {
c, _ = context.WithTimeout(c, 3*time.Second)
wg.Add(1)
go func(c context.Context) {
defer wg.Done()
select {
case <-c.Done():
fmt.Println("f() Done:", c.Err())
return
case r := <-time.After(5 * time.Second):
fmt.Println("f():", r)
}
}(c)
}
basically you initiate a base context and then derive other contexts from it, when a context is terminated, either by passing the time or a call to its close, it closes its Done channel and the Done channel of all the contexts that are derived from it.
I am trying to set up a service routine that runs every hour on the hour. It seems to me that either of those two are easy. To run my routine on the hour I can use time.AfterFunc(), first calculating the remaining time until the top of the hour. And to run my routine every hour I can use time.NewTicker().
However, I'm struggling to figure out how to start the NewTicker only after the function passed to AfterFunc() has fired.
My main() function looks something like this:
func main() {
fmt.Println("starting up")
// Here I'm setting up all kinds of HTTP listeners and gRPC listeners, none
// of which is important, save to mention that the app has more happening
// than just this service routine.
// Calculate duration until next hour and call time.AfterFunc()
// For the purposes of this exercise I'm just using 5 seconds so as not to
// have to wait increments of hours to see the results
time.AfterFunc(time.Second * 5, func() {
fmt.Println("AfterFunc")
})
// Set up a ticker to run every hour. Again, for the purposes of this
// exercise I'm ticking every 2 seconds just to see some results
t := time.NewTicker(time.Second * 2)
defer t.Stop()
go func() {
for now := range t.C {
fmt.Println("Ticker")
}
}()
// Block until termination signal is received
osSignals := make(chan os.Signal, 1)
signal.Notify(osSignals, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-osSignals
fmt.Println("exiting gracefully")
}
Of course time.AfterFunc() is blocking and the payload of my Ticker is deliberately put in a go routine so it also won't be blocking. This is so that my HTTP and gRPC listeners can continue to listen but also to allow the block of code at the end of main() to exit gracefully upon a termination signal from the OS. But the obvious downside now is that the Ticker kicks off pretty much immediately and fires twice (2 second intervals) before the function passed to AfterFunc() fires. The output looks like this:
Ticker
Ticker
AfterFunc
Ticker
Ticker
Ticker
etc.
What I wanted of course is:
AfterFunc
Ticker
Ticker
Ticker
Ticker
Ticker
etc.
The following also doesn't work and I'm not exactly sure why. It prints AfterFunc but the Ticker never fires.
time.AfterFunc(time.Second * 5, func() {
fmt.Println("AfterFunc")
t := time.NewTicker(time.Second * 2)
defer t.Stop()
go func() {
for now := range t.C {
fmt.Println("Ticker")
}
}()
})
The Go Programming Language Specification
Program execution
Program execution begins by initializing the main package and then
invoking the function main. When that function invocation returns, the
program exits. It does not wait for other (non-main) goroutines to
complete.
time.AfterFunc(time.Second * 5, func() {
fmt.Println("AfterFunc")
t := time.NewTicker(time.Second * 2)
defer t.Stop()
go func() {
for now := range t.C {
fmt.Println("Ticker")
}
}()
})
defer t.Stop() stops the ticker.
You are not waiting for the goroutine to run.
Gosh, I figured it out not long after posting, though I still think my solution lacks some elegance.
As #peterSO pointed out, the function passed to AfterFunc() executes and stops the Ticker moments after creating it with defer t.Stop()
The solution for me was to define the t variable before the call to AfterFunc() so that it has scope outside of the AfterFunc() payload function, and then stop it at the end of my main() func. Here is the new main() func:
func main() {
fmt.Println("starting up")
var t *time.Ticker
time.AfterFunc(time.Second * 5, func() {
fmt.Println("AfterFunc")
t = time.NewTicker(time.Second * 2)
go func() {
for now := range t.C {
fmt.Println("Ticker")
}
}()
})
//defer t.Stop()
// Block until termination signal is received
osSignals := make(chan os.Signal, 1)
signal.Notify(osSignals, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-osSignals
t.Stop()
logger.Info("exiting gracefully")
}
Strangely though that commented out defer t.Stop() causes a panic (invalid memory address or nil pointer dereference) when the application closes upon a termination signal. If I stop it as in the uncommented t.Stop() at the very end of the code, it works as expected. Not sure why that is.
time.AfterFunc(time.Second*5, func() {
fmt.Println("AfterFunc")
t := time.NewTicker(time.Second * 2)
defer t.Stop()
for range t.C {
fmt.Println("Ticker")
}
})
It produces the output you need:
starting up
AfterFunc
Ticker
Ticker
Ticker
Ticker
Ticker