1
0
Fork 0
forked from External/grumble

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" "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
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" "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")
} }

View file

@ -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)