mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #1810 from xiang90/purge
*: support purging old wal/snap files
This commit is contained in:
commit
7beac083ff
@ -63,6 +63,8 @@ var (
|
|||||||
snapCount = fs.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
|
snapCount = fs.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
|
||||||
printVersion = fs.Bool("version", false, "Print the version and exit")
|
printVersion = fs.Bool("version", false, "Print the version and exit")
|
||||||
forceNewCluster = fs.Bool("force-new-cluster", false, "Force to create a new one member cluster")
|
forceNewCluster = fs.Bool("force-new-cluster", false, "Force to create a new one member cluster")
|
||||||
|
maxSnapFiles = fs.Uint("max-snapshots", 5, "Maximum number of snapshot files to retain (0 is unlimited)")
|
||||||
|
maxWalFiles = fs.Uint("max-wals", 5, "Maximum number of wal files to retain (0 is unlimited)")
|
||||||
|
|
||||||
initialCluster = fs.String("initial-cluster", "default=http://localhost:2380,default=http://localhost:7001", "Initial cluster configuration for bootstrapping")
|
initialCluster = fs.String("initial-cluster", "default=http://localhost:2380,default=http://localhost:7001", "Initial cluster configuration for bootstrapping")
|
||||||
initialClusterToken = fs.String("initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during bootstrap")
|
initialClusterToken = fs.String("initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during bootstrap")
|
||||||
@ -280,6 +282,8 @@ func startEtcd() (<-chan struct{}, error) {
|
|||||||
PeerURLs: apurls,
|
PeerURLs: apurls,
|
||||||
DataDir: *dir,
|
DataDir: *dir,
|
||||||
SnapCount: *snapCount,
|
SnapCount: *snapCount,
|
||||||
|
MaxSnapFiles: *maxSnapFiles,
|
||||||
|
MaxWALFiles: *maxWalFiles,
|
||||||
Cluster: cls,
|
Cluster: cls,
|
||||||
DiscoveryURL: *durl,
|
DiscoveryURL: *durl,
|
||||||
DiscoveryProxy: *dproxy,
|
DiscoveryProxy: *dproxy,
|
||||||
|
@ -37,6 +37,8 @@ type ServerConfig struct {
|
|||||||
PeerURLs types.URLs
|
PeerURLs types.URLs
|
||||||
DataDir string
|
DataDir string
|
||||||
SnapCount uint64
|
SnapCount uint64
|
||||||
|
MaxSnapFiles uint
|
||||||
|
MaxWALFiles uint
|
||||||
Cluster *Cluster
|
Cluster *Cluster
|
||||||
NewCluster bool
|
NewCluster bool
|
||||||
ForceNewCluster bool
|
ForceNewCluster bool
|
||||||
|
@ -37,6 +37,7 @@ import (
|
|||||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
"github.com/coreos/etcd/etcdserver/stats"
|
"github.com/coreos/etcd/etcdserver/stats"
|
||||||
"github.com/coreos/etcd/migrate"
|
"github.com/coreos/etcd/migrate"
|
||||||
|
"github.com/coreos/etcd/pkg/fileutil"
|
||||||
"github.com/coreos/etcd/pkg/pbutil"
|
"github.com/coreos/etcd/pkg/pbutil"
|
||||||
"github.com/coreos/etcd/pkg/types"
|
"github.com/coreos/etcd/pkg/types"
|
||||||
"github.com/coreos/etcd/pkg/wait"
|
"github.com/coreos/etcd/pkg/wait"
|
||||||
@ -59,6 +60,8 @@ const (
|
|||||||
|
|
||||||
StoreAdminPrefix = "/0"
|
StoreAdminPrefix = "/0"
|
||||||
StoreKeysPrefix = "/1"
|
StoreKeysPrefix = "/1"
|
||||||
|
|
||||||
|
purgeFileInterval = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -157,6 +160,7 @@ type RaftTimer interface {
|
|||||||
|
|
||||||
// EtcdServer is the production implementation of the Server interface
|
// EtcdServer is the production implementation of the Server interface
|
||||||
type EtcdServer struct {
|
type EtcdServer struct {
|
||||||
|
cfg *ServerConfig
|
||||||
w wait.Wait
|
w wait.Wait
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
@ -301,6 +305,7 @@ func NewServer(cfg *ServerConfig) (*EtcdServer, error) {
|
|||||||
lstats := stats.NewLeaderStats(id.String())
|
lstats := stats.NewLeaderStats(id.String())
|
||||||
|
|
||||||
srv := &EtcdServer{
|
srv := &EtcdServer{
|
||||||
|
cfg: cfg,
|
||||||
store: st,
|
store: st,
|
||||||
node: n,
|
node: n,
|
||||||
raftStorage: s,
|
raftStorage: s,
|
||||||
@ -327,6 +332,7 @@ func NewServer(cfg *ServerConfig) (*EtcdServer, error) {
|
|||||||
func (s *EtcdServer) Start() {
|
func (s *EtcdServer) Start() {
|
||||||
s.start()
|
s.start()
|
||||||
go s.publish(defaultPublishRetryInterval)
|
go s.publish(defaultPublishRetryInterval)
|
||||||
|
go s.purgeFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
// start prepares and starts server in a new goroutine. It is no longer safe to
|
// start prepares and starts server in a new goroutine. It is no longer safe to
|
||||||
@ -346,6 +352,24 @@ func (s *EtcdServer) start() {
|
|||||||
go s.run()
|
go s.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *EtcdServer) purgeFile() {
|
||||||
|
var serrc, werrc <-chan error
|
||||||
|
if s.cfg.MaxSnapFiles > 0 {
|
||||||
|
serrc = fileutil.PurgeFile(s.cfg.SnapDir(), "snap", s.cfg.MaxSnapFiles, purgeFileInterval, s.done)
|
||||||
|
}
|
||||||
|
if s.cfg.MaxWALFiles > 0 {
|
||||||
|
werrc = fileutil.PurgeFile(s.cfg.WALDir(), "wal", s.cfg.MaxWALFiles, purgeFileInterval, s.done)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case e := <-werrc:
|
||||||
|
log.Fatalf("etcdserver: failed to purge wal file %v", e)
|
||||||
|
case e := <-serrc:
|
||||||
|
log.Fatalf("etcdserver: failed to purge snap file %v", e)
|
||||||
|
case <-s.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *EtcdServer) ID() types.ID { return s.id }
|
func (s *EtcdServer) ID() types.ID { return s.id }
|
||||||
|
|
||||||
func (s *EtcdServer) SenderFinder() rafthttp.SenderFinder { return s.sendhub }
|
func (s *EtcdServer) SenderFinder() rafthttp.SenderFinder { return s.sendhub }
|
||||||
|
@ -35,3 +35,17 @@ func IsDirWriteable(dir string) error {
|
|||||||
}
|
}
|
||||||
return os.Remove(f)
|
return os.Remove(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadDir returns the filenames in the given directory.
|
||||||
|
func ReadDir(dirpath string) ([]string, error) {
|
||||||
|
dir, err := os.Open(dirpath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer dir.Close()
|
||||||
|
names, err := dir.Readdirnames(-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
46
pkg/fileutil/purge.go
Normal file
46
pkg/fileutil/purge.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package fileutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PurgeFile(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) <-chan error {
|
||||||
|
errC := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
fnames, err := ReadDir(dirname)
|
||||||
|
if err != nil {
|
||||||
|
errC <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newfnames := make([]string, 0)
|
||||||
|
for _, fname := range fnames {
|
||||||
|
if strings.HasSuffix(fname, suffix) {
|
||||||
|
newfnames = append(newfnames, fname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(newfnames)
|
||||||
|
for len(newfnames) > int(max) {
|
||||||
|
f := path.Join(dirname, newfnames[0])
|
||||||
|
err := os.Remove(f)
|
||||||
|
if err != nil {
|
||||||
|
errC <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("filePurge: successfully remvoed file %s", f)
|
||||||
|
newfnames = newfnames[1:]
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-time.After(interval):
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return errC
|
||||||
|
}
|
50
pkg/fileutil/purge_test.go
Normal file
50
pkg/fileutil/purge_test.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package fileutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPurgeFile(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "purgefile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
_, err := os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop := make(chan struct{})
|
||||||
|
errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
|
||||||
|
for i := 5; i < 10; i++ {
|
||||||
|
_, err := os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
}
|
||||||
|
fnames, err := ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
wnames := []string{"7.test", "8.test", "9.test"}
|
||||||
|
if !reflect.DeepEqual(fnames, wnames) {
|
||||||
|
t.Errorf("filenames = %v, want %v", fnames, wnames)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case err := <-errch:
|
||||||
|
t.Errorf("unexpected purge error %v", err)
|
||||||
|
case <-time.After(time.Millisecond):
|
||||||
|
}
|
||||||
|
close(stop)
|
||||||
|
}
|
20
wal/util.go
20
wal/util.go
@ -19,9 +19,9 @@ package wal
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/fileutil"
|
||||||
"github.com/coreos/etcd/pkg/types"
|
"github.com/coreos/etcd/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func DetectVersion(dirpath string) WalVersion {
|
func DetectVersion(dirpath string) WalVersion {
|
||||||
names, err := readDir(dirpath)
|
names, err := fileutil.ReadDir(dirpath)
|
||||||
if err != nil || len(names) == 0 {
|
if err != nil || len(names) == 0 {
|
||||||
return WALNotExist
|
return WALNotExist
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ func DetectVersion(dirpath string) WalVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Exist(dirpath string) bool {
|
func Exist(dirpath string) bool {
|
||||||
names, err := readDir(dirpath)
|
names, err := fileutil.ReadDir(dirpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -97,20 +97,6 @@ func isValidSeq(names []string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// readDir returns the filenames in wal directory.
|
|
||||||
func readDir(dirpath string) ([]string, error) {
|
|
||||||
dir, err := os.Open(dirpath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer dir.Close()
|
|
||||||
names, err := dir.Readdirnames(-1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return names, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkWalNames(names []string) []string {
|
func checkWalNames(names []string) []string {
|
||||||
wnames := make([]string, 0)
|
wnames := make([]string, 0)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/fileutil"
|
||||||
"github.com/coreos/etcd/pkg/pbutil"
|
"github.com/coreos/etcd/pkg/pbutil"
|
||||||
"github.com/coreos/etcd/raft"
|
"github.com/coreos/etcd/raft"
|
||||||
"github.com/coreos/etcd/raft/raftpb"
|
"github.com/coreos/etcd/raft/raftpb"
|
||||||
@ -110,7 +111,7 @@ func Create(dirpath string, metadata []byte) (*WAL, error) {
|
|||||||
// index. The WAL cannot be appended to before reading out all of its
|
// index. The WAL cannot be appended to before reading out all of its
|
||||||
// previous records.
|
// previous records.
|
||||||
func OpenAtIndex(dirpath string, index uint64) (*WAL, error) {
|
func OpenAtIndex(dirpath string, index uint64) (*WAL, error) {
|
||||||
names, err := readDir(dirpath)
|
names, err := fileutil.ReadDir(dirpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user