Skip to content

Commit

Permalink
enrich output of immuadmin status command
Browse files Browse the repository at this point in the history
Signed-off-by: Stefano Scafiti <[email protected]>
  • Loading branch information
ostafen committed Apr 19, 2024
1 parent 8721fc8 commit e472617
Show file tree
Hide file tree
Showing 15 changed files with 1,832 additions and 1,583 deletions.
55 changes: 55 additions & 0 deletions cmd/helper/size.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package helper

import (
"strconv"
"strings"
)

const (
Byte = 1
KiloByte = 1 << (10 * iota)
MegaByte
GigaByte
TeraByte
PetaByte
ExaByte
)

var unitMap = map[int]string{
Byte: "B",
KiloByte: "KB",
MegaByte: "MB",
GigaByte: "GB",
TeraByte: "TB",
PetaByte: "PB",
ExaByte: "EB",
}

func FormatByteSize(size uint64) string {
u := getUnit(size)

fsize := float64(size) / float64(u)
if rounded := uint64(fsize + 0.05); rounded == 1024 {
u = getUnit(1024 * uint64(u))
fsize = 1
}
return strings.TrimSuffix(strconv.FormatFloat(fsize, 'f', 1, 64), ".0") + " " + unitMap[u]
}

func getUnit(size uint64) int {
switch {
case size >= ExaByte:
return ExaByte
case size >= PetaByte:
return PetaByte
case size >= TeraByte:
return TeraByte
case size >= GigaByte:
return GigaByte
case size >= MegaByte:
return MegaByte
case size >= KiloByte:
return KiloByte
}
return Byte
}
23 changes: 23 additions & 0 deletions cmd/helper/size_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package helper_test

import (
"testing"

"github.com/codenotary/immudb/cmd/helper"
"github.com/stretchr/testify/require"
)

func TestFormatByteSize(t *testing.T) {
require.Equal(t, "25 B", helper.FormatByteSize(25))
require.Equal(t, "10 KB", helper.FormatByteSize(size(10.0, helper.KiloByte)))
require.Equal(t, "5.4 MB", helper.FormatByteSize(size(5.4, helper.MegaByte)))
require.Equal(t, "11.8 GB", helper.FormatByteSize(size(11.8, helper.GigaByte)))
require.Equal(t, "99.3 PB", helper.FormatByteSize(size(99.27, helper.PetaByte)))
require.Equal(t, "1 EB", helper.FormatByteSize(size(1023.95, helper.PetaByte)))
require.Equal(t, "2.3 EB", helper.FormatByteSize(size(2.3, helper.ExaByte)))
}

func size(fraction float64, unit int) uint64 {
s := fraction * float64(unit)
return uint64(s)
}
21 changes: 19 additions & 2 deletions cmd/immuadmin/command/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ package immuadmin

import (
"fmt"
"time"

"github.com/spf13/cobra"

c "github.com/codenotary/immudb/cmd/helper"
"github.com/codenotary/immudb/cmd/immuadmin/command/stats"
"github.com/codenotary/immudb/pkg/api/schema"
)

