forked from External/grumble
grumble, pkg/freezer: more stable freezing on Windows.
This commit is contained in:
parent
06ba075c41
commit
f953ece6f2
5 changed files with 171 additions and 68 deletions
90
freeze.go
90
freeze.go
|
|
@ -20,69 +20,48 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Freeze a server to disk, and re-open the log, if needed.
|
// Freeze a server to disk and closes the log file.
|
||||||
// This must be called from within the Server's synchronous handler.
|
// This must be called from within the Server's synchronous handler.
|
||||||
func (server *Server) FreezeToFile() (err error) {
|
func (server *Server) FreezeToFile() error {
|
||||||
// Close the log file, if it's open
|
// See freeeze_{windows,unix}.go for real implementations.
|
||||||
if server.freezelog != nil {
|
err := server.freezeToFile()
|
||||||
err = server.freezelog.Close()
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if server.running {
|
||||||
|
// Re-open the freeze log.
|
||||||
|
err = server.openFreezeLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the whole server is synced to disk
|
|
||||||
fs, err := server.Freeze()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f, err := ioutil.TempFile(filepath.Join(Args.DataDir, "servers", strconv.FormatInt(server.Id, 10)), ".main.fz_")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
buf, err := proto.Marshal(fs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = f.Write(buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = f.Sync()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = f.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = os.Rename(f.Name(), filepath.Join(Args.DataDir, "servers", strconv.FormatInt(server.Id, 10), "main.fz"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-open a new log file
|
|
||||||
err = server.openFreezeLog()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open a new freeze log
|
// Open a new freeze log.
|
||||||
func (server *Server) openFreezeLog() (err error) {
|
func (server *Server) openFreezeLog() error {
|
||||||
|
if server.freezelog != nil {
|
||||||
|
err := server.freezelog.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logfn := filepath.Join(Args.DataDir, "servers", strconv.FormatInt(server.Id, 10), "log.fz")
|
logfn := filepath.Join(Args.DataDir, "servers", strconv.FormatInt(server.Id, 10), "log.fz")
|
||||||
err = os.Remove(logfn)
|
err := os.Remove(logfn)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
// OK. File does not exist...
|
// fallthrough
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
server.freezelog, err = freezer.NewLogFile(logfn)
|
server.freezelog, err = freezer.NewLogFile(logfn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -406,10 +385,20 @@ func NewServerFromFrozen(name string) (s *Server, err error) {
|
||||||
|
|
||||||
path := filepath.Join(Args.DataDir, "servers", name)
|
path := filepath.Join(Args.DataDir, "servers", name)
|
||||||
mainFile := filepath.Join(path, "main.fz")
|
mainFile := filepath.Join(path, "main.fz")
|
||||||
logFile := filepath.Join(path, "log.fz")
|
backupFile := filepath.Join(path, "backup.fz")
|
||||||
|
logFn := filepath.Join(path, "log.fz")
|
||||||
|
|
||||||
r, err := os.Open(mainFile)
|
r, err := os.Open(mainFile)
|
||||||
if err != nil {
|
if os.IsNotExist(err) {
|
||||||
|
err = os.Rename(backupFile, mainFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r, err = os.Open(mainFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
@ -504,7 +493,8 @@ func NewServerFromFrozen(name string) (s *Server, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to walk the stored log file
|
// Attempt to walk the stored log file
|
||||||
walker, err := freezer.NewFileWalker(logFile)
|
logFile, err := os.Open(logFn)
|
||||||
|
walker, err := freezer.NewReaderWalker(logFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -512,6 +502,10 @@ func NewServerFromFrozen(name string) (s *Server, err error) {
|
||||||
for {
|
for {
|
||||||
values, err := walker.Next()
|
values, err := walker.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
|
err = logFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
49
freeze_unix.go
Normal file
49
freeze_unix.go
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (c) 2012 The Grumble Authors
|
||||||
|
// The use of this source code is goverened by a BSD-style
|
||||||
|
// license that can be found in the LICENSE-file.
|
||||||
|
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func (server *Server) freezeToFile() (err error) {
|
||||||
|
// Close the log file, if it's open
|
||||||
|
if server.freezelog != nil {
|
||||||
|
err = server.freezelog.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the whole server is synced to disk
|
||||||
|
fs, err := server.Freeze()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := ioutil.TempFile(filepath.Join(Args.DataDir, "servers", strconv.FormatInt(server.Id, 10)), ".main.fz_")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf, err := proto.Marshal(fs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = f.Sync()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.Rename(f.Name(), filepath.Join(Args.DataDir, "servers", strconv.FormatInt(server.Id, 10), "main.fz"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
59
freeze_windows.go
Normal file
59
freeze_windows.go
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright (c) 2012 The Grumble Authors
|
||||||
|
// The use of this source code is goverened by a BSD-style
|
||||||
|
// license that can be found in the LICENSE-file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.google.com/p/goprotobuf/proto"
|
||||||
|
"mumbleapp.com/grumble/pkg/replacefile"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (server *Server) freezeToFile() (err error) {
|
||||||
|
// Close the log file, if it's open
|
||||||
|
if server.freezelog != nil {
|
||||||
|
err = server.freezelog.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the whole server is synced to disk
|
||||||
|
fs, err := server.Freeze()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := ioutil.TempFile(filepath.Join(Args.DataDir, "servers", strconv.FormatInt(server.Id, 10)), ".main.fz_")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf, err := proto.Marshal(fs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = f.Sync()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
src := f.Name()
|
||||||
|
dst := filepath.Join(Args.DataDir, "servers", strconv.FormatInt(server.Id, 10), "main.fz")
|
||||||
|
backup := filepath.Join(Args.DataDir, "servers", strconv.FormatInt(server.Id, 10), "backup.fz")
|
||||||
|
err = replacefile.ReplaceFile(dst, src, backup, replacefile.Flag(0))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testValues []interface{} = []interface{}{
|
var testValues []proto.Message = []proto.Message{
|
||||||
&ConfigKeyValuePair{Key: proto.String("Foo")},
|
&ConfigKeyValuePair{Key: proto.String("Foo")},
|
||||||
&BanList{Bans: []*Ban{&Ban{Mask: proto.Uint32(32)}}},
|
&BanList{Bans: []*Ban{&Ban{Mask: proto.Uint32(32)}}},
|
||||||
&User{Id: proto.Uint32(0), Name: proto.String("SuperUser")},
|
&User{Id: proto.Uint32(0), Name: proto.String("SuperUser")},
|
||||||
|
|
@ -97,18 +97,26 @@ func TestLogging(t *testing.T) {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer l.Close()
|
|
||||||
defer os.Remove("logging.log")
|
defer os.Remove("logging.log")
|
||||||
|
|
||||||
for _, val := range testValues {
|
for _, val := range testValues {
|
||||||
err = l.Put(val)
|
err = l.Put(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
walker, err := NewFileWalker("logging.log")
|
err = l.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open("logging.log")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
walker, err := NewReaderWalker(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
|
@ -118,6 +126,10 @@ func TestLogging(t *testing.T) {
|
||||||
for {
|
for {
|
||||||
entries, err := walker.Next()
|
entries, err := walker.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -127,7 +139,10 @@ func TestLogging(t *testing.T) {
|
||||||
t.Error("> 1 entry in log tx")
|
t.Error("> 1 entry in log tx")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val := entries[0]
|
val, ok := entries[0].(proto.Message)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("val does not implement proto.Message")
|
||||||
|
}
|
||||||
if !proto.Equal(val, testValues[i]) {
|
if !proto.Equal(val, testValues[i]) {
|
||||||
t.Error("proto message mismatch")
|
t.Error("proto message mismatch")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checks whether the error err is an EOF
|
// Checks whether the error err is an EOF
|
||||||
|
|
@ -79,19 +78,6 @@ func (txr *txReader) Consumed() int {
|
||||||
return txr.consumed
|
return txr.consumed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Walker that iterates over the entries of the given log file.
|
|
||||||
func NewFileWalker(fn string) (walker *Walker, err error) {
|
|
||||||
f, err := os.Open(fn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
walker = new(Walker)
|
|
||||||
walker.r = f
|
|
||||||
|
|
||||||
return walker, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new Walker that iterates over the log entries of a given Reader.
|
// Create a new Walker that iterates over the log entries of a given Reader.
|
||||||
func NewReaderWalker(r io.Reader) (walker *Walker, err error) {
|
func NewReaderWalker(r io.Reader) (walker *Walker, err error) {
|
||||||
walker = new(Walker)
|
walker = new(Walker)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue