1// Copyright 2017 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package etcdserver
16
17import (
18	"fmt"
19	"os"
20	"time"
21
22	"github.com/coreos/etcd/lease"
23	"github.com/coreos/etcd/mvcc"
24	"github.com/coreos/etcd/mvcc/backend"
25	"github.com/coreos/etcd/raft/raftpb"
26	"github.com/coreos/etcd/snap"
27)
28
29func newBackend(cfg ServerConfig) backend.Backend {
30	bcfg := backend.DefaultBackendConfig()
31	bcfg.Path = cfg.backendPath()
32	if cfg.QuotaBackendBytes > 0 && cfg.QuotaBackendBytes != DefaultQuotaBytes {
33		// permit 10% excess over quota for disarm
34		bcfg.MmapSize = uint64(cfg.QuotaBackendBytes + cfg.QuotaBackendBytes/10)
35	}
36	return backend.New(bcfg)
37}
38
39// openSnapshotBackend renames a snapshot db to the current etcd db and opens it.
40func openSnapshotBackend(cfg ServerConfig, ss *snap.Snapshotter, snapshot raftpb.Snapshot) (backend.Backend, error) {
41	snapPath, err := ss.DBFilePath(snapshot.Metadata.Index)
42	if err != nil {
43		return nil, fmt.Errorf("database snapshot file path error: %v", err)
44	}
45	if err := os.Rename(snapPath, cfg.backendPath()); err != nil {
46		return nil, fmt.Errorf("rename snapshot file error: %v", err)
47	}
48	return openBackend(cfg), nil
49}
50
51// openBackend returns a backend using the current etcd db.
52func openBackend(cfg ServerConfig) backend.Backend {
53	fn := cfg.backendPath()
54	beOpened := make(chan backend.Backend)
55	go func() {
56		beOpened <- newBackend(cfg)
57	}()
58	select {
59	case be := <-beOpened:
60		return be
61	case <-time.After(10 * time.Second):
62		plog.Warningf("another etcd process is using %q and holds the file lock, or loading backend file is taking >10 seconds", fn)
63		plog.Warningf("waiting for it to exit before starting...")
64	}
65	return <-beOpened
66}
67
68// recoverBackendSnapshot recovers the DB from a snapshot in case etcd crashes
69// before updating the backend db after persisting raft snapshot to disk,
70// violating the invariant snapshot.Metadata.Index < db.consistentIndex. In this
71// case, replace the db with the snapshot db sent by the leader.
72func recoverSnapshotBackend(cfg ServerConfig, oldbe backend.Backend, snapshot raftpb.Snapshot) (backend.Backend, error) {
73	var cIndex consistentIndex
74	kv := mvcc.New(oldbe, &lease.FakeLessor{}, nil, &cIndex)
75	defer kv.Close()
76	if snapshot.Metadata.Index <= kv.ConsistentIndex() {
77		return oldbe, nil
78	}
79	oldbe.Close()
80	return openSnapshotBackend(cfg, snap.New(cfg.SnapDir()), snapshot)
81}
82