Skip to content

Commit

Permalink
Concurrent dedicated Server stopper shall not be executed concurrentl… (
Browse files Browse the repository at this point in the history
#36)

Concurrent dedicated Server stopper shall not be executed concurrently with sequential component stopper
  • Loading branch information
skovtunenko authored Jul 7, 2022
1 parent 4743872 commit 93f1965
Show file tree
Hide file tree
Showing 3 changed files with 0 additions and 125 deletions.
30 changes: 0 additions & 30 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package graterm_test

import (
"context"
"fmt"
"github.com/skovtunenko/graterm"
"log"
"net/http"
"syscall"
"time"
)
Expand Down Expand Up @@ -82,31 +80,3 @@ func ExampleStopper_Register() {
}
}
}

func ExampleStopper_ServerAsyncStarterFunc() {
stopper, appCtx := graterm.NewWithSignals(context.Background(), syscall.SIGINT, syscall.SIGTERM)

// Register some hooks:
stopper.Register(3, "HOOK", 1*time.Second, func(ctx context.Context) {
log.Println("terminating HOOK...")
defer log.Println("...HOOK terminated")
})

const hostPort = ":8080"
server := &http.Server{
Addr: hostPort,
Handler: http.DefaultServeMux,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello, world!\n")
})

asyncStarterFunc := stopper.ServerAsyncStarterFunc(appCtx, server)
// ... here it might be some actions before we need to start the server.....
asyncStarterFunc()
log.Printf("application started on: %q\n", hostPort)

if err := stopper.Wait(appCtx, 40*time.Second); err != nil {
log.Printf("graceful termination period was timed out: %+v", err)
}
}
46 changes: 0 additions & 46 deletions term.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package graterm

import (
"context"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"sort"
Expand Down Expand Up @@ -195,47 +193,3 @@ func (s *Stopper) waitShutdown(appCtx context.Context) {
runWg.Wait()
}
}

// serverWrapper provides a methods to start underlying srv asynchronously.
type serverWrapper struct {
log Logger

srv Server
}

// ServerAsyncStarterFunc creates an serverWrapper instance with graceful shutdown capabilities.
// The HTTP server will be gracefully shutdown once appCtx is done.
//
// Note: this method will start internal server shutdown monitoring goroutine.
func (s *Stopper) ServerAsyncStarterFunc(appCtx context.Context, srv Server) func() {
srvWithShutdown := &serverWrapper{
log: s.log,
srv: srv,
}

{
s.wg.Add(1)
go srvWithShutdown.waitShutdown(appCtx, s.wg)
}

return srvWithShutdown.startAsync
}

// startAsync starts Server asynchronously.
func (w *serverWrapper) startAsync() {
go func() {
w.log.Printf("starting server asynchronously")
if err := w.srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
w.log.Printf("server ListenAndServe() finished: %+v", err)
}
}()
}

func (w *serverWrapper) waitShutdown(appCtx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
<-appCtx.Done()

if err := w.srv.Shutdown(context.Background()); err != nil {
w.log.Printf("server Shutdown() finished with error: %+v", err)
}
}
49 changes: 0 additions & 49 deletions term_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"github.com/stretchr/testify/require"
"log"
"net/http"
"os"
"os/signal"
"runtime"
Expand Down Expand Up @@ -314,51 +313,3 @@ func TestStopper_SetLogger(t *testing.T) {
})
}
}

// mockServer is a mocked NOOP Server.
type mockServer struct {
expectedErr error
}

func (m *mockServer) ListenAndServe() error {
return m.expectedErr
}

func (m *mockServer) Shutdown(_ context.Context) error {
return m.expectedErr
}

func TestNewServerWithShutdown(t *testing.T) {
tests := []struct {
name string
serverTerminationErr error
}{
{
name: "Regular server termination",
serverTerminationErr: http.ErrServerClosed,
},
{
name: "Unexpected server termination",
serverTerminationErr: http.ErrAbortHandler,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

s, ctx := NewWithSignals(ctx, os.Interrupt)
require.NotNil(t, ctx)

httpServer := &mockServer{
tt.serverTerminationErr,
}

asyncStarterFunc := s.ServerAsyncStarterFunc(ctx, httpServer)
asyncStarterFunc()
})
}
}

0 comments on commit 93f1965

Please sign in to comment.