diff --git a/CHANGELOG.md b/CHANGELOG.md index 32cb5d76..5860de13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,7 @@ When officially released 8.0 will support (mostly) seamless upgrades from the 7. - [PR #1420](https://github.com/rqlite/rqlite/pull/1420), [PR #1431](https://github.com/rqlite/rqlite/pull/1431): Nodes join a cluster using the Raft address, not the HTTP API. - [PR #1426](https://github.com/rqlite/rqlite/pull/1426): 'go mod' updates, including moving to Raft 1.6. - [PR #1430](https://github.com/rqlite/rqlite/pull/1430): Check that any supplied Join addresses are not HTTP servers. -- [PR #1437](https://github.com/rqlite/rqlite/pull/1437), [PR #1438](https://github.com/rqlite/rqlite/pull/1438): Actually timeout if needed during `nodes/` access. Fixes [issue #1435](https://github.com/rqlite/rqlite/issues/1435). Thanks @dwco-z +- [PR #1437](https://github.com/rqlite/rqlite/pull/1437), [PR #1438](https://github.com/rqlite/rqlite/pull/1438), [PR #1439](https://github.com/rqlite/rqlite/pull/1439): Actually timeout if needed during `nodes/` access. Fixes [issue #1435](https://github.com/rqlite/rqlite/issues/1435). Thanks @dwco-z - [PR #1440](https://github.com/rqlite/rqlite/pull/1440): Add a Compacting WAL rewriter. Thanks @benbjohnson. ## 7.21.4 (July 8th 2023) diff --git a/http/nodes.go b/http/nodes.go index 0be62e5a..6ed8dd9a 100644 --- a/http/nodes.go +++ b/http/nodes.go @@ -24,6 +24,8 @@ type Node struct { Leader bool `json:"leader"` Time float64 `json:"time,omitempty"` Error string `json:"error,omitempty"` + + mu sync.Mutex } // NewNodeFromServer creates a Node from a Server. @@ -50,7 +52,7 @@ func (n *Node) Test(ga GetAddresser, leaderAddr string, timeout time.Duration) { defer close(done) apiAddr, err := ga.GetNodeAPIAddr(n.Addr, timeout) if err != nil { - n.Error = err.Error() + n.SetError(err.Error()) return } n.APIAddr = apiAddr @@ -60,11 +62,30 @@ func (n *Node) Test(ga GetAddresser, leaderAddr string, timeout time.Duration) { select { case <-timer.C: - n.Error = "timeout" + n.SetError("timeout waiting for node to respond") case <-done: } } +// SetError sets the Error field of the Node in a synchronized manner. +func (n *Node) SetError(err string) { + n.mu.Lock() + defer n.mu.Unlock() + n.Error = err +} + +// MarshalJSON implements the json.Marshaler interface. +func (n *Node) MarshalJSON() ([]byte, error) { + n.mu.Lock() + defer n.mu.Unlock() + type Alias Node + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(n), + }) +} + type Nodes []*Node func (n Nodes) Len() int { return len(n) } diff --git a/http/nodes_test.go b/http/nodes_test.go index b5438664..bf6958af 100644 --- a/http/nodes_test.go +++ b/http/nodes_test.go @@ -108,7 +108,7 @@ func Test_NodeTestDouble_Timeout(t *testing.T) { nodes := Nodes{node1, node2} nodes.Test(mockGA, "leader-raft-addr", 1*time.Second) - if !node1.Reachable || !node1.Leader || node2.Reachable || node2.Leader || node2.Error != "timeout" { + if !node1.Reachable || !node1.Leader || node2.Reachable || node2.Leader || node2.Error != "timeout waiting for node to respond" { t.Fatalf("Test method did not correctly update node status %s", asJSON(nodes)) }