Compare commits

...

6 Commits

Author SHA1 Message Date
9481945228 *: bump to v2.0.5 2015-03-11 11:33:43 -07:00
e13b09e4d9 wal: fix ReleaseLockTo
ReleaseLockTo should not release the lock on the WAL
segment that is right before the given index. When
restarting etcd, etcd needs to read from the WAL segment
that has a smaller index than the snapshot index.

The correct behavior is that ReleaseLockTo releases
the locks w is holding so that w only holds one lock
that has an index smaller than the given index.
2015-03-10 09:45:46 -07:00
78e0149f41 raft: do not reset vote if term is not changed
raft MUST keep the voting information for the same term. reset
should not reset vote if term is not changed.
2015-03-10 09:42:45 -07:00
4c86ab4868 pkg/transport: fix downgrade https to http bug in transport
If the TLS config is empty, etcd downgrades https to http without a warning.
This commit avoid the downgrade and stoping etcd from bootstrap if it cannot
listen on TLS.
2015-03-10 09:39:01 -07:00
59327bab47 pkg/transport: set the maxIdleConnsPerHost to -1
for transport that are using timeout connections, we set the
maxIdleConnsPerHost to -1. The default transport does not clear
the timeout for the connections it sets to be idle. So the connections
with timeout cannot be reused.
2015-03-10 09:38:39 -07:00
62ed1ebf03 Documentation: fix "Missing infra1="
Documentation: fix "Missing infra1="
2015-03-10 09:38:27 -07:00
10 changed files with 126 additions and 25 deletions

View File

@ -30,7 +30,7 @@ ETCD_INITIAL_CLUSTER_STATE=new
``` ```
``` ```
-initial-cluster infra0=http://10.0.1.10:2380,http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \ -initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state new -initial-cluster-state new
``` ```

View File

