Skip to content

Commit

Permalink
Merge pull request #856 from go-kivik/serverDBsStats
Browse files Browse the repository at this point in the history
Add AllDBsStats and DBsStats to server/fsdb
  • Loading branch information
flimzy authored Dec 27, 2023
2 parents e7404ce + 33d279a commit 91634ec
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 4 deletions.
8 changes: 6 additions & 2 deletions kivik.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,14 @@ func (c *Client) fallbackDBsStats(ctx context.Context, dbnames []string) ([]*DBS
dbstats := make([]*DBStats, len(dbnames))
for i, dbname := range dbnames {
stat, err := c.DB(dbname).Stats(ctx)
if err != nil {
switch {
case HTTPStatus(err) == http.StatusNotFound:
continue
case err != nil:
return nil, err
default:
dbstats[i] = stat
}
dbstats[i] = stat
}
return dbstats, nil
}
Expand Down
8 changes: 8 additions & 0 deletions x/fsdb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"context"
"errors"
"net/http"
"os"
"path/filepath"

"github.com/go-kivik/kivik/v4/driver"
Expand Down Expand Up @@ -59,6 +60,13 @@ func (d *db) Delete(context.Context, string, driver.Options) (newRev string, err
}

func (d *db) Stats(context.Context) (*driver.DBStats, error) {
_, err := d.fs.Stat(d.path())
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, &statusError{status: http.StatusNotFound, error: err}
}
return nil, err
}
return &driver.DBStats{
Name: d.dbName,
CompactRunning: false,
Expand Down
74 changes: 74 additions & 0 deletions x/fsdb/db_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

package fs

import (
"context"
"net/http"
"testing"

"github.com/google/go-cmp/cmp"
"gitlab.com/flimzy/testy"

"github.com/go-kivik/kivik/v4"
"github.com/go-kivik/kivik/v4/driver"
"github.com/go-kivik/kivik/v4/x/fsdb/filesystem"
)

func Test_db_Stats(t *testing.T) {
tests := []struct {
name string
path, dbname string
wantStatus int
wantErr string
want *driver.DBStats
}{
{
name: "success",
path: "testdata",
dbname: "db_att",
want: &driver.DBStats{
Name: "db_att",
},
},
{
name: "not found",
path: "testdata",
dbname: "notfound",
wantStatus: http.StatusNotFound,
wantErr: `[Nn]o such file or directory`,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &client{root: tt.path, fs: filesystem.Default()}
db, err := c.newDB(tt.dbname)
if err != nil {
t.Fatal(err)
}

stats, err := db.Stats(context.Background())
if !testy.ErrorMatchesRE(tt.wantErr, err) {
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
return
}
if status := kivik.HTTPStatus(err); status != tt.wantStatus {
t.Errorf("status = %v, wantStatus %v", status, tt.wantStatus)
}
if d := cmp.Diff(tt.want, stats); d != "" {
t.Errorf("Unexpected stats:\n%s\n", d)
}
})
}
}
4 changes: 2 additions & 2 deletions x/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ func (s *Server) routes(mux *chi.Mux) {
)
admin.Get("/_active_tasks", httpe.ToHandler(s.activeTasks()).ServeHTTP)
admin.Get("/_all_dbs", httpe.ToHandler(s.allDBs()).ServeHTTP)
auth.Get("/_dbs_info", httpe.ToHandler(s.notImplemented()).ServeHTTP)
auth.Post("/_dbs_info", httpe.ToHandler(s.notImplemented()).ServeHTTP)
auth.Get("/_dbs_info", httpe.ToHandler(s.allDBsStats()).ServeHTTP)
auth.Post("/_dbs_info", httpe.ToHandler(s.dbsStats()).ServeHTTP)
auth.Get("/_cluster_setup", httpe.ToHandler(s.notImplemented()).ServeHTTP)
auth.Post("/_cluster_setup", httpe.ToHandler(s.notImplemented()).ServeHTTP)
auth.Post("/_db_updates", httpe.ToHandler(s.notImplemented()).ServeHTTP)
Expand Down
48 changes: 48 additions & 0 deletions x/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,54 @@ func TestServer(t *testing.T) {
"foo": "bar",
},
},
{
name: "all dbs stats",
method: http.MethodGet,
path: "/_dbs_info",
authUser: userAdmin,
wantStatus: http.StatusOK,
wantJSON: []map[string]interface{}{
{
"compact_running": false,
"data_size": 0,
"db_name": "db1",
"disk_size": 0,
"doc_count": 0,
"doc_del_count": 0,
"update_seq": "",
},
{
"compact_running": false,
"data_size": 0,
"db_name": "db2",
"disk_size": 0,
"doc_count": 0,
"doc_del_count": 0,
"update_seq": "",
},
},
},
{
name: "dbs stats",
method: http.MethodPost,
path: "/_dbs_info",
authUser: userAdmin,
headers: map[string]string{"Content-Type": "application/json"},
body: strings.NewReader(`{"keys":["db1","notfound"]}`),
wantStatus: http.StatusOK,
wantJSON: []map[string]interface{}{
{
"compact_running": false,
"data_size": 0,
"db_name": "db1",
"disk_size": 0,
"doc_count": 0,
"doc_del_count": 0,
"update_seq": "",
},
nil,
},
},
}

for _, tt := range tests {
Expand Down
48 changes: 48 additions & 0 deletions x/server/stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

//go:build !js
// +build !js

package server

import (
"net/http"

"gitlab.com/flimzy/httpe"
)

func (s *Server) allDBsStats() httpe.HandlerWithError {
return httpe.HandlerWithErrorFunc(func(w http.ResponseWriter, r *http.Request) error {
stats, err := s.client.AllDBsStats(r.Context(), options(r))
if err != nil {
return err
}
return serveJSON(w, http.StatusOK, stats)
})
}

func (s *Server) dbsStats() httpe.HandlerWithError {
return httpe.HandlerWithErrorFunc(func(w http.ResponseWriter, r *http.Request) error {
var req struct {
Keys []string `json:"keys"`
}
if err := s.bind(r, &req); err != nil {
return err
}
stats, err := s.client.DBsStats(r.Context(), req.Keys)
if err != nil {
return err
}
return serveJSON(w, http.StatusOK, stats)
})
}

0 comments on commit 91634ec

Please sign in to comment.