-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmain.go
113 lines (105 loc) · 2.67 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Command bitmapist implements standalone bitmapist-compatible server
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/signal"
"path/filepath"
"runtime/debug"
"syscall"
"time"
"github.com/Doist/bitmapist-server/internal/bitmapist"
"github.com/artyom/autoflags"
"github.com/artyom/red"
)
var explicitVersion string // to be set by CI with -ldflags="-X=main.explicitVersion=v1.2.3"
func main() {
args := struct {
Addr string `flag:"addr,address to listen"`
File string `flag:"db,path to database file"`
Bak string `flag:"bak,file to save backup to on SIGUSR1"`
Dbg bool `flag:"debug,log incoming commands"`
}{
Addr: "localhost:6379",
File: "bitmapist.db",
}
autoflags.Define(&args)
var versionOnly bool
flag.BoolVar(&versionOnly, "v", versionOnly, "print version and exit")
flag.BoolVar(&versionOnly, "version", versionOnly, "print version and exit")
flag.Parse()
if versionOnly || (len(os.Args) == 2 && os.Args[1] == "version") {
v := "unknown"
if explicitVersion != "" {
v = explicitVersion
} else if bi, ok := debug.ReadBuildInfo(); ok {
v = bi.Main.Version
}
fmt.Printf("bitmapist-server %s\n", v)
return
}
log := log.New(os.Stderr, "", log.LstdFlags)
log.Println("loading data from", args.File)
begin := time.Now()
s, err := bitmapist.New(args.File)
if err != nil {
log.Fatal(err)
}
log.Println("loaded in", time.Since(begin))
s.WithLogger(log)
srv := red.NewServer()
srv.WithLogger(log)
if args.Dbg {
srv.WithCommands()
}
s.Register(srv)
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
log.Println(<-sigCh)
signal.Reset()
if err := s.Shutdown(); err != nil {
log.Fatal(err)
}
os.Exit(0)
}()
if args.Bak != "" && args.Bak != args.File {
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGUSR1)
for range sigCh {
log.Printf("backing up database to %q", args.Bak)
switch err := doBackup(s, args.Bak); err {
case nil:
log.Println("backup successfully saved")
default:
log.Println("error doing backup:", err)
}
}
}()
}
log.Fatal(srv.ListenAndServe(args.Addr))
}
// doBackup creates temporary file, calls s.Backup on it and renames temporary
// file to dst if backup completed successfully.
func doBackup(s *bitmapist.Server, dst string) error {
f, err := ioutil.TempFile(filepath.Dir(dst), "bitmapist-backup-")
if err != nil {
return err
}
defer f.Close()
defer os.Remove(f.Name())
if err := s.Backup(f); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
if err := os.Chmod(f.Name(), 0644); err != nil {
return err
}
return os.Rename(f.Name(), dst)
}