diff --git a/http/service.go b/http/service.go index 9cec326d..59a9af62 100644 --- a/http/service.go +++ b/http/service.go @@ -5,7 +5,6 @@ package http import ( "encoding/json" "io/ioutil" - "log" "net" "net/http" "strings" @@ -24,7 +23,7 @@ type Store interface { // Query executes a slice of queries, each of which returns rows. // If tx is true, then the query will take place while a read // transaction is held on the database. - Query(queries []string, tx bool) ([]*sql.Rows, error) + Query(queries []string, tx, leader bool) ([]*sql.Rows, error) // Join joins the node, reachable at addr, to the cluster. Join(addr string) error @@ -83,7 +82,7 @@ func (s *Service) Start() error { go func() { err := server.Serve(s.ln) if err != nil { - log.Fatalf("HTTP serve: %s", err) + return } }() @@ -254,6 +253,12 @@ func (s *Service) handleQuery(w http.ResponseWriter, r *http.Request) { return } + isLeader, err := isLeader(r) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Get the query statement(s), and do tx if necessary. queries := []string{} @@ -277,7 +282,7 @@ func (s *Service) handleQuery(w http.ResponseWriter, r *http.Request) { } } - results, err := s.store.Query(queries, isTx) + results, err := s.store.Query(queries, isTx, isLeader) if err != nil { resp.Error = err.Error() diff --git a/http/service_test.go b/http/service_test.go index c297fbc4..512d76a9 100644 --- a/http/service_test.go +++ b/http/service_test.go @@ -27,7 +27,7 @@ func Test_404Routes(t *testing.T) { client := &http.Client{} - resp, err := client.Get(host+"/db/xxx") + resp, err := client.Get(host + "/db/xxx") if err != nil { t.Fatalf("failed to make request") } @@ -55,7 +55,7 @@ func Test_405Routes(t *testing.T) { client := &http.Client{} - resp, err := client.Get(host+"/db/execute") + resp, err := client.Get(host + "/db/execute") if err != nil { t.Fatalf("failed to make request") } @@ -87,7 +87,7 @@ func (m *MockStore) Execute(queries []string, tx bool) ([]*sql.Result, error) { return nil, nil } -func (m *MockStore) Query(queries []string, tx bool) ([]*sql.Rows, error) { +func (m *MockStore) Query(queries []string, tx, leader bool) ([]*sql.Rows, error) { return nil, nil } diff --git a/store/store.go b/store/store.go index b2cce61f..b243d5d8 100644 --- a/store/store.go +++ b/store/store.go @@ -175,11 +175,15 @@ func (s *Store) Backup() ([]byte, error) { } // Query executes queries that return rows, and do not modify the database. -func (s *Store) Query(queries []string, tx bool) ([]*sql.Rows, error) { +func (s *Store) Query(queries []string, tx, leader bool) ([]*sql.Rows, error) { // Allow concurrent queries. s.mu.RLock() defer s.mu.RUnlock() + if leader && s.raft.State() != raft.Leader { + return nil, fmt.Errorf("not leader") + } + r, err := s.db.Query(queries, tx, true) return r, err }