blob: 295d95299a208c2c606c1b7856dc1dbb60dc6cfd [file] [log] [blame]
khenaidooffe076b2019-01-15 16:08:08 -05001// Copyright 2015 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 "context"
19 "fmt"
20 "path/filepath"
21 "sort"
22 "strings"
23 "time"
24
25 "github.com/coreos/etcd/pkg/netutil"
26 "github.com/coreos/etcd/pkg/transport"
27 "github.com/coreos/etcd/pkg/types"
28)
29
30// ServerConfig holds the configuration of etcd as taken from the command line or discovery.
31type ServerConfig struct {
32 Name string
33 DiscoveryURL string
34 DiscoveryProxy string
35 ClientURLs types.URLs
36 PeerURLs types.URLs
37 DataDir string
38 // DedicatedWALDir config will make the etcd to write the WAL to the WALDir
39 // rather than the dataDir/member/wal.
40 DedicatedWALDir string
41 SnapCount uint64
42 MaxSnapFiles uint
43 MaxWALFiles uint
44 InitialPeerURLsMap types.URLsMap
45 InitialClusterToken string
46 NewCluster bool
47 ForceNewCluster bool
48 PeerTLSInfo transport.TLSInfo
49
50 TickMs uint
51 ElectionTicks int
52
53 // InitialElectionTickAdvance is true, then local member fast-forwards
54 // election ticks to speed up "initial" leader election trigger. This
55 // benefits the case of larger election ticks. For instance, cross
56 // datacenter deployment may require longer election timeout of 10-second.
57 // If true, local node does not need wait up to 10-second. Instead,
58 // forwards its election ticks to 8-second, and have only 2-second left
59 // before leader election.
60 //
61 // Major assumptions are that:
62 // - cluster has no active leader thus advancing ticks enables faster
63 // leader election, or
64 // - cluster already has an established leader, and rejoining follower
65 // is likely to receive heartbeats from the leader after tick advance
66 // and before election timeout.
67 //
68 // However, when network from leader to rejoining follower is congested,
69 // and the follower does not receive leader heartbeat within left election
70 // ticks, disruptive election has to happen thus affecting cluster
71 // availabilities.
72 //
73 // Disabling this would slow down initial bootstrap process for cross
74 // datacenter deployments. Make your own tradeoffs by configuring
75 // --initial-election-tick-advance at the cost of slow initial bootstrap.
76 //
77 // If single-node, it advances ticks regardless.
78 //
79 // See https://github.com/coreos/etcd/issues/9333 for more detail.
80 InitialElectionTickAdvance bool
81
82 BootstrapTimeout time.Duration
83
84 AutoCompactionRetention time.Duration
85 AutoCompactionMode string
86 QuotaBackendBytes int64
87 MaxTxnOps uint
88
89 // MaxRequestBytes is the maximum request size to send over raft.
90 MaxRequestBytes uint
91
92 StrictReconfigCheck bool
93
94 // ClientCertAuthEnabled is true when cert has been signed by the client CA.
95 ClientCertAuthEnabled bool
96
97 AuthToken string
98
99 // InitialCorruptCheck is true to check data corruption on boot
100 // before serving any peer/client traffic.
101 InitialCorruptCheck bool
102 CorruptCheckTime time.Duration
103
104 Debug bool
105}
106
107// VerifyBootstrap sanity-checks the initial config for bootstrap case
108// and returns an error for things that should never happen.
109func (c *ServerConfig) VerifyBootstrap() error {
110 if err := c.hasLocalMember(); err != nil {
111 return err
112 }
113 if err := c.advertiseMatchesCluster(); err != nil {
114 return err
115 }
116 if checkDuplicateURL(c.InitialPeerURLsMap) {
117 return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
118 }
119 if c.InitialPeerURLsMap.String() == "" && c.DiscoveryURL == "" {
120 return fmt.Errorf("initial cluster unset and no discovery URL found")
121 }
122 return nil
123}
124
125// VerifyJoinExisting sanity-checks the initial config for join existing cluster
126// case and returns an error for things that should never happen.
127func (c *ServerConfig) VerifyJoinExisting() error {
128 // The member has announced its peer urls to the cluster before starting; no need to
129 // set the configuration again.
130 if err := c.hasLocalMember(); err != nil {
131 return err
132 }
133 if checkDuplicateURL(c.InitialPeerURLsMap) {
134 return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
135 }
136 if c.DiscoveryURL != "" {
137 return fmt.Errorf("discovery URL should not be set when joining existing initial cluster")
138 }
139 return nil
140}
141
142// hasLocalMember checks that the cluster at least contains the local server.
143func (c *ServerConfig) hasLocalMember() error {
144 if urls := c.InitialPeerURLsMap[c.Name]; urls == nil {
145 return fmt.Errorf("couldn't find local name %q in the initial cluster configuration", c.Name)
146 }
147 return nil
148}
149
150// advertiseMatchesCluster confirms peer URLs match those in the cluster peer list.
151func (c *ServerConfig) advertiseMatchesCluster() error {
152 urls, apurls := c.InitialPeerURLsMap[c.Name], c.PeerURLs.StringSlice()
153 urls.Sort()
154 sort.Strings(apurls)
155 ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
156 defer cancel()
157 ok, err := netutil.URLStringsEqual(ctx, apurls, urls.StringSlice())
158 if ok {
159 return nil
160 }
161
162 initMap, apMap := make(map[string]struct{}), make(map[string]struct{})
163 for _, url := range c.PeerURLs {
164 apMap[url.String()] = struct{}{}
165 }
166 for _, url := range c.InitialPeerURLsMap[c.Name] {
167 initMap[url.String()] = struct{}{}
168 }
169
170 missing := []string{}
171 for url := range initMap {
172 if _, ok := apMap[url]; !ok {
173 missing = append(missing, url)
174 }
175 }
176 if len(missing) > 0 {
177 for i := range missing {
178 missing[i] = c.Name + "=" + missing[i]
179 }
180 mstr := strings.Join(missing, ",")
181 apStr := strings.Join(apurls, ",")
182 return fmt.Errorf("--initial-cluster has %s but missing from --initial-advertise-peer-urls=%s (%v)", mstr, apStr, err)
183 }
184
185 for url := range apMap {
186 if _, ok := initMap[url]; !ok {
187 missing = append(missing, url)
188 }
189 }
190 if len(missing) > 0 {
191 mstr := strings.Join(missing, ",")
192 umap := types.URLsMap(map[string]types.URLs{c.Name: c.PeerURLs})
193 return fmt.Errorf("--initial-advertise-peer-urls has %s but missing from --initial-cluster=%s", mstr, umap.String())
194 }
195
196 // resolved URLs from "--initial-advertise-peer-urls" and "--initial-cluster" did not match or failed
197 apStr := strings.Join(apurls, ",")
198 umap := types.URLsMap(map[string]types.URLs{c.Name: c.PeerURLs})
199 return fmt.Errorf("failed to resolve %s to match --initial-cluster=%s (%v)", apStr, umap.String(), err)
200}
201
202func (c *ServerConfig) MemberDir() string { return filepath.Join(c.DataDir, "member") }
203
204func (c *ServerConfig) WALDir() string {
205 if c.DedicatedWALDir != "" {
206 return c.DedicatedWALDir
207 }
208 return filepath.Join(c.MemberDir(), "wal")
209}
210
211func (c *ServerConfig) SnapDir() string { return filepath.Join(c.MemberDir(), "snap") }
212
213func (c *ServerConfig) ShouldDiscover() bool { return c.DiscoveryURL != "" }
214
215// ReqTimeout returns timeout for request to finish.
216func (c *ServerConfig) ReqTimeout() time.Duration {
217 // 5s for queue waiting, computation and disk IO delay
218 // + 2 * election timeout for possible leader election
219 return 5*time.Second + 2*time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond
220}
221
222func (c *ServerConfig) electionTimeout() time.Duration {
223 return time.Duration(c.ElectionTicks*int(c.TickMs)) * time.Millisecond
224}
225
226func (c *ServerConfig) peerDialTimeout() time.Duration {
227 // 1s for queue wait and election timeout
228 return time.Second + time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond
229}
230
231func (c *ServerConfig) PrintWithInitial() { c.print(true) }
232
233func (c *ServerConfig) Print() { c.print(false) }
234
235func (c *ServerConfig) print(initial bool) {
236 plog.Infof("name = %s", c.Name)
237 if c.ForceNewCluster {
238 plog.Infof("force new cluster")
239 }
240 plog.Infof("data dir = %s", c.DataDir)
241 plog.Infof("member dir = %s", c.MemberDir())
242 if c.DedicatedWALDir != "" {
243 plog.Infof("dedicated WAL dir = %s", c.DedicatedWALDir)
244 }
245 plog.Infof("heartbeat = %dms", c.TickMs)
246 plog.Infof("election = %dms", c.ElectionTicks*int(c.TickMs))
247 plog.Infof("snapshot count = %d", c.SnapCount)
248 if len(c.DiscoveryURL) != 0 {
249 plog.Infof("discovery URL= %s", c.DiscoveryURL)
250 if len(c.DiscoveryProxy) != 0 {
251 plog.Infof("discovery proxy = %s", c.DiscoveryProxy)
252 }
253 }
254 plog.Infof("advertise client URLs = %s", c.ClientURLs)
255 if initial {
256 plog.Infof("initial advertise peer URLs = %s", c.PeerURLs)
257 plog.Infof("initial cluster = %s", c.InitialPeerURLsMap)
258 }
259}
260
261func checkDuplicateURL(urlsmap types.URLsMap) bool {
262 um := make(map[string]bool)
263 for _, urls := range urlsmap {
264 for _, url := range urls {
265 u := url.String()
266 if um[u] {
267 return true
268 }
269 um[u] = true
270 }
271 }
272 return false
273}
274
275func (c *ServerConfig) bootstrapTimeout() time.Duration {
276 if c.BootstrapTimeout != 0 {
277 return c.BootstrapTimeout
278 }
279 return time.Second
280}
281
282func (c *ServerConfig) backendPath() string { return filepath.Join(c.SnapDir(), "db") }