@ -31,7 +31,10 @@ func NewListener(addr string, scheme string, info TLSInfo) (net.Listener, error)
return nil, err return nil, err
} }
if !info.Empty() && scheme == "https" { if scheme == "https" {
if info.Empty() {
return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", scheme+"://"+addr)
}
cfg, err := info.ServerConfig() cfg, err := info.ServerConfig()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -70,6 +70,13 @@ func TestNewListenerTLSInfo(t *testing.T) {
} }
} }
func TestNewListenerTLSEmptyInfo(t *testing.T) {
_, err := NewListener("127.0.0.1:0", "https", TLSInfo{})
if err == nil {
t.Errorf("err = nil, want not presented error")
}
}
func TestNewListenerTLSInfoNonexist(t *testing.T) { func TestNewListenerTLSInfoNonexist(t *testing.T) {
tlsInfo := TLSInfo{CertFile: "@badname", KeyFile: "@badname"} tlsInfo := TLSInfo{CertFile: "@badname", KeyFile: "@badname"}
_, err := NewListener("127.0.0.1:0", "https", tlsInfo) _, err := NewListener("127.0.0.1:0", "https", tlsInfo)

View File

@ -28,6 +28,9 @@ func NewTimeoutTransport(info TLSInfo, rdtimeoutd, wtimeoutd time.Duration) (*ht
if err != nil { if err != nil {
return nil, err return nil, err
} }
// the timeouted connection will tiemout soon after it is idle.
// it should not be put back to http transport as an idle connection for future usage.
tr.MaxIdleConnsPerHost = -1
tr.Dial = (&rwTimeoutDialer{ tr.Dial = (&rwTimeoutDialer{
Dialer: net.Dialer{ Dialer: net.Dialer{
Timeout: 30 * time.Second, Timeout: 30 * time.Second,

View File

@ -15,6 +15,8 @@
package transport package transport
import ( import (
"bytes"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -28,7 +30,12 @@ func TestNewTimeoutTransport(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected NewTimeoutTransport error: %v", err) t.Fatalf("unexpected NewTimeoutTransport error: %v", err)
} }
srv := httptest.NewServer(http.NotFoundHandler())
remoteAddr := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(r.RemoteAddr))
}
srv := httptest.NewServer(http.HandlerFunc(remoteAddr))
defer srv.Close() defer srv.Close()
conn, err := tr.Dial("tcp", srv.Listener.Addr().String()) conn, err := tr.Dial("tcp", srv.Listener.Addr().String())
if err != nil { if err != nil {
@ -46,4 +53,33 @@ func TestNewTimeoutTransport(t *testing.T) {
if tconn.wtimeoutd != time.Hour { if tconn.wtimeoutd != time.Hour {
t.Errorf("write timeout = %s, want %s", tconn.wtimeoutd, time.Hour) t.Errorf("write timeout = %s, want %s", tconn.wtimeoutd, time.Hour)
} }
// ensure not reuse timeout connection
req, err := http.NewRequest("GET", srv.URL, nil)
if err != nil {
t.Fatalf("unexpected err %v", err)
}
resp, err := tr.RoundTrip(req)
if err != nil {
t.Fatalf("unexpected err %v", err)
}
addr0, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
t.Fatalf("unexpected err %v", err)
}
resp, err = tr.RoundTrip(req)
if err != nil {
t.Fatalf("unexpected err %v", err)
}
addr1, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
t.Fatalf("unexpected err %v", err)
}
if bytes.Equal(addr0, addr1) {
t.Errorf("addr0 = %s addr1= %s, want not equal", string(addr0), string(addr1))
}
} }

View File

@ -304,7 +304,7 @@ func TestNodeStart(t *testing.T) {
wants := []Ready{ wants := []Ready{
{ {
SoftState: &SoftState{Lead: 1, RaftState: StateLeader}, SoftState: &SoftState{Lead: 1, RaftState: StateLeader},
HardState: raftpb.HardState{Term: 2, Commit: 2}, HardState: raftpb.HardState{Term: 2, Commit: 2, Vote: 1},
Entries: []raftpb.Entry{ Entries: []raftpb.Entry{
{Type: raftpb.EntryConfChange, Term: 1, Index: 1, Data: ccdata}, {Type: raftpb.EntryConfChange, Term: 1, Index: 1, Data: ccdata},
{Term: 2, Index: 2}, {Term: 2, Index: 2},
@ -315,7 +315,7 @@ func TestNodeStart(t *testing.T) {
}, },
}, },
{ {
HardState: raftpb.HardState{Term: 2, Commit: 3}, HardState: raftpb.HardState{Term: 2, Commit: 3, Vote: 1},
Entries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}}, Entries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}},
CommittedEntries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}}, CommittedEntries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}},
}, },

View File

@ -306,9 +306,11 @@ func (r *raft) maybeCommit() bool {
} }
func (r *raft) reset(term uint64) { func (r *raft) reset(term uint64) {
r.Term = term if r.Term != term {
r.Term = term
r.Vote = None
}
r.lead = None r.lead = None
r.Vote = None
r.elapsed = 0 r.elapsed = 0
r.votes = make(map[uint64]bool) r.votes = make(map[uint64]bool)
for i := range r.prs { for i := range r.prs {

View File

@ -15,6 +15,6 @@
package version package version
var ( var (
Version = "2.0.4+git" Version = "2.0.5"
InternalVersion = "2" InternalVersion = "2"
) )

View File

@ -320,27 +320,34 @@ func (w *WAL) sync() error {
return w.f.Sync() return w.f.Sync()
} }
// ReleaseLockTo releases the locks w is holding, which // ReleaseLockTo releases the locks, which has smaller index than the given index
// have index smaller or equal to the given index. // except the largest one among them.
// For example, if WAL is holding lock 1,2,3,4,5,6, ReleaseLockTo(4) will release
// lock 1,2 but keep 3. ReleaseLockTo(5) will release 1,2,3 but keep 4.
func (w *WAL) ReleaseLockTo(index uint64) error { func (w *WAL) ReleaseLockTo(index uint64) error {
for _, l := range w.locks { var smaller int
_, i, err := parseWalName(path.Base(l.Name()))
for i, l := range w.locks {
_, lockIndex, err := parseWalName(path.Base(l.Name()))
if err != nil { if err != nil {
return err return err
} }
if i > index { if lockIndex >= index {
return nil smaller = i - 1
break
} }
err = l.Unlock()
if err != nil {
return err
}
err = l.Destroy()
if err != nil {
return err
}
w.locks = w.locks[1:]
} }
if smaller <= 0 {
return nil
}
for i := 0; i < smaller; i++ {
w.locks[i].Unlock()
w.locks[i].Destroy()
}
w.locks = w.locks[smaller:]
return nil return nil
} }

View File

@ -435,6 +435,7 @@ func TestOpenNotInUse(t *testing.T) {
unlockIndex := uint64(5) unlockIndex := uint64(5)
w.ReleaseLockTo(unlockIndex) w.ReleaseLockTo(unlockIndex)
// 1,2,3 are avaliable.
w2, err := OpenNotInUse(p, walpb.Snapshot{}) w2, err := OpenNotInUse(p, walpb.Snapshot{})
defer w2.Close() defer w2.Close()
if err != nil { if err != nil {
@ -444,8 +445,8 @@ func TestOpenNotInUse(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("err = %v, want nil", err) t.Fatalf("err = %v, want nil", err)
} }
if g := ents[len(ents)-1].Index; g != unlockIndex { if g := ents[len(ents)-1].Index; g != unlockIndex-2 {
t.Errorf("last index read = %d, want %d", g, unlockIndex) t.Errorf("last index read = %d, want %d", g, unlockIndex-2)
} }
} }
@ -462,3 +463,45 @@ func TestSaveEmpty(t *testing.T) {
t.Errorf("buf.Bytes = %d, want 0", len(buf.Bytes())) t.Errorf("buf.Bytes = %d, want 0", len(buf.Bytes()))
} }
} }
func TestReleaseLockTo(t *testing.T) {
p, err := ioutil.TempDir(os.TempDir(), "waltest")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(p)
// create WAL
w, err := Create(p, nil)
defer w.Close()
if err != nil {
t.Fatal(err)
}
// make 10 seperate files
for i := 0; i < 10; i++ {
es := []raftpb.Entry{{Index: uint64(i)}}
if err = w.Save(raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
if err = w.Cut(); err != nil {
t.Fatal(err)
}
}
// release the lock to 5
unlockIndex := uint64(5)
w.ReleaseLockTo(unlockIndex)
// expected remaining are 4,5,6,7,8,9,10
if len(w.locks) != 7 {
t.Errorf("len(w.locks) = %d, want %d", len(w.locks), 7)
}
for i, l := range w.locks {
_, lockIndex, err := parseWalName(path.Base(l.Name()))
if err != nil {
t.Fatal(err)
}
if lockIndex != uint64(i+4) {
t.Errorf("#%d: lockindex = %d, want %d", i, lockIndex, uint64(i+4))
}
}
}