Removed global variables from core package

The core package uses global variables that keep from having
more than one Tile38 instance runnning in the same process.

Move the core variables in the server.Options type which are
uniquely stated per Server instance.

The build variables are still present in the core package.
This commit is contained in:
tidwall 2022-09-24 15:42:07 -07:00
parent 0301545fe6
commit 13ceb7da41
11 changed files with 166 additions and 69 deletions

View File

@ -141,12 +141,33 @@ Developer Options:
} }
var ( var (
devMode bool
nohup bool nohup bool
showEvioDisabled bool showEvioDisabled bool
showThreadsDisabled bool showThreadsDisabled bool
) )
var (
// use to be in core/options.go
// DevMode puts application in to dev mode
devMode = false
// ShowDebugMessages allows for log.Debug to print to console.
showDebugMessages = false
// ProtectedMode forces Tile38 to default in protected mode.
protectedMode = "no"
// AppendOnly allows for disabling the appendonly file.
appendOnly = true
// AppendFileName allows for custom appendonly file path
appendFileName = ""
// QueueFileName allows for custom queue.db file path
queueFileName = ""
)
// parse non standard args. // parse non standard args.
nargs := []string{os.Args[0]} nargs := []string{os.Args[0]}
for i := 1; i < len(os.Args); i++ { for i := 1; i < len(os.Args); i++ {
@ -163,10 +184,10 @@ Developer Options:
if i < len(os.Args) { if i < len(os.Args) {
switch strings.ToLower(os.Args[i]) { switch strings.ToLower(os.Args[i]) {
case "no": case "no":
core.ProtectedMode = "no" protectedMode = "no"
continue continue
case "yes": case "yes":
core.ProtectedMode = "yes" protectedMode = "yes"
continue continue
} }
} }
@ -183,10 +204,10 @@ Developer Options:
if i < len(os.Args) { if i < len(os.Args) {
switch strings.ToLower(os.Args[i]) { switch strings.ToLower(os.Args[i]) {
case "no": case "no":
core.AppendOnly = false appendOnly = false
continue continue
case "yes": case "yes":
core.AppendOnly = true appendOnly = true
continue continue
} }
} }
@ -198,14 +219,14 @@ Developer Options:
fmt.Fprintf(os.Stderr, "appendfilename must have a value\n") fmt.Fprintf(os.Stderr, "appendfilename must have a value\n")
os.Exit(1) os.Exit(1)
} }
core.AppendFileName = os.Args[i] appendFileName = os.Args[i]
case "--queuefilename", "-queuefilename": case "--queuefilename", "-queuefilename":
i++ i++
if i == len(os.Args) || os.Args[i] == "" { if i == len(os.Args) || os.Args[i] == "" {
fmt.Fprintf(os.Stderr, "queuefilename must have a value\n") fmt.Fprintf(os.Stderr, "queuefilename must have a value\n")
os.Exit(1) os.Exit(1)
} }
core.QueueFileName = os.Args[i] queueFileName = os.Args[i]
case "--http-transport", "-http-transport": case "--http-transport", "-http-transport":
i++ i++
if i < len(os.Args) { if i < len(os.Args) {
@ -308,8 +329,7 @@ Developer Options:
log.Level = 1 log.Level = 1
} }
core.DevMode = devMode showDebugMessages = veryVerbose
core.ShowDebugMessages = veryVerbose
hostd := "" hostd := ""
if host != "" { if host != "" {
@ -455,6 +475,12 @@ Developer Options:
UseHTTP: httpTransport, UseHTTP: httpTransport,
MetricsAddr: *metricsAddr, MetricsAddr: *metricsAddr,
UnixSocketPath: unixSocket, UnixSocketPath: unixSocket,
DevMode: devMode,
ShowDebugMessages: showDebugMessages,
ProtectedMode: protectedMode,
AppendOnly: appendOnly,
AppendFileName: appendFileName,
QueueFileName: queueFileName,
} }
if err := server.Serve(opts); err != nil { if err := server.Serve(opts); err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -1,19 +0,0 @@
package core
// DevMode puts application in to dev mode
var DevMode = false
// ShowDebugMessages allows for log.Debug to print to console.
var ShowDebugMessages = false
// ProtectedMode forces Tile38 to default in protected mode.
var ProtectedMode = "no"
// AppendOnly allows for disabling the appendonly file.
var AppendOnly = true
// AppendFileName allows for custom appendonly file path
var AppendFileName = ""
// QueueFileName allows for custom queue.db file path
var QueueFileName = ""

View File

@ -8,7 +8,6 @@ import (
"time" "time"
"github.com/tidwall/btree" "github.com/tidwall/btree"
"github.com/tidwall/tile38/core"
"github.com/tidwall/tile38/internal/collection" "github.com/tidwall/tile38/internal/collection"
"github.com/tidwall/tile38/internal/field" "github.com/tidwall/tile38/internal/field"
"github.com/tidwall/tile38/internal/log" "github.com/tidwall/tile38/internal/log"
@ -42,7 +41,7 @@ func (s *Server) aofshrink() {
}() }()
err := func() error { err := func() error {
f, err := os.Create(core.AppendFileName + "-shrink") f, err := os.Create(s.opts.AppendFileName + "-shrink")
if err != nil { if err != nil {
return err return err
} }
@ -279,13 +278,13 @@ func (s *Server) aofshrink() {
if err := f.Close(); err != nil { if err := f.Close(); err != nil {
log.Fatalf("shrink new aof close fatal operation: %v", err) log.Fatalf("shrink new aof close fatal operation: %v", err)
} }
if err := os.Rename(core.AppendFileName, core.AppendFileName+"-bak"); err != nil { if err := os.Rename(s.opts.AppendFileName, s.opts.AppendFileName+"-bak"); err != nil {
log.Fatalf("shrink backup fatal operation: %v", err) log.Fatalf("shrink backup fatal operation: %v", err)
} }
if err := os.Rename(core.AppendFileName+"-shrink", core.AppendFileName); err != nil { if err := os.Rename(s.opts.AppendFileName+"-shrink", s.opts.AppendFileName); err != nil {
log.Fatalf("shrink rename fatal operation: %v", err) log.Fatalf("shrink rename fatal operation: %v", err)
} }
s.aof, err = os.OpenFile(core.AppendFileName, os.O_CREATE|os.O_RDWR, 0600) s.aof, err = os.OpenFile(s.opts.AppendFileName, os.O_CREATE|os.O_RDWR, 0600)
if err != nil { if err != nil {
log.Fatalf("shrink openfile fatal operation: %v", err) log.Fatalf("shrink openfile fatal operation: %v", err)
} }
@ -296,7 +295,7 @@ func (s *Server) aofshrink() {
} }
s.aofsz = int(n) s.aofsz = int(n)
os.Remove(core.AppendFileName + "-bak") // ignore error os.Remove(s.opts.AppendFileName + "-bak") // ignore error
return nil return nil
}() }()

View File

@ -9,7 +9,6 @@ import (
"time" "time"
"github.com/tidwall/resp" "github.com/tidwall/resp"
"github.com/tidwall/tile38/core"
"github.com/tidwall/tile38/internal/log" "github.com/tidwall/tile38/internal/log"
) )
@ -140,7 +139,7 @@ func getEndOfLastValuePositionInFile(fname string, startPos int64) (int64, error
// We will do some various checksums on the leader until we find the correct position to start at. // We will do some various checksums on the leader until we find the correct position to start at.
func (s *Server) followCheckSome(addr string, followc int, auth string, func (s *Server) followCheckSome(addr string, followc int, auth string,
) (pos int64, err error) { ) (pos int64, err error) {
if core.ShowDebugMessages { if s.opts.ShowDebugMessages {
log.Debug("follow:", addr, ":check some") log.Debug("follow:", addr, ":check some")
} }
s.mu.Lock() s.mu.Lock()
@ -211,7 +210,7 @@ func (s *Server) followCheckSome(addr string, followc int, auth string,
return 0, err return 0, err
} }
if pos == fullpos { if pos == fullpos {
if core.ShowDebugMessages { if s.opts.ShowDebugMessages {
log.Debug("follow: aof fully intact") log.Debug("follow: aof fully intact")
} }
return pos, nil return pos, nil

View File

@ -9,7 +9,6 @@ import (
"time" "time"
"github.com/tidwall/resp" "github.com/tidwall/resp"
"github.com/tidwall/tile38/core"
"github.com/tidwall/tile38/internal/log" "github.com/tidwall/tile38/internal/log"
) )
@ -240,7 +239,7 @@ func (s *Server) followStep(host string, port int, followc int) error {
if v.String() != "OK" { if v.String() != "OK" {
return errors.New("invalid response to replconf request") return errors.New("invalid response to replconf request")
} }
if core.ShowDebugMessages { if s.opts.ShowDebugMessages {
log.Debug("follow:", addr, ":replconf") log.Debug("follow:", addr, ":replconf")
} }
@ -254,7 +253,7 @@ func (s *Server) followStep(host string, port int, followc int) error {
if v.String() != "OK" { if v.String() != "OK" {
return errors.New("invalid response to aof live request") return errors.New("invalid response to aof live request")
} }
if core.ShowDebugMessages { if s.opts.ShowDebugMessages {
log.Debug("follow:", addr, ":read aof") log.Debug("follow:", addr, ":read aof")
} }

View File

@ -135,6 +135,8 @@ type Server struct {
monconnsMu sync.RWMutex monconnsMu sync.RWMutex
monconns map[net.Conn]bool // monitor connections monconns map[net.Conn]bool // monitor connections
opts Options
} }
// Options for Serve() // Options for Serve()
@ -145,15 +147,36 @@ type Options struct {
UseHTTP bool UseHTTP bool
MetricsAddr string MetricsAddr string
UnixSocketPath string // path for unix socket UnixSocketPath string // path for unix socket
// DevMode puts application in to dev mode
DevMode bool
// ShowDebugMessages allows for log.Debug to print to console.
ShowDebugMessages bool
// ProtectedMode forces Tile38 to default in protected mode.
ProtectedMode string
// AppendOnly allows for disabling the appendonly file.
AppendOnly bool
// AppendFileName allows for custom appendonly file path
AppendFileName string
// QueueFileName allows for custom queue.db file path
QueueFileName string
} }
// Serve starts a new tile38 server // Serve starts a new tile38 server
func Serve(opts Options) error { func Serve(opts Options) error {
if core.AppendFileName == "" { if opts.AppendFileName == "" {
core.AppendFileName = path.Join(opts.Dir, "appendonly.aof") opts.AppendFileName = path.Join(opts.Dir, "appendonly.aof")
} }
if core.QueueFileName == "" { if opts.QueueFileName == "" {
core.QueueFileName = path.Join(opts.Dir, "queue.db") opts.QueueFileName = path.Join(opts.Dir, "queue.db")
}
if opts.ProtectedMode == "" {
opts.ProtectedMode = "no"
} }
log.Infof("Server started, Tile38 version %s, git %s", core.Version, core.GitSHA) log.Infof("Server started, Tile38 version %s, git %s", core.Version, core.GitSHA)
@ -183,6 +206,7 @@ func Serve(opts Options) error {
groupHooks: btree.NewNonConcurrent(byGroupHook), groupHooks: btree.NewNonConcurrent(byGroupHook),
groupObjects: btree.NewNonConcurrent(byGroupObject), groupObjects: btree.NewNonConcurrent(byGroupObject),
hookExpires: btree.NewNonConcurrent(byHookExpires), hookExpires: btree.NewNonConcurrent(byHookExpires),
opts: opts,
} }
s.epc = endpoint.NewManager(s) s.epc = endpoint.NewManager(s)
@ -256,7 +280,7 @@ func Serve(opts Options) error {
}() }()
// Load the queue before the aof // Load the queue before the aof
qdb, err := buntdb.Open(core.QueueFileName) qdb, err := buntdb.Open(opts.QueueFileName)
if err != nil { if err != nil {
return err return err
} }
@ -284,8 +308,8 @@ func Serve(opts Options) error {
if err := s.migrateAOF(); err != nil { if err := s.migrateAOF(); err != nil {
return err return err
} }
if core.AppendOnly { if opts.AppendOnly {
f, err := os.OpenFile(core.AppendFileName, os.O_CREATE|os.O_RDWR, 0600) f, err := os.OpenFile(opts.AppendFileName, os.O_CREATE|os.O_RDWR, 0600)
if err != nil { if err != nil {
return err return err
} }
@ -334,7 +358,7 @@ func Serve(opts Options) error {
} }
func (s *Server) isProtected() bool { func (s *Server) isProtected() bool {
if core.ProtectedMode == "no" { if s.opts.ProtectedMode == "no" {
// --protected-mode no // --protected-mode no
return false return false
} }
@ -1051,19 +1075,19 @@ func (s *Server) command(msg *Message, client *Client) (
case "ttl": case "ttl":
res, err = s.cmdTTL(msg) res, err = s.cmdTTL(msg)
case "shutdown": case "shutdown":
if !core.DevMode { if !s.opts.DevMode {
err = fmt.Errorf("unknown command '%s'", msg.Args[0]) err = fmt.Errorf("unknown command '%s'", msg.Args[0])
return return
} }
log.Fatal("shutdown requested by developer") log.Fatal("shutdown requested by developer")
case "massinsert": case "massinsert":
if !core.DevMode { if !s.opts.DevMode {
err = fmt.Errorf("unknown command '%s'", msg.Args[0]) err = fmt.Errorf("unknown command '%s'", msg.Args[0])
return return
} }
res, err = s.cmdMassInsert(msg) res, err = s.cmdMassInsert(msg)
case "sleep": case "sleep":
if !core.DevMode { if !s.opts.DevMode {
err = fmt.Errorf("unknown command '%s'", msg.Args[0]) err = fmt.Errorf("unknown command '%s'", msg.Args[0])
return return
} }

View File

@ -318,7 +318,7 @@ func (s *Server) extStats(m map[string]interface{}) {
// Whether or not a cluster is enabled // Whether or not a cluster is enabled
m["tile38_cluster_enabled"] = false m["tile38_cluster_enabled"] = false
// Whether or not the Tile38 AOF is enabled // Whether or not the Tile38 AOF is enabled
m["tile38_aof_enabled"] = core.AppendOnly m["tile38_aof_enabled"] = s.opts.AppendOnly
// Whether or not an AOF shrink is currently in progress // Whether or not an AOF shrink is currently in progress
m["tile38_aof_rewrite_in_progress"] = s.shrinking m["tile38_aof_rewrite_in_progress"] = s.shrinking
// Length of time the last AOF shrink took // Length of time the last AOF shrink took
@ -409,7 +409,7 @@ func boolInt(t bool) int {
return 0 return 0
} }
func (s *Server) writeInfoPersistence(w *bytes.Buffer) { func (s *Server) writeInfoPersistence(w *bytes.Buffer) {
fmt.Fprintf(w, "aof_enabled:%d\r\n", boolInt(core.AppendOnly)) fmt.Fprintf(w, "aof_enabled:%d\r\n", boolInt(s.opts.AppendOnly))
fmt.Fprintf(w, "aof_rewrite_in_progress:%d\r\n", boolInt(s.shrinking)) // Flag indicating a AOF rewrite operation is on-going fmt.Fprintf(w, "aof_rewrite_in_progress:%d\r\n", boolInt(s.shrinking)) // Flag indicating a AOF rewrite operation is on-going
fmt.Fprintf(w, "aof_last_rewrite_time_sec:%d\r\n", s.lastShrinkDuration.get()/int(time.Second)) // Duration of the last AOF rewrite operation in seconds fmt.Fprintf(w, "aof_last_rewrite_time_sec:%d\r\n", s.lastShrinkDuration.get()/int(time.Second)) // Duration of the last AOF rewrite operation in seconds

View File

@ -7,6 +7,12 @@ export CGO_ENABLED=0
cd tests cd tests
go test -coverpkg=../internal/server -coverprofile=/tmp/coverage.out $GOTEST go test -coverpkg=../internal/server -coverprofile=/tmp/coverage.out $GOTEST
# go test \
# -coverpkg=../internal/... -coverprofile=/tmp/coverage.out \
# -v . -v ../... $GOTEST
go tool cover -html=/tmp/coverage.out -o /tmp/coverage.html go tool cover -html=/tmp/coverage.out -o /tmp/coverage.html
echo "details: file:///tmp/coverage.html" echo "details: file:///tmp/coverage.html"
cd .. cd ..

35
tests/aof_test.go Normal file
View File

@ -0,0 +1,35 @@
package tests
import (
"testing"
)
func subTestAOF(t *testing.T, mc *mockServer) {
runStep(t, mc, "loading", aof_loading_test)
}
func aof_loading_test(mc *mockServer) error {
// aof, err := mc.readAOF()
// if err != nil {
// return err
// }
// aof = append(aof, "asdfasdf\r\n"...)
// aof = nil
// mc2, err := mockOpenServer(MockServerOptions{
// Silent: false,
// Metrics: false,
// AOFData: aof,
// })
// if err != nil {
// return err
// }
// defer mc2.Close()
// time.Sleep(time.Minute)
// `
return nil
}

View File

@ -7,12 +7,12 @@ import (
"log" "log"
"math/rand" "math/rand"
"os" "os"
"path/filepath"
"strings" "strings"
"time" "time"
"github.com/gomodule/redigo/redis" "github.com/gomodule/redigo/redis"
"github.com/tidwall/sjson" "github.com/tidwall/sjson"
"github.com/tidwall/tile38/core"
tlog "github.com/tidwall/tile38/internal/log" tlog "github.com/tidwall/tile38/internal/log"
"github.com/tidwall/tile38/internal/server" "github.com/tidwall/tile38/internal/server"
) )
@ -38,34 +38,55 @@ type mockServer struct {
port int port int
conn redis.Conn conn redis.Conn
ioJSON bool ioJSON bool
dir string
// alt *mockServer // alt *mockServer
} }
func mockOpenServer(silent, metrics bool) (*mockServer, error) { func (mc *mockServer) readAOF() ([]byte, error) {
return os.ReadFile(filepath.Join(mc.dir, "appendonly.aof"))
}
type MockServerOptions struct {
AOFData []byte
Silent bool
Metrics bool
}
func mockOpenServer(opts MockServerOptions) (*mockServer, error) {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
port := rand.Int()%20000 + 20000 port := rand.Int()%20000 + 20000
dir := fmt.Sprintf("data-mock-%d", port) dir := fmt.Sprintf("data-mock-%d", port)
if !silent { if !opts.Silent {
fmt.Printf("Starting test server at port %d\n", port) fmt.Printf("Starting test server at port %d\n", port)
} }
if len(opts.AOFData) > 0 {
if err := os.MkdirAll(dir, 0777); err != nil {
return nil, err
}
err := os.WriteFile(filepath.Join(dir, "appendonly.aof"),
opts.AOFData, 0666)
if err != nil {
return nil, err
}
}
logOutput := io.Discard logOutput := io.Discard
if os.Getenv("PRINTLOG") == "1" { if os.Getenv("PRINTLOG") == "1" {
logOutput = os.Stderr logOutput = os.Stderr
} }
core.DevMode = true
s := &mockServer{port: port} s := &mockServer{port: port}
tlog.SetOutput(logOutput) tlog.SetOutput(logOutput)
go func() { go func() {
opts := server.Options{ sopts := server.Options{
Host: "localhost", Host: "localhost",
Port: port, Port: port,
Dir: dir, Dir: dir,
UseHTTP: true, UseHTTP: true,
DevMode: true,
} }
if metrics { if opts.Metrics {
opts.MetricsAddr = ":4321" sopts.MetricsAddr = ":4321"
} }
if err := server.Serve(opts); err != nil { if err := server.Serve(sopts); err != nil {
log.Fatal(err) log.Fatal(err)
} }
}() }()
@ -73,6 +94,7 @@ func mockOpenServer(silent, metrics bool) (*mockServer, error) {
s.Close() s.Close()
return nil, err return nil, err
} }
s.dir = dir
return s, nil return s, nil
} }

View File

@ -38,7 +38,10 @@ func TestAll(t *testing.T) {
os.Exit(1) os.Exit(1)
}() }()
mc, err := mockOpenServer(false, true) mc, err := mockOpenServer(MockServerOptions{
Silent: false,
Metrics: true,
})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -62,6 +65,7 @@ func TestAll(t *testing.T) {
runSubTest(t, "info", mc, subTestInfo) runSubTest(t, "info", mc, subTestInfo)
runSubTest(t, "timeouts", mc, subTestTimeout) runSubTest(t, "timeouts", mc, subTestTimeout)
runSubTest(t, "metrics", mc, subTestMetrics) runSubTest(t, "metrics", mc, subTestMetrics)
runSubTest(t, "aof", mc, subTestAOF)
} }
func runSubTest(t *testing.T, name string, mc *mockServer, test func(t *testing.T, mc *mockServer)) { func runSubTest(t *testing.T, name string, mc *mockServer, test func(t *testing.T, mc *mockServer)) {
@ -111,7 +115,9 @@ func BenchmarkAll(b *testing.B) {
os.Exit(1) os.Exit(1)
}() }()
mc, err := mockOpenServer(true, true) mc, err := mockOpenServer(MockServerOptions{
Silent: true, Metrics: true,
})
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }