diff --git a/embed/config.go b/embed/config.go index 2f64d927f2a..12d6401d76e 100644 --- a/embed/config.go +++ b/embed/config.go @@ -335,6 +335,10 @@ type Config struct { // Only valid if "logger" option is "capnslog". // WARN: DO NOT USE THIS! LogPkgLevels string `json:"log-package-levels"` + + // UnsafeNoFsync disables all uses of fsync. + // Setting this is unsafe and will cause data loss. + UnsafeNoFsync bool `json:"unsafe-no-fsync"` } // configYAML holds the config suitable for yaml parsing diff --git a/embed/etcd.go b/embed/etcd.go index ac7dbc987fb..75a635b6abd 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -204,6 +204,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { Debug: cfg.Debug, ForceNewCluster: cfg.ForceNewCluster, EnableGRPCGateway: cfg.EnableGRPCGateway, + UnsafeNoFsync: cfg.UnsafeNoFsync, EnableLeaseCheckpoint: cfg.ExperimentalEnableLeaseCheckpoint, CompactionBatchLimit: cfg.ExperimentalCompactionBatchLimit, } diff --git a/etcdmain/config.go b/etcdmain/config.go index ac94418545a..5bcb3b8796a 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -258,6 +258,7 @@ func newConfig() *config { fs.IntVar(&cfg.ec.ExperimentalCompactionBatchLimit, "experimental-compaction-batch-limit", cfg.ec.ExperimentalCompactionBatchLimit, "Sets the maximum revisions deleted in each compaction batch.") // unsafe + fs.BoolVar(&cfg.ec.UnsafeNoFsync, "unsafe-no-fsync", false, "Disables fsync, unsafe, will cause data loss.") fs.BoolVar(&cfg.ec.ForceNewCluster, "force-new-cluster", false, "Force to create a new one member cluster.") // ignored diff --git a/etcdserver/backend.go b/etcdserver/backend.go index 3eace1a33c6..aa443cac86f 100644 --- a/etcdserver/backend.go +++ b/etcdserver/backend.go @@ -31,6 +31,7 @@ import ( func newBackend(cfg ServerConfig) backend.Backend { bcfg := backend.DefaultBackendConfig() bcfg.Path = cfg.backendPath() + bcfg.UnsafeNoFsync = cfg.UnsafeNoFsync if cfg.BackendBatchLimit != 0 { bcfg.BatchLimit = cfg.BackendBatchLimit if cfg.Logger != nil { diff --git a/etcdserver/config.go b/etcdserver/config.go index 88cd721c325..e7b71e7120d 100644 --- a/etcdserver/config.go +++ b/etcdserver/config.go @@ -157,6 +157,10 @@ type ServerConfig struct { LeaseCheckpointInterval time.Duration EnableGRPCGateway bool + + // UnsafeNoFsync disables all uses of fsync. + // Setting this is unsafe and will cause data loss. + UnsafeNoFsync bool `json:"unsafe-no-fsync"` } // VerifyBootstrap sanity-checks the initial config for bootstrap case diff --git a/etcdserver/raft.go b/etcdserver/raft.go index e7cf0729929..df08e3de3fe 100644 --- a/etcdserver/raft.go +++ b/etcdserver/raft.go @@ -465,6 +465,9 @@ func startNode(cfg ServerConfig, cl *membership.RaftCluster, ids []types.ID) (id plog.Panicf("create wal error: %v", err) } } + if cfg.UnsafeNoFsync { + w.SetUnsafeNoFsync() + } peers := make([]raft.Peer, len(ids)) for i, id := range ids { var ctx []byte @@ -527,7 +530,7 @@ func restartNode(cfg ServerConfig, snapshot *raftpb.Snapshot) (types.ID, *member if snapshot != nil { walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term } - w, id, cid, st, ents := readWAL(cfg.Logger, cfg.WALDir(), walsnap) + w, id, cid, st, ents := readWAL(cfg.Logger, cfg.WALDir(), walsnap, cfg.UnsafeNoFsync) if cfg.Logger != nil { cfg.Logger.Info( @@ -582,7 +585,7 @@ func restartAsStandaloneNode(cfg ServerConfig, snapshot *raftpb.Snapshot) (types if snapshot != nil { walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term } - w, id, cid, st, ents := readWAL(cfg.Logger, cfg.WALDir(), walsnap) + w, id, cid, st, ents := readWAL(cfg.Logger, cfg.WALDir(), walsnap, cfg.UnsafeNoFsync) // discard the previously uncommitted entries for i, ent := range ents { diff --git a/etcdserver/storage.go b/etcdserver/storage.go index 9a000b8da66..d87e51af0cb 100644 --- a/etcdserver/storage.go +++ b/etcdserver/storage.go @@ -82,7 +82,7 @@ func (st *storage) Release(snap raftpb.Snapshot) error { // readWAL reads the WAL at the given snap and returns the wal, its latest HardState and cluster ID, and all entries that appear // after the position of the given snap in the WAL. // The snap must have been previously saved to the WAL, or this call will panic. -func readWAL(lg *zap.Logger, waldir string, snap walpb.Snapshot) (w *wal.WAL, id, cid types.ID, st raftpb.HardState, ents []raftpb.Entry) { +func readWAL(lg *zap.Logger, waldir string, snap walpb.Snapshot, unsafeNoFsync bool) (w *wal.WAL, id, cid types.ID, st raftpb.HardState, ents []raftpb.Entry) { var ( err error wmetadata []byte @@ -97,6 +97,9 @@ func readWAL(lg *zap.Logger, waldir string, snap walpb.Snapshot) (w *wal.WAL, id plog.Fatalf("open wal error: %v", err) } } + if unsafeNoFsync { + w.SetUnsafeNoFsync() + } if wmetadata, st, ents, err = w.ReadAll(); err != nil { w.Close() // we can only repair ErrUnexpectedEOF and we never repair twice. diff --git a/mvcc/backend/backend.go b/mvcc/backend/backend.go index 26f196fbff3..523d358d5ae 100644 --- a/mvcc/backend/backend.go +++ b/mvcc/backend/backend.go @@ -123,6 +123,8 @@ type BackendConfig struct { MmapSize uint64 // Logger logs backend-side operations. Logger *zap.Logger + // UnsafeNoFsync disables all uses of fsync. + UnsafeNoFsync bool `json:"unsafe-no-fsync"` } func DefaultBackendConfig() BackendConfig { @@ -150,6 +152,8 @@ func newBackend(bcfg BackendConfig) *backend { } bopts.InitialMmapSize = bcfg.mmapSize() bopts.FreelistType = bcfg.BackendFreelistType + bopts.NoSync = bcfg.UnsafeNoFsync + bopts.NoGrowSync = bcfg.UnsafeNoFsync db, err := bolt.Open(bcfg.Path, 0600, bopts) if err != nil { diff --git a/wal/wal.go b/wal/wal.go index 0451f94d058..883b864011c 100644 --- a/wal/wal.go +++ b/wal/wal.go @@ -84,6 +84,8 @@ type WAL struct { decoder *decoder // decoder to decode records readClose func() error // closer for decode reader + unsafeNoSync bool // if set, do not fsync + mu sync.Mutex enti uint64 // index of the last entry saved to the wal encoder *encoder // encoder to encode records @@ -233,6 +235,10 @@ func Create(lg *zap.Logger, dirpath string, metadata []byte) (*WAL, error) { return w, nil } +func (w *WAL) SetUnsafeNoFsync() { + w.unsafeNoSync = true +} + func (w *WAL) cleanupWAL(lg *zap.Logger) { var err error if err = w.Close(); err != nil { @@ -768,6 +774,9 @@ func (w *WAL) cut() error { } func (w *WAL) sync() error { + if w.unsafeNoSync { + return nil + } if w.encoder != nil { if err := w.encoder.flush(); err != nil { return err