diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7e0956c6d..c60f4fa3b 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -4,7 +4,7 @@ on: name: Nightly jobs: monkey-test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: target: [race-monkey-test, race-ondisk-monkey-test, memfs-monkey-test, memfs-ondisk-monkey-test] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d37b5006..4270c81d3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ on: [push, pull_request] name: Test jobs: unit-test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Install Go uses: actions/setup-go@v3 @@ -17,7 +17,7 @@ jobs: make actions-test bash <(curl -s https://codecov.io/bash) -t $CODECOV_TOKEN race-unit-test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Install Go uses: actions/setup-go@v3 @@ -28,7 +28,7 @@ jobs: - name: Test run: RACE=1 make test memfs-unit-test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Install Go uses: actions/setup-go@v3 @@ -39,7 +39,7 @@ jobs: - name: Test run: MEMFS_TEST=1 make test go1-18-unit-test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Install Go uses: actions/setup-go@v3 @@ -61,7 +61,7 @@ jobs: - name: Test run: make test build-tools: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Install Go uses: actions/setup-go@v3 @@ -72,7 +72,7 @@ jobs: - name: Build run: make tools build-examples: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Install Go uses: actions/setup-go@v3 @@ -85,7 +85,7 @@ jobs: GOPROXY=direct go get github.com/lni/dragonboat/v4@master make static-check: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/Makefile b/Makefile index 7e3098e69..a8066b24a 100644 --- a/Makefile +++ b/Makefile @@ -206,11 +206,11 @@ tools-checkdisk: ############################################################################### .PHONY: install-static-check-tools install-static-check-tools: - @go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.50.1 + @go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.2 CHECKED_PKGS=$(shell go list ./...) CHECKED_DIRS=$(subst $(PKGNAME), ,$(subst $(PKGNAME)/, ,$(CHECKED_PKGS))) . -EXTRA_LINTERS=-E misspell -E rowserrcheck -E depguard -E unconvert \ +EXTRA_LINTERS=-E misspell -E rowserrcheck -E unconvert \ -E prealloc -E stylecheck .PHONY: static-check static-check: diff --git a/config/config.go b/config/config.go index cd299211f..91cf547d9 100644 --- a/config/config.go +++ b/config/config.go @@ -118,8 +118,8 @@ type Config struct { // SyncRequestSnapshot methods to manually request snapshots. SnapshotEntries uint64 // CompactionOverhead defines the number of most recent entries to keep after - // each Raft log compaction. Raft log compaction is performance automatically - // every time when a snapshot is created. + // each Raft log compaction. Raft log compaction is performed automatically + // every time a snapshot is created. // // For example, when a snapshot is created at let's say index 10,000, then all // Raft log entries with index <= 10,000 can be removed from that node as they @@ -128,8 +128,8 @@ type Config struct { // have to be sent to the follower if the follower requires any Raft log entry // at index <= 10,000. When CompactionOverhead is set to say 500, Dragonboat // then compacts the Raft log up to index 9,500 and keeps Raft log entries - // between index (9,500, 1,0000]. As a result, the node can still replicate - // Raft log entries between index (9,500, 1,0000] to other peers and only fall + // between index (9,500, 10,000]. As a result, the node can still replicate + // Raft log entries between index (9,500, 10,000] to other peers and only fall // back to stream the full snapshot if any Raft log entry with index <= 9,500 // is required to be replicated. CompactionOverhead uint64 diff --git a/go.mod b/go.mod index 124e4deda..e8bb32677 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/pierrec/lz4/v4 v4.1.14 github.com/stretchr/testify v1.7.0 golang.org/x/exp v0.0.0-20200513190911-00229845015e - golang.org/x/sys v0.3.0 + golang.org/x/sys v0.12.0 ) require ( @@ -42,9 +42,9 @@ require ( github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/valyala/fastrand v1.1.0 // indirect github.com/valyala/histogram v1.2.0 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/net v0.0.0-20211008194852-3b03d305991f // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + golang.org/x/crypto v0.13.0 // indirect + golang.org/x/net v0.15.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) go 1.17 diff --git a/go.sum b/go.sum index 8685d53a2..daeabd21d 100644 --- a/go.sum +++ b/go.sum @@ -321,6 +321,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -363,6 +365,10 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211008194852-3b03d305991f h1:1scJEYZBaF48BaG6tYbtxmLcXqwYGSfGcMoStTqkkIw= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -405,6 +411,8 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -486,6 +494,8 @@ gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/transport/tcp.go b/internal/transport/tcp.go index dc96fe26d..af1fe7992 100644 --- a/internal/transport/tcp.go +++ b/internal/transport/tcp.go @@ -435,15 +435,23 @@ func (t *TCP) Start() error { panic(err) } var once sync.Once + connCloseCh := make(chan struct{}) closeFn := func() { once.Do(func() { + select { + case connCloseCh <- struct{}{}: + default: + } if err := conn.Close(); err != nil { plog.Errorf("failed to close the connection %v", err) } }) } t.connStopper.RunWorker(func() { - <-t.stopper.ShouldStop() + select { + case <-t.stopper.ShouldStop(): + case <-connCloseCh: + } closeFn() }) t.connStopper.RunWorker(func() { diff --git a/nodehost.go b/nodehost.go index d706d547f..9140b34ef 100644 --- a/nodehost.go +++ b/nodehost.go @@ -493,7 +493,7 @@ func (nh *NodeHost) GetNodeHostRegistry() (INodeHostRegistry, bool) { // - joining a new node to an existing Raft shard, set join to true and leave // the initialMembers map empty. This requires the joining node to have already // been added as a member node of the Raft shard. -// - restarting an crashed or stopped node, set join to false and leave the +// - restarting a crashed or stopped node, set join to false and leave the // initialMembers map to be empty. This applies to both initial member nodes // and those joined later. func (nh *NodeHost) StartReplica(initialMembers map[uint64]Target, @@ -556,7 +556,7 @@ func (nh *NodeHost) StopReplica(shardID uint64, replicaID uint64) error { } // SyncPropose makes a synchronous proposal on the Raft shard specified by -// the input client session object. The specified context parameter must has +// the input client session object. The specified context parameter must have // the timeout value set. // // SyncPropose returns the result returned by IStateMachine or @@ -592,7 +592,7 @@ func (nh *NodeHost) SyncPropose(ctx context.Context, } // SyncRead performs a synchronous linearizable read on the specified Raft -// shard. The specified context parameter must has the timeout value set. The +// shard. The specified context parameter must have the timeout value set. The // query interface{} specifies what to query, it will be passed to the Lookup // method of the IStateMachine or IOnDiskStateMachine after the system // determines that it is safe to perform the local read. It returns the query @@ -646,9 +646,9 @@ type Membership struct { Removed map[uint64]struct{} } -// SyncGetShardMembership is a rsynchronous method that queries the membership +// SyncGetShardMembership is a synchronous method that queries the membership // information from the specified Raft shard. The specified context parameter -// must has the timeout value set. +// must have the timeout value set. func (nh *NodeHost) SyncGetShardMembership(ctx context.Context, shardID uint64) (*Membership, error) { v, err := nh.linearizableRead(ctx, shardID, @@ -711,7 +711,7 @@ func (nh *NodeHost) GetNoOPSession(shardID uint64) *client.Session { // SyncGetSession starts a synchronous proposal to create, register and return // a new client session object for the specified Raft shard. The specified -// context parameter must has the timeout value set. +// context parameter must have the timeout value set. // // A client session object is used to ensure that a retried proposal, e.g. // proposal retried after timeout, will not be applied more than once into the @@ -747,7 +747,7 @@ func (nh *NodeHost) SyncGetSession(ctx context.Context, // SyncCloseSession closes the specified client session by unregistering it // from the system in a synchronous manner. The specified context parameter -// must has the timeout value set. +// must have the timeout value set. // // Closed client session should not be used in future proposals. func (nh *NodeHost) SyncCloseSession(ctx context.Context, @@ -919,7 +919,7 @@ func (nh *NodeHost) StaleRead(shardID uint64, // SyncRequestSnapshot is the synchronous variant of the RequestSnapshot // method. See RequestSnapshot for more details. // -// The input context object must has deadline set. +// The input context object must have deadline set. // // SyncRequestSnapshot returns the index of the created snapshot or the error // encountered. diff --git a/request_test.go b/request_test.go index 5ee80617f..5b21ceefe 100644 --- a/request_test.go +++ b/request_test.go @@ -340,10 +340,11 @@ func TestPendingSnapshotCanBeRequested(t *testing.T) { ps := newPendingSnapshot(snapshotC) ss, err := ps.request(rsm.UserRequested, "", false, 0, 0, 10) if err != nil { - t.Errorf("failed to request snapshot") + t.Fatalf("failed to request snapshot") } if ss == nil { t.Fatalf("nil ss returned") + return } if ps.pending == nil { t.Errorf("pending not set") @@ -386,10 +387,11 @@ func TestMultiplePendingSnapshotIsNotAllowed(t *testing.T) { ps := newPendingSnapshot(snapshotC) ss, err := ps.request(rsm.UserRequested, "", false, 0, 0, 100) if err != nil { - t.Errorf("failed to request snapshot") + t.Fatalf("failed to request snapshot") } if ss == nil { t.Fatalf("nil ss returned") + return } ss, err = ps.request(rsm.UserRequested, "", false, 0, 0, 100) if err != ErrSystemBusy { @@ -408,7 +410,7 @@ func TestPendingSnapshotCanBeGCed(t *testing.T) { t.Fatalf("failed to request snapshot") } if ss == nil { - t.Errorf("nil ss returned") + t.Fatalf("nil ss returned") } if ps.pending == nil { t.Errorf("pending not set") @@ -440,10 +442,11 @@ func TestPendingSnapshotCanBeApplied(t *testing.T) { ps := newPendingSnapshot(snapshotC) ss, err := ps.request(rsm.UserRequested, "", false, 0, 0, 100) if err != nil { - t.Errorf("failed to request snapshot") + t.Fatalf("failed to request snapshot") } if ss == nil { t.Fatalf("nil ss returned") + return } ps.apply(ss.key, false, false, 123) select { @@ -464,10 +467,11 @@ func TestPendingSnapshotCanBeIgnored(t *testing.T) { ps := newPendingSnapshot(snapshotC) ss, err := ps.request(rsm.UserRequested, "", false, 0, 0, 100) if err != nil { - t.Errorf("failed to request snapshot") + t.Fatalf("failed to request snapshot") } if ss == nil { t.Fatalf("nil ss returned") + return } ps.apply(ss.key, true, false, 123) select { @@ -488,10 +492,12 @@ func TestPendingSnapshotIsIdentifiedByTheKey(t *testing.T) { ps := newPendingSnapshot(snapshotC) ss, err := ps.request(rsm.UserRequested, "", false, 0, 0, 100) if err != nil { - t.Errorf("failed to request snapshot") + t.Fatalf("failed to request snapshot") + return } if ss == nil { - t.Errorf("nil ss returned") + t.Fatalf("nil ss returned") + return } if ps.pending == nil { t.Fatalf("pending not set") diff --git a/snapshotstate_test.go b/snapshotstate_test.go index 8f2f8a524..3ccb334f0 100644 --- a/snapshotstate_test.go +++ b/snapshotstate_test.go @@ -51,11 +51,10 @@ func TestSnapshotTaskCanNotBeSetTwice(t *testing.T) { func TestCanGetSnapshotTask(t *testing.T) { defer leaktest.AfterTest(t)() sr := snapshotTask{} - rec, ok := sr.getTask() - if ok { + if _, ok := sr.getTask(); ok { t.Errorf("unexpected record") } - rec = rsm.Task{} + rec := rsm.Task{} sr.setTask(rec) r, ok := sr.getTask() if !ok { diff --git a/statemachine/rsm.go b/statemachine/rsm.go index 9a076dd96..746b4af99 100644 --- a/statemachine/rsm.go +++ b/statemachine/rsm.go @@ -81,13 +81,13 @@ type SnapshotFile struct { // IStateMachine instance recovers from the saved snapshot. type ISnapshotFileCollection interface { // AddFile adds an external file to the snapshot being currently generated. - // The file must has been finalized meaning its content will not change in + // The file must have been finalized meaning its content will not change in // the future. It is your application's responsibility to make sure that the - // file being added can be accessible from the current process and it is + // file being added is accessible from the current process and it is // possible to create a hard link to it from the NodeHostDir directory // specified in NodeHost's NodeHostConfig. The file to be added is identified // by the specified fileID. The metadata byte slice is the metadata of the - // file being added, it can be the checksum of the file, file type, file name, + // file being added. It can be the checksum of the file, file type, file name, // other file hierarchy information, or a serialized combination of such // metadata. AddFile(fileID uint64, path string, metadata []byte)