From 698403a02fc9a225c3d51615962b5d3d1264bca9 Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Thu, 15 Feb 2024 23:52:11 -0500 Subject: [PATCH 01/13] Add commit flag to Query Params --- http/query_params.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/http/query_params.go b/http/query_params.go index 5fbd938a..7222fec0 100644 --- a/http/query_params.go +++ b/http/query_params.go @@ -181,6 +181,11 @@ func (qp QueryParams) FreshnessStrict() bool { return qp.HasKey("freshness_strict") } +// Commit returns whether the commit flag is set. +func (qp QueryParams) Commit() bool { + return qp.HasKey("commit") +} + // Timeout returns the requested timeout duration. func (qp QueryParams) Timeout(def time.Duration) time.Duration { t, ok := qp["timeout"] From e3b72079b422ae5fcaa42800fe9acd0176caec42 Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 00:00:21 -0500 Subject: [PATCH 02/13] Add WaitCommitIndex --- store/store.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/store/store.go b/store/store.go index e1d26929..f4ddc10f 100644 --- a/store/store.go +++ b/store/store.go @@ -98,6 +98,7 @@ const ( sqliteFile = "db.sqlite" leaderWaitDelay = 100 * time.Millisecond appliedWaitDelay = 100 * time.Millisecond + commitEquivalenceDelay = 50 * time.Millisecond appliedIndexUpdateInterval = 5 * time.Second connectionPoolCount = 5 connectionTimeout = 10 * time.Second @@ -712,6 +713,31 @@ func (s *Store) WaitForAppliedIndex(idx uint64, timeout time.Duration) error { } } +// WaitCommitIndex blocks until the local Raft commit index is equal to +// or greater than the Leader Commit Index at the time this function +// is called, or the timeout expires. +func (s *Store) WaitCommitIndex(timeout time.Duration) error { + tck := time.NewTicker(commitEquivalenceDelay) + defer tck.Stop() + tmr := time.NewTimer(timeout) + defer tmr.Stop() + + lci, err := s.LeaderCommitIndex() + if err != nil { + return err + } + for { + select { + case <-tck.C: + if lci <= s.raft.CommitIndex() { + return nil + } + case <-tmr.C: + return fmt.Errorf("timeout expired") + } + } +} + // DBAppliedIndex returns the index of the last Raft log that changed the // underlying database. If the index is unknown then 0 is returned. func (s *Store) DBAppliedIndex() uint64 { From 1cb7d052852e9fa3f392102ce05229eeef154cdb Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 00:02:29 -0500 Subject: [PATCH 03/13] Unit test WaitCommitIndex --- store/store_multi_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/store/store_multi_test.go b/store/store_multi_test.go index 1b5f03af..76336cc1 100644 --- a/store/store_multi_test.go +++ b/store/store_multi_test.go @@ -76,6 +76,10 @@ func Test_MultiNodeSimple(t *testing.T) { t.Fatalf("wrong leader commit index, got: %d, exp: %d", got, exp) } + if err := s0.WaitCommitIndex(time.Second); err != nil { + t.Fatalf("failed to wait for commit index: %s", err.Error()) + } + // Now, do a NONE consistency query on each node, to actually confirm the data // has been replicated. testFn1 := func(t *testing.T, s *Store) { @@ -108,6 +112,10 @@ func Test_MultiNodeSimple(t *testing.T) { t.Fatalf("wrong leader commit index, got: %d, exp: %d", got, exp) } + if err := s1.WaitCommitIndex(time.Second); err != nil { + t.Fatalf("failed to wait for commit index: %s", err.Error()) + } + // Write another row using Request rr := executeQueryRequestFromString("INSERT INTO foo(id, name) VALUES(2, 'fiona')", proto.QueryRequest_QUERY_REQUEST_LEVEL_STRONG, false, false) _, err = s0.Request(rr) From c41799fc8d30ae0070ca74995e2bcc6a43a38a26 Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 00:05:35 -0500 Subject: [PATCH 04/13] Better wait for function --- store/store.go | 13 ++++--------- store/store_multi_test.go | 10 ++++++++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/store/store.go b/store/store.go index f4ddc10f..4d1bdb2b 100644 --- a/store/store.go +++ b/store/store.go @@ -713,23 +713,18 @@ func (s *Store) WaitForAppliedIndex(idx uint64, timeout time.Duration) error { } } -// WaitCommitIndex blocks until the local Raft commit index is equal to -// or greater than the Leader Commit Index at the time this function -// is called, or the timeout expires. -func (s *Store) WaitCommitIndex(timeout time.Duration) error { +// WaitForCommitIndex blocks until the local Raft commit index is equal to +// or greater the given index, or the timeout expires. +func (s *Store) WaitForCommitIndex(idx uint64, timeout time.Duration) error { tck := time.NewTicker(commitEquivalenceDelay) defer tck.Stop() tmr := time.NewTimer(timeout) defer tmr.Stop() - lci, err := s.LeaderCommitIndex() - if err != nil { - return err - } for { select { case <-tck.C: - if lci <= s.raft.CommitIndex() { + if s.raft.CommitIndex() >= idx { return nil } case <-tmr.C: diff --git a/store/store_multi_test.go b/store/store_multi_test.go index 76336cc1..cf234cc6 100644 --- a/store/store_multi_test.go +++ b/store/store_multi_test.go @@ -76,9 +76,12 @@ func Test_MultiNodeSimple(t *testing.T) { t.Fatalf("wrong leader commit index, got: %d, exp: %d", got, exp) } - if err := s0.WaitCommitIndex(time.Second); err != nil { + if err := s0.WaitForCommitIndex(4, time.Second); err != nil { t.Fatalf("failed to wait for commit index: %s", err.Error()) } + if err := s0.WaitForCommitIndex(5, 500*time.Millisecond); err == nil { + t.Fatalf("unexpectedly waited successfully for commit index") + } // Now, do a NONE consistency query on each node, to actually confirm the data // has been replicated. @@ -112,9 +115,12 @@ func Test_MultiNodeSimple(t *testing.T) { t.Fatalf("wrong leader commit index, got: %d, exp: %d", got, exp) } - if err := s1.WaitCommitIndex(time.Second); err != nil { + if err := s1.WaitForCommitIndex(4, time.Second); err != nil { t.Fatalf("failed to wait for commit index: %s", err.Error()) } + if err := s1.WaitForCommitIndex(5, 500*time.Millisecond); err == nil { + t.Fatalf("unexpectedly waited successfully for commit index") + } // Write another row using Request rr := executeQueryRequestFromString("INSERT INTO foo(id, name) VALUES(2, 'fiona')", proto.QueryRequest_QUERY_REQUEST_LEVEL_STRONG, false, false) From 0c2ac1ca3cf027004d0db471a3150c0eda3f8b26 Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 00:06:59 -0500 Subject: [PATCH 05/13] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3eabcc6..be2834ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - [PR #1685](https://github.com/rqlite/rqlite/pull/1685): Rename a Proto (but not its fields). - [PR #1686](https://github.com/rqlite/rqlite/pull/1686): Node returns _Meta_, not just Address. - [PR #1688](https://github.com/rqlite/rqlite/pull/1688): Expose Leader Commit Index, as read from latest AppendEntries RPC. +- [PR #1689](https://github.com/rqlite/rqlite/pull/1689): `/readyz` can wait for Commit Index. ## 8.20.1 (February 13th 2024) ### Implementation changes and bug fixes From 45f0ca741470a12f45cc97f4b853940a835c6704 Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 00:19:53 -0500 Subject: [PATCH 06/13] Hook in commit flag to /readyz --- http/service.go | 15 ++++++++++++++- http/service_test.go | 4 ++++ store/store.go | 11 +++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/http/service.go b/http/service.go index 3be6d0ff..fcf81f30 100644 --- a/http/service.go +++ b/http/service.go @@ -76,6 +76,10 @@ type Store interface { // Ready returns whether the Store is ready to service requests. Ready() bool + // Committed blocks until the local commit index is greater than or + // equal to the Leader index, as checked when the function is called. + Committed(timeout time.Duration) (uint64, error) + // Stats returns stats on the Store. Stats() (map[string]interface{}, error) @@ -1003,8 +1007,17 @@ func (s *Service) handleReadyz(w http.ResponseWriter, r *http.Request, qp QueryP return } + okMsg := "[+]node ok\n[+]leader ok\n[+]store ok" + if qp.Commit() { + if _, err := s.store.Committed(qp.Timeout(defaultTimeout)); err != nil { + w.WriteHeader(http.StatusServiceUnavailable) + w.Write([]byte(fmt.Sprintf("[+]node ok\n[+]leader ok\n[+]store ok\n[+]commit %s", err.Error()))) + return + } + okMsg += "\n[+]commit ok" + } w.WriteHeader(http.StatusOK) - w.Write([]byte("[+]node ok\n[+]leader ok\n[+]store ok")) + w.Write([]byte(okMsg)) } func (s *Service) handleExecute(w http.ResponseWriter, r *http.Request, qp QueryParams) { diff --git a/http/service_test.go b/http/service_test.go index bd5239f5..7080a59f 100644 --- a/http/service_test.go +++ b/http/service_test.go @@ -1419,6 +1419,10 @@ func (m *MockStore) Ready() bool { return !m.notReady } +func (m *MockStore) Committed(timeout time.Duration) (uint64, error) { + return 0, nil +} + func (m *MockStore) Stats() (map[string]interface{}, error) { return nil, nil } diff --git a/store/store.go b/store/store.go index 4d1bdb2b..13aab8b3 100644 --- a/store/store.go +++ b/store/store.go @@ -633,6 +633,17 @@ func (s *Store) Ready() bool { }() } +// Committed blocks until the local commit index is greater than or +// equal to the Leader index, as checked when the function is called. +// It returns the committed index. +func (s *Store) Committed(timeout time.Duration) (uint64, error) { + lci, err := s.LeaderCommitIndex() + if err != nil { + return lci, err + } + return lci, s.WaitForCommitIndex(lci, timeout) +} + // Close closes the store. If wait is true, waits for a graceful shutdown. func (s *Store) Close(wait bool) (retErr error) { defer func() { From c08e02971798d86e6546dec26b0c0387fc399e5f Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 08:24:01 -0500 Subject: [PATCH 07/13] Unit test readyz?commit at HTTP level --- http/service_test.go | 48 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/http/service_test.go b/http/service_test.go index 7080a59f..1f32d6af 100644 --- a/http/service_test.go +++ b/http/service_test.go @@ -1039,10 +1039,10 @@ func Test_Readyz(t *testing.T) { host := fmt.Sprintf("http://%s", s.Addr().String()) resp, err := client.Get(host + "/readyz") if err != nil { - t.Fatalf("failed to make nodes request") + t.Fatalf("failed to make readyz request") } if resp.StatusCode != http.StatusOK { - t.Fatalf("failed to get expected StatusOK for nodes, got %d", resp.StatusCode) + t.Fatalf("failed to get expected StatusOK for node, got %d", resp.StatusCode) } m.notReady = true @@ -1051,9 +1051,31 @@ func Test_Readyz(t *testing.T) { t.Fatalf("failed to make nodes request") } if resp.StatusCode != http.StatusServiceUnavailable { - t.Fatalf("failed to get expected StatusServiceUnavailable for nodes, got %d", resp.StatusCode) + t.Fatalf("failed to get expected StatusServiceUnavailable, got %d", resp.StatusCode) } + m.notReady = false + m.committedFn = func(timeout time.Duration) (uint64, error) { + return 0, fmt.Errorf("timeout") + } + resp, err = client.Get(host + "/readyz?commit") + if err != nil { + t.Fatalf("failed to make readyz request with commit set") + } + if resp.StatusCode != http.StatusServiceUnavailable { + t.Fatalf("failed to get expected StatusServiceUnavailable, got %d", resp.StatusCode) + } + m.notReady = false + m.committedFn = func(timeout time.Duration) (uint64, error) { + return 0, nil + } + resp, err = client.Get(host + "/readyz?commit") + if err != nil { + t.Fatalf("failed to make readyz request with commit set") + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("failed to get expected StatusOK, got %d", resp.StatusCode) + } } func Test_ForwardingRedirectQuery(t *testing.T) { @@ -1368,14 +1390,15 @@ func Test_DBTimeoutQueryParam(t *testing.T) { } type MockStore struct { - executeFn func(er *command.ExecuteRequest) ([]*command.ExecuteResult, error) - queryFn func(qr *command.QueryRequest) ([]*command.QueryRows, error) - requestFn func(eqr *command.ExecuteQueryRequest) ([]*command.ExecuteQueryResponse, error) - backupFn func(br *command.BackupRequest, dst io.Writer) error - loadFn func(lr *command.LoadRequest) error - readFromFn func(r io.Reader) (int64, error) - leaderAddr string - notReady bool // Default value is true, easier to test. + executeFn func(er *command.ExecuteRequest) ([]*command.ExecuteResult, error) + queryFn func(qr *command.QueryRequest) ([]*command.QueryRows, error) + requestFn func(eqr *command.ExecuteQueryRequest) ([]*command.ExecuteQueryResponse, error) + backupFn func(br *command.BackupRequest, dst io.Writer) error + loadFn func(lr *command.LoadRequest) error + readFromFn func(r io.Reader) (int64, error) + committedFn func(timeout time.Duration) (uint64, error) + leaderAddr string + notReady bool // Default value is true, easier to test. } func (m *MockStore) Execute(er *command.ExecuteRequest) ([]*command.ExecuteResult, error) { @@ -1420,6 +1443,9 @@ func (m *MockStore) Ready() bool { } func (m *MockStore) Committed(timeout time.Duration) (uint64, error) { + if m.committedFn != nil { + return m.committedFn(timeout) + } return 0, nil } From 4391ef0f9c62be78733205f7db5f90a94c15713a Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 08:27:44 -0500 Subject: [PATCH 08/13] E2E supports 'commit' flag --- system_test/e2e/helpers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/system_test/e2e/helpers.py b/system_test/e2e/helpers.py index 119af4f6..d1abdf94 100644 --- a/system_test/e2e/helpers.py +++ b/system_test/e2e/helpers.py @@ -281,8 +281,8 @@ class Node(object): raise_for_status(r) return r.json() - def ready(self, noleader=False): - r = requests.get(self._ready_url(noleader)) + def ready(self, noleader=False, commit=False): + r = requests.get(self._ready_url(noleader, commit)) return r.status_code == 200 def expvar(self): @@ -602,10 +602,14 @@ class Node(object): return 'http://' + self.APIAddr() + '/status' def _nodes_url(self): return 'http://' + self.APIAddr() + '/nodes?nonvoters' # Getting all nodes back makes testing easier - def _ready_url(self, noleader=False): + def _ready_url(self, noleader=False, commit=False): + vals = [] nl = "" if noleader: - nl = "?noleader" + vals = vals + ["noleader"] + if commit: + vals = vals + ["commit"] + nl = '?' + '&'.join(vals) return 'http://' + self.APIAddr() + '/readyz' + nl def _expvar_url(self): return 'http://' + self.APIAddr() + '/debug/vars' From b696f0dea23c24bc047796c493e486006c803aab Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 08:37:21 -0500 Subject: [PATCH 09/13] E2E testing of readyz commit --- system_test/e2e/helpers.py | 4 ++-- system_test/e2e/multi_node.py | 1 + system_test/e2e/single_node.py | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/system_test/e2e/helpers.py b/system_test/e2e/helpers.py index d1abdf94..c77fb7dd 100644 --- a/system_test/e2e/helpers.py +++ b/system_test/e2e/helpers.py @@ -342,10 +342,10 @@ class Node(object): raise Exception('leader is available but node %s at %s reports empty leader addr' % (self.node_id, self.APIAddr())) return lr - def wait_for_ready(self, timeout=TIMEOUT): + def wait_for_ready(self, commit=False, timeout=TIMEOUT): deadline = time.time() + timeout while time.time() < deadline: - if self.ready(): + if self.ready(commit): return time.sleep(0.1) raise Exception('rqlite node failed to become ready within %d seconds' % timeout) diff --git a/system_test/e2e/multi_node.py b/system_test/e2e/multi_node.py index 46e4299f..907ec147 100644 --- a/system_test/e2e/multi_node.py +++ b/system_test/e2e/multi_node.py @@ -649,6 +649,7 @@ class TestShutdown(unittest.TestCase): # Check that we have a working single-node cluster with a leader by doing # a write. n1.wait_for_ready() + n1.wait_for_ready(commit=True) j = n1.execute('CREATE TABLE foo (id INTEGER NOT NULL PRIMARY KEY, name TEXT)') self.assertEqual(j, d_("{'results': [{}]}")) diff --git a/system_test/e2e/single_node.py b/system_test/e2e/single_node.py index 58c71cac..8cf18e15 100644 --- a/system_test/e2e/single_node.py +++ b/system_test/e2e/single_node.py @@ -237,6 +237,7 @@ class TestSingleNodeReadyz(unittest.TestCase): self.assertEqual(False, n0.ready()) self.assertEqual(True, n0.ready(noleader=True)) self.assertEqual(False, n0.ready(noleader=False)) + self.assertEqual(False, n0.ready(commit=True)) deprovision_node(n0) class TestEndToEndSnapshotRestoreSingle(unittest.TestCase): From 53e0666c9eb3761dc8caad65eab5b963477366dc Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 08:47:42 -0500 Subject: [PATCH 10/13] More HTTP-level unit testing of readyz?commit --- http/service_test.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/http/service_test.go b/http/service_test.go index 1f32d6af..2ac7fb5d 100644 --- a/http/service_test.go +++ b/http/service_test.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "strings" + "sync/atomic" "testing" "time" @@ -1046,16 +1047,22 @@ func Test_Readyz(t *testing.T) { } m.notReady = true + m.committedFn = func(timeout time.Duration) (uint64, error) { + t.Fatal("committedFn should not have been called") + return 0, nil + } resp, err = client.Get(host + "/readyz") if err != nil { - t.Fatalf("failed to make nodes request") + t.Fatalf("failed to make readyz request") } if resp.StatusCode != http.StatusServiceUnavailable { t.Fatalf("failed to get expected StatusServiceUnavailable, got %d", resp.StatusCode) } + cnt := &atomic.Uint32{} m.notReady = false m.committedFn = func(timeout time.Duration) (uint64, error) { + cnt.Store(1) return 0, fmt.Errorf("timeout") } resp, err = client.Get(host + "/readyz?commit") @@ -1065,8 +1072,12 @@ func Test_Readyz(t *testing.T) { if resp.StatusCode != http.StatusServiceUnavailable { t.Fatalf("failed to get expected StatusServiceUnavailable, got %d", resp.StatusCode) } + if cnt.Load() != 1 { + t.Fatalf("failed to call committedFn") + } m.notReady = false m.committedFn = func(timeout time.Duration) (uint64, error) { + cnt.Store(2) return 0, nil } resp, err = client.Get(host + "/readyz?commit") @@ -1076,6 +1087,9 @@ func Test_Readyz(t *testing.T) { if resp.StatusCode != http.StatusOK { t.Fatalf("failed to get expected StatusOK, got %d", resp.StatusCode) } + if cnt.Load() != 2 { + t.Fatalf("failed to call committedFn") + } } func Test_ForwardingRedirectQuery(t *testing.T) { From a48f48e55a54637b5734b349bd4ba1e07ec5e932 Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 08:55:41 -0500 Subject: [PATCH 11/13] Wait until Leader Commit is at least 1 --- store/store.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/store/store.go b/store/store.go index 13aab8b3..6ea74b0c 100644 --- a/store/store.go +++ b/store/store.go @@ -635,12 +635,16 @@ func (s *Store) Ready() bool { // Committed blocks until the local commit index is greater than or // equal to the Leader index, as checked when the function is called. -// It returns the committed index. +// It returns the committed index. If the Leader index is 0, then the +// system waits until the commit index is at least 1. func (s *Store) Committed(timeout time.Duration) (uint64, error) { lci, err := s.LeaderCommitIndex() if err != nil { return lci, err } + if lci == 0 { + lci = 1 + } return lci, s.WaitForCommitIndex(lci, timeout) } From 1641eb5bdedf86ea0d77f394d0ae62c66c1f4078 Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Fri, 16 Feb 2024 09:06:47 -0500 Subject: [PATCH 12/13] Unit test commit query params --- http/query_params_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/http/query_params_test.go b/http/query_params_test.go index 7883ee0d..bfb0bfb7 100644 --- a/http/query_params_test.go +++ b/http/query_params_test.go @@ -33,6 +33,7 @@ func Test_NewQueryParams(t *testing.T) { {"Invalid URL Encoding", "invalid=%ZZ", nil, true}, {"freshness_strict", "&freshness=5s&freshness_strict", QueryParams{"freshness_strict": "", "freshness": "5s"}, false}, {"freshness_strict requires freshness", "freshness_strict", nil, true}, + {"commit with timeout", "commit&timeout=2s", QueryParams{"commit": "", "timeout": "2s"}, false}, } for _, tc := range testCases { From 7e0baf5da8e28e55f4172fed94e7e4b4dc21be26 Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Sat, 17 Feb 2024 21:54:14 -0500 Subject: [PATCH 13/13] Use "sync" instead of "commit" "sync" is a better word for what is happening, "commit" is too Raft-centric. --- http/query_params.go | 6 +++--- http/query_params_test.go | 2 +- http/service.go | 4 ++-- http/service_test.go | 8 ++++---- system_test/e2e/helpers.py | 14 +++++++------- system_test/e2e/multi_node.py | 2 +- system_test/e2e/single_node.py | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/http/query_params.go b/http/query_params.go index 7222fec0..5079b220 100644 --- a/http/query_params.go +++ b/http/query_params.go @@ -181,9 +181,9 @@ func (qp QueryParams) FreshnessStrict() bool { return qp.HasKey("freshness_strict") } -// Commit returns whether the commit flag is set. -func (qp QueryParams) Commit() bool { - return qp.HasKey("commit") +// Sync returns whether the sync flag is set. +func (qp QueryParams) Sync() bool { + return qp.HasKey("sync") } // Timeout returns the requested timeout duration. diff --git a/http/query_params_test.go b/http/query_params_test.go index bfb0bfb7..79e48c16 100644 --- a/http/query_params_test.go +++ b/http/query_params_test.go @@ -33,7 +33,7 @@ func Test_NewQueryParams(t *testing.T) { {"Invalid URL Encoding", "invalid=%ZZ", nil, true}, {"freshness_strict", "&freshness=5s&freshness_strict", QueryParams{"freshness_strict": "", "freshness": "5s"}, false}, {"freshness_strict requires freshness", "freshness_strict", nil, true}, - {"commit with timeout", "commit&timeout=2s", QueryParams{"commit": "", "timeout": "2s"}, false}, + {"sync with timeout", "sync&timeout=2s", QueryParams{"sync": "", "timeout": "2s"}, false}, } for _, tc := range testCases { diff --git a/http/service.go b/http/service.go index fcf81f30..183dc5fc 100644 --- a/http/service.go +++ b/http/service.go @@ -1008,10 +1008,10 @@ func (s *Service) handleReadyz(w http.ResponseWriter, r *http.Request, qp QueryP } okMsg := "[+]node ok\n[+]leader ok\n[+]store ok" - if qp.Commit() { + if qp.Sync() { if _, err := s.store.Committed(qp.Timeout(defaultTimeout)); err != nil { w.WriteHeader(http.StatusServiceUnavailable) - w.Write([]byte(fmt.Sprintf("[+]node ok\n[+]leader ok\n[+]store ok\n[+]commit %s", err.Error()))) + w.Write([]byte(fmt.Sprintf("[+]node ok\n[+]leader ok\n[+]store ok\n[+]sync %s", err.Error()))) return } okMsg += "\n[+]commit ok" diff --git a/http/service_test.go b/http/service_test.go index 2ac7fb5d..302e4a61 100644 --- a/http/service_test.go +++ b/http/service_test.go @@ -1065,9 +1065,9 @@ func Test_Readyz(t *testing.T) { cnt.Store(1) return 0, fmt.Errorf("timeout") } - resp, err = client.Get(host + "/readyz?commit") + resp, err = client.Get(host + "/readyz?sync") if err != nil { - t.Fatalf("failed to make readyz request with commit set") + t.Fatalf("failed to make readyz request with sync set") } if resp.StatusCode != http.StatusServiceUnavailable { t.Fatalf("failed to get expected StatusServiceUnavailable, got %d", resp.StatusCode) @@ -1080,9 +1080,9 @@ func Test_Readyz(t *testing.T) { cnt.Store(2) return 0, nil } - resp, err = client.Get(host + "/readyz?commit") + resp, err = client.Get(host + "/readyz?sync") if err != nil { - t.Fatalf("failed to make readyz request with commit set") + t.Fatalf("failed to make readyz request with sync set") } if resp.StatusCode != http.StatusOK { t.Fatalf("failed to get expected StatusOK, got %d", resp.StatusCode) diff --git a/system_test/e2e/helpers.py b/system_test/e2e/helpers.py index c77fb7dd..d75339c1 100644 --- a/system_test/e2e/helpers.py +++ b/system_test/e2e/helpers.py @@ -281,8 +281,8 @@ class Node(object): raise_for_status(r) return r.json() - def ready(self, noleader=False, commit=False): - r = requests.get(self._ready_url(noleader, commit)) + def ready(self, noleader=False, sync=False): + r = requests.get(self._ready_url(noleader, sync)) return r.status_code == 200 def expvar(self): @@ -342,10 +342,10 @@ class Node(object): raise Exception('leader is available but node %s at %s reports empty leader addr' % (self.node_id, self.APIAddr())) return lr - def wait_for_ready(self, commit=False, timeout=TIMEOUT): + def wait_for_ready(self, sync=False, timeout=TIMEOUT): deadline = time.time() + timeout while time.time() < deadline: - if self.ready(commit): + if self.ready(sync): return time.sleep(0.1) raise Exception('rqlite node failed to become ready within %d seconds' % timeout) @@ -602,13 +602,13 @@ class Node(object): return 'http://' + self.APIAddr() + '/status' def _nodes_url(self): return 'http://' + self.APIAddr() + '/nodes?nonvoters' # Getting all nodes back makes testing easier - def _ready_url(self, noleader=False, commit=False): + def _ready_url(self, noleader=False, sync=False): vals = [] nl = "" if noleader: vals = vals + ["noleader"] - if commit: - vals = vals + ["commit"] + if sync: + vals = vals + ["sync"] nl = '?' + '&'.join(vals) return 'http://' + self.APIAddr() + '/readyz' + nl def _expvar_url(self): diff --git a/system_test/e2e/multi_node.py b/system_test/e2e/multi_node.py index 907ec147..5daa3e0b 100644 --- a/system_test/e2e/multi_node.py +++ b/system_test/e2e/multi_node.py @@ -649,7 +649,7 @@ class TestShutdown(unittest.TestCase): # Check that we have a working single-node cluster with a leader by doing # a write. n1.wait_for_ready() - n1.wait_for_ready(commit=True) + n1.wait_for_ready(sync=True) j = n1.execute('CREATE TABLE foo (id INTEGER NOT NULL PRIMARY KEY, name TEXT)') self.assertEqual(j, d_("{'results': [{}]}")) diff --git a/system_test/e2e/single_node.py b/system_test/e2e/single_node.py index 8cf18e15..b77a1df1 100644 --- a/system_test/e2e/single_node.py +++ b/system_test/e2e/single_node.py @@ -237,7 +237,7 @@ class TestSingleNodeReadyz(unittest.TestCase): self.assertEqual(False, n0.ready()) self.assertEqual(True, n0.ready(noleader=True)) self.assertEqual(False, n0.ready(noleader=False)) - self.assertEqual(False, n0.ready(commit=True)) + self.assertEqual(False, n0.ready(sync=True)) deprovision_node(n0) class TestEndToEndSnapshotRestoreSingle(unittest.TestCase):