Adding graceful server shutdown
continuous-integration/drone/push Build is passing Details

main
Drew Bednar 4 months ago
parent 37404b4821
commit 514ba0cba3

@ -16,7 +16,7 @@ tmp_dir = "tmp"
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html", "go.tmpl"]
include_file = []
kill_delay = "0s"
kill_delay = "1s"
log = "build-errors.log"
poll = false
poll_interval = 0
@ -24,7 +24,7 @@ tmp_dir = "tmp"
pre_cmd = []
rerun = false
rerun_delay = 500
send_interrupt = false
send_interrupt = true
stop_on_error = false
[color]

@ -52,6 +52,7 @@ The `ratchetd` cmd binary uses Oauth so you will need to create a new Oauth App.
## Additional Resources
- [Graceful Shutdown](https://dev.to/mokiat/proper-http-shutdown-in-go-3fji)
- [Content Range Requests](https://web.archive.org/web/20230918195519/https://benramsey.com/blog/2008/05/206-partial-content-and-range-requests/)
- [HTTP 204 and 205 Status Codes](https://web.archive.org/web/20230918193536/https://benramsey.com/blog/2008/05/http-status-204-no-content-and-205-reset-content/)
- [How to Disable FileServer Directory Listings](https://www.alexedwards.net/blog/disable-http-fileserver-directory-listings)

@ -1,12 +1,17 @@
package main
import (
"context"
"crypto/tls"
"errors"
"flag"
"fmt"
"io"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
rdb "git.runcible.io/learning/ratchet/internal/database"
@ -22,17 +27,20 @@ import (
// commit string
// )
func main() {
func run(ctx context.Context, w io.Writer, args []string) error {
// CONFIGURATION
// Parse command line options
addr := flag.String("addr", "0.0.0.0", "HTTP network address")
port := flag.String("port", "5001", "HTTP port")
logLevel := flag.String("logging", "INFO", "Logging Level. Valid values [INFO, DEBUG, WARN, ERROR].")
dbPath := flag.String("database", "./ratchet.db", "A path to a sqlite3 database")
certPath := flag.String("cert", "./tls/cert.pem", "A public cert in .pem format")
keyPath := flag.String("key", "./tls/key.pem", "A private key in .pem format")
flags := flag.NewFlagSet(args[0], flag.ExitOnError)
addr := flags.String("addr", "0.0.0.0", "HTTP network address")
port := flags.String("port", "5001", "HTTP port")
logLevel := flags.String("logging", "INFO", "Logging Level. Valid values [INFO, DEBUG, WARN, ERROR].")
dbPath := flags.String("database", "./ratchet.db", "A path to a sqlite3 database")
certPath := flags.String("cert", "./tls/cert.pem", "A public cert in .pem format")
keyPath := flags.String("key", "./tls/key.pem", "A private key in .pem format")
// must call parse or all values will be the defaults
flag.Parse()
if err := flags.Parse(args[1:]); err != nil {
return err
}
// DEPENDENCY INJECTION FOR HANDLERS
// Setup Logging
@ -41,17 +49,22 @@ func main() {
db, err := rdb.OpenSqlite3DB(*dbPath)
if err != nil {
slog.Error(err.Error())
os.Exit(1)
return err
}
// Close db connection before exiting main.
defer db.Close()
defer func() {
slog.Info("Cleaning up database")
_, err := db.Exec("PRAGMA wal_checkpoint(TRUNCATE)")
if err != nil {
slog.Error(fmt.Sprintf("Error checkpointing database: %v", err))
}
db.Close()
}()
//tc, err := server.InitTemplateCache()
tc, err := server.InitFSTemplateCache()
if err != nil {
slog.Error(err.Error())
os.Exit(1)
return err
}
// SessionManager
@ -126,8 +139,37 @@ func main() {
slog.Info(fmt.Sprintf("Listening on https://%s", srv.Addr))
err = srv.ListenAndServeTLS(*certPath, *keyPath)
slog.Error(err.Error())
os.Exit(1)
go func() {
if err = srv.ListenAndServeTLS(*certPath, *keyPath); !errors.Is(err, http.ErrServerClosed) {
slog.Error(err.Error())
os.Exit(1)
}
slog.Info("Stopped serving connections")
}()
// Handle graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// Block until signal is recieved
<-sigChan
shutdownCtx, shutdownRelease := context.WithTimeout(ctx, 10*time.Second)
defer shutdownRelease()
if err := srv.Shutdown(shutdownCtx); err != nil {
slog.Error("Failed to close within context timeout. Forcing server close.")
srv.Close()
return err
}
return nil
}
func main() {
ctx := context.Background()
if err := run(ctx, os.Stdout, os.Args); err != nil {
slog.Error(err.Error())
os.Exit(1)
}
}

Loading…
Cancel
Save