grumble, pkg/freezer: more stable freezing on Windows.

This commit is contained in:
Mikkel Krautz 2012-11-18 17:40:18 +01:00
parent 06ba075c41
commit f953ece6f2
5 changed files with 171 additions and 68 deletions

View file

@ -20,69 +20,48 @@ import (
"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.
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"))
func (server *Server) FreezeToFile() error {
// See freeeze_{windows,unix}.go for real implementations.
err := server.freezeToFile()
if err != nil {
return err
}
// Re-open a new log file
if server.running {
// Re-open the freeze log.
err = server.openFreezeLog()
if err != nil {
return err
}
}
return nil
}
// Open a new freeze log
func (server *Server) openFreezeLog() (err error) {
// Open a new freeze log.
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")
err = os.Remove(logfn)
err := os.Remove(logfn)
if os.IsNotExist(err) {
// OK. File does not exist...
// fallthrough
} else if err != nil {
return err
}
server.freezelog, err = freezer.NewLogFile(logfn)
if err != nil {
return err
}
return nil
}
@ -406,12 +385,22 @@ func NewServerFromFrozen(name string) (s *Server, err error) {
path := filepath.Join(Args.DataDir, "servers", name)
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)
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
}
defer r.Close()
buf, err := ioutil.ReadAll(r)
@ -504,7 +493,8 @@ func NewServerFromFrozen(name string) (s *Server, err error) {
}
// 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 {
return nil, err
}
@ -512,6 +502,10 @@ func NewServerFromFrozen(name string) (s *Server, err error) {
for {
values, err := walker.Next()
if err == io.EOF {
err = logFile.Close()
if err != nil {
return nil, err
}
break
} else if err != nil {
return nil, err

49
freeze_unix.go Normal file
View 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
View 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
}

View file

@ -15,7 +15,7 @@ import (
"testing"
)
var testValues []interface{} = []interface{}{
var testValues []proto.Message = []proto.Message{
&ConfigKeyValuePair{Key: proto.String("Foo")},
&BanList{Bans: []*Ban{&Ban{Mask: proto.Uint32(32)}}},
&User{Id: proto.Uint32(0), Name: proto.String("SuperUser")},
@ -97,18 +97,26 @@ func TestLogging(t *testing.T) {
t.Error(err)
return
}
defer l.Close()
defer os.Remove("logging.log")
for _, val := range testValues {
err = l.Put(val)
if err != nil {
t.Error(err)
return
t.Fatal(err)
}
}
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 {
t.Error(err)
return
@ -118,6 +126,10 @@ func TestLogging(t *testing.T) {
for {
entries, err := walker.Next()
if err == io.EOF {
err = f.Close()
if err != nil {
t.Fatal(err)
}
break
} else if err != nil {
t.Error(err)
@ -127,7 +139,10 @@ func TestLogging(t *testing.T) {
t.Error("> 1 entry in log tx")
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]) {
t.Error("proto message mismatch")
}

View file

@ -11,7 +11,6 @@ import (
"hash/crc32"
"io"
"math"
"os"
)
// Checks whether the error err is an EOF
@ -79,19 +78,6 @@ func (txr *txReader) Consumed() int {
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.
func NewReaderWalker(r io.Reader) (walker *Walker, err error) {
walker = new(Walker)