func (cl *commandline) status(cmd *cobra.Command) {
Expand All @@ -34,10 +36,25 @@ func (cl *commandline) status(cmd *cobra.Command) {
PersistentPostRun: cl.disconnect,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cl.context
if err := cl.immuClient.HealthCheck(ctx); err != nil {

info, err := cl.immuClient.ServerInfo(ctx, &schema.ServerInfoRequest{})
if err != nil {
c.QuitWithUserError(err)
}
fmt.Fprintf(cmd.OutOrStdout(), "OK - server is reachable and responding to queries\n")

startedAt := time.Unix(info.StartedAt, 0)
uptime := time.Now().Truncate(time.Second).Sub(startedAt)

fmt.Fprintf(
cmd.OutOrStdout(),
"Status:\t\tOK - server is reachable and responding to queries\nVersion:\t%s\nUp time:\t%s (up %s)\nDatabases:\t%d (%s)\nTransactions:\t%d\n",
info.Version,
startedAt.Format(time.RFC822),
uptime,
info.NumDatabases,
c.FormatByteSize(uint64(info.DatabasesDiskSize)),
info.NumTransactions,
)
return nil
},
Args: cobra.NoArgs,
Expand Down
4 changes: 4 additions & 0 deletions cmd/immuadmin/command/stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ func TestStats_Status(t *testing.T) {
out, err := ioutil.ReadAll(b)
require.NoError(t, err)
assert.Contains(t, string(out), "OK - server is reachable and responding to queries")
assert.Contains(t, string(out), "Version")
assert.Contains(t, string(out), "Up time")
assert.Contains(t, string(out), "Databases")
assert.Contains(t, string(out), "Transactions")
}

func TestStats_StatsText(t *testing.T) {
Expand Down
21 changes: 21 additions & 0 deletions embedded/store/immustore.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"sync"
Expand Down Expand Up @@ -1364,6 +1365,26 @@ func (s *ImmuStore) MaxValueLen() int {
return s.maxValueLen
}

func (s *ImmuStore) Size() (uint64, error) {
var size uint64

err := filepath.WalkDir(s.path, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}

if !d.IsDir() {
info, err := d.Info()
if err != nil {
return err
}
size += uint64(info.Size())
}
return nil
})
return size, err
}

func (s *ImmuStore) TxCount() uint64 {
s.commitStateRWMutex.RLock()
defer s.commitStateRWMutex.RUnlock()
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/protomodel/authorization.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

642 changes: 320 additions & 322 deletions pkg/api/protomodel/documents.pb.go

Large diffs are not rendered by default.

2,542 changes: 1,294 additions & 1,248 deletions pkg/api/schema/schema.pb.go

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions pkg/api/schema/schema.proto
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,18 @@ message ServerInfoRequest {}
message ServerInfoResponse {
// The version of the server instance.
string version = 1;

// Unix timestamp (seconds) indicating when the server process has been started.
int64 startedAt = 2;

// Total number of transactions across all databases.
int64 numTransactions = 3;

// Total number of databases present.
int32 numDatabases = 4;

// Total disk size used by all databases.
int64 databasesDiskSize = 5;
}

message HealthResponse {
Expand Down
8 changes: 7 additions & 1 deletion pkg/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ type DB interface {

Size() (uint64, error)

TxCount() (uint64, error)

// Key-Value
Set(ctx context.Context, req *schema.SetRequest) (*schema.TxHeader, error)
VerifiableSet(ctx context.Context, req *schema.VerifiableSetRequest) (*schema.VerifiableTx, error)
Expand Down Expand Up @@ -911,8 +913,12 @@ func (d *db) GetAll(ctx context.Context, req *schema.KeyListRequest) (*schema.En
return list, nil
}

// Size ...
func (d *db) Size() (uint64, error) {
return d.st.Size()
}

// TxCount ...
func (d *db) TxCount() (uint64, error) {
return d.st.TxCount(), nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/database/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func TestDefaultDbCreation(t *testing.T) {

defer db.Close()

n, err := db.Size()
n, err := db.TxCount()
require.NoError(t, err)
require.Zero(t, n)

Expand Down
4 changes: 4 additions & 0 deletions pkg/server/db_dummy_closed.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ func (db *closedDB) Size() (uint64, error) {
return 0, store.ErrAlreadyClosed
}

func (db *closedDB) TxCount() (uint64, error) {
return 0, store.ErrAlreadyClosed
}

func (db *closedDB) Set(ctx context.Context, req *schema.SetRequest) (*schema.TxHeader, error) {
return nil, store.ErrAlreadyClosed
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/server/db_dummy_closed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestDummyClosedDatabase(t *testing.T) {
_, err = cdb.CurrentState()
require.ErrorIs(t, err, store.ErrAlreadyClosed)

_, err = cdb.Size()
_, err = cdb.TxCount()
require.ErrorIs(t, err, store.ErrAlreadyClosed)

_, err = cdb.Set(context.Background(), nil)
Expand Down
60 changes: 58 additions & 2 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (s *ImmuServer) Initialize() error {

defaultDB, _ := s.dbList.GetByIndex(defaultDbIndex)

dbSize, _ := defaultDB.Size()
dbSize, _ := defaultDB.TxCount()
if dbSize <= 1 {
s.Logger.Infof("started with an empty default database")
}
Expand Down Expand Up @@ -802,7 +802,63 @@ func (s *ImmuServer) UpdateMTLSConfig(ctx context.Context, req *schema.MTLSConfi

// ServerInfo returns information about the server instance.
func (s *ImmuServer) ServerInfo(ctx context.Context, req *schema.ServerInfoRequest) (*schema.ServerInfoResponse, error) {
return &schema.ServerInfoResponse{Version: version.Version}, nil
dbSize, err := s.totalDBSize()
if err != nil {
return nil, err
}

numTransactions, err := s.numTransactions()
if err != nil {
return nil, err
}

return &schema.ServerInfoResponse{
Version: version.Version,
StartedAt: startedAt.Unix(),
NumTransactions: int64(numTransactions),
NumDatabases: int32(s.dbList.Length()),
DatabasesDiskSize: dbSize,
}, err
}

func (s *ImmuServer) numTransactions() (uint64, error) {
s.dbListMutex.Lock()
defer s.dbListMutex.Unlock()

var count uint64
for i := 0; i < s.dbList.Length(); i++ {
db, err := s.dbList.GetByIndex(i)
if err != nil {
return 0, err
}

dbTxCount, err := db.TxCount()
if err != nil {
return 0, err
}
count += dbTxCount
}
return count, nil
}

func (s *ImmuServer) totalDBSize() (int64, error) {
s.dbListMutex.Lock()
defer s.dbListMutex.Unlock()

var size int64
for i := 0; i < s.dbList.Length(); i++ {
db, err := s.dbList.GetByIndex(i)
if err != nil {
return -1, err
}

dbSize, err := db.Size()
if err != nil {
return -1, err
}
size += int64(dbSize)
}
return size, nil
}

// Health ...
Expand Down
17 changes: 12 additions & 5 deletions pkg/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"testing"
"time"

"github.com/codenotary/immudb/cmd/version"
"github.com/codenotary/immudb/pkg/fs"
"github.com/codenotary/immudb/pkg/stream"
"golang.org/x/crypto/bcrypt"
Expand Down Expand Up @@ -168,7 +169,7 @@ func TestServerResetAdminPassword(t *testing.T) {
_, err = s.getValidatedUser(context.Background(), []byte(auth.SysAdminUsername), []byte("password2"))
require.ErrorContains(t, err, "password")

txID, err = s.sysDB.Size()
txID, err = s.sysDB.TxCount()
require.NoError(t, err)
})

Expand All @@ -179,7 +180,7 @@ func TestServerResetAdminPassword(t *testing.T) {
err := s.loadSystemDatabase(dbRootpath, nil, "password2", false)
require.NoError(t, err)

currTxID, err := s.sysDB.Size()
currTxID, err := s.sysDB.TxCount()
require.NoError(t, err)
require.Equal(t, txID, currTxID)

Expand All @@ -198,7 +199,7 @@ func TestServerResetAdminPassword(t *testing.T) {
require.NoError(t, err)

// There should be new TX with updated password
currTxID, err := s.sysDB.Size()
currTxID, err := s.sysDB.TxCount()
require.NoError(t, err)
require.Equal(t, txID+1, currTxID)

Expand All @@ -217,7 +218,7 @@ func TestServerResetAdminPassword(t *testing.T) {
require.NoError(t, err)

// No ne TX is needed
currTxID, err := s.sysDB.Size()
currTxID, err := s.sysDB.TxCount()
require.NoError(t, err)
require.Equal(t, txID+1, currTxID)

Expand Down Expand Up @@ -1007,8 +1008,14 @@ func testServerHistoryError(ctx context.Context, s *ImmuServer, t *testing.T) {
}

func testServerInfo(ctx context.Context, s *ImmuServer, t *testing.T) {
_, err := s.ServerInfo(ctx, &schema.ServerInfoRequest{})
resp, err := s.ServerInfo(ctx, &schema.ServerInfoRequest{})
require.NoError(t, err)

require.Equal(t, resp.Version, version.Version)
require.Equal(t, resp.StartedAt, startedAt.Unix())
require.Equal(t, resp.NumTransactions, int64(16))
require.GreaterOrEqual(t, resp.NumDatabases, int32(1))
require.Greater(t, resp.DatabasesDiskSize, int64(0))
}

func testServerHealth(ctx context.Context, s *ImmuServer, t *testing.T) {
Expand Down

0 comments on commit e472617

Please sign in to comment.