satellite/metabase: add method to get table statistics

In loop we need to start verifying it's correctness. This allows to
gather these stats.

Change-Id: I146fb50e2b3658b6f3c2682cdc1983e6abd73c29
This commit is contained in:
Egon Elbre 2021-06-07 12:20:06 +03:00
parent 2ebbc9757f
commit 0df4a27bf7
5 changed files with 157 additions and 5 deletions

View File

@ -74,9 +74,8 @@ func Open(ctx context.Context, log *zap.Logger, connstr string) (*DB, error) {
return db, nil
}
// InternalImplementation returns *metabase.DB.
// TODO: remove.
func (db *DB) InternalImplementation() interface{} { return db }
// Implementation rturns the database implementation.
func (db *DB) Implementation() dbutil.Implementation { return db.impl }
// UnderlyingTagSQL returns *tagsql.DB.
// TODO: remove.

View File

@ -37,7 +37,7 @@ func Run(t *testing.T, fn func(ctx *testcontext.Context, t *testing.T, db *metab
t.Fatal(err)
}
fn(ctx, t, db.InternalImplementation().(*metabase.DB))
fn(ctx, t, db)
})
}
}
@ -65,7 +65,7 @@ func Bench(b *testing.B, fn func(ctx *testcontext.Context, b *testing.B, db *met
}
b.ResetTimer()
fn(ctx, b, db.InternalImplementation().(*metabase.DB))
fn(ctx, b, db)
})
}
}

View File

@ -721,3 +721,22 @@ func (step DeletePart) Check(ctx *testcontext.Context, t testing.TB, db *metabas
diff := cmp.Diff(step.Result, result, cmpopts.EquateApproxTime(5*time.Second))
require.Zero(t, diff)
}
// GetTableStats is for testing metabase.GetTableStats.
type GetTableStats struct {
Opts metabase.GetTableStats
Result metabase.TableStats
ErrClass *errs.Class
ErrText string
}
// Check runs the test.
func (step GetTableStats) Check(ctx *testcontext.Context, t testing.TB, db *metabase.DB) metabase.TableStats {
result, err := db.GetTableStats(ctx, step.Opts)
checkError(t, err, step.ErrClass, step.ErrText)
diff := cmp.Diff(step.Result, result)
require.Zero(t, diff)
return result
}

View File

@ -0,0 +1,41 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package metabase
import (
"context"
"time"
"github.com/zeebo/errs"
"storj.io/common/errs2"
)
// GetTableStats contains arguments necessary for getting table statistics.
type GetTableStats struct {
AsOfSystemInterval time.Duration
}
// TableStats contains information about the metabase status.
type TableStats struct {
ObjectCount int64
SegmentCount int64
}
// GetTableStats gathers information about the metabase tables.
func (db *DB) GetTableStats(ctx context.Context, opts GetTableStats) (result TableStats, err error) {
defer mon.Task()(&ctx)(&err)
var group errs2.Group
group.Go(func() error {
row := db.db.QueryRow(ctx, `SELECT count(*) FROM objects `+db.impl.AsOfSystemInterval(opts.AsOfSystemInterval))
return Error.Wrap(row.Scan(&result.ObjectCount))
})
group.Go(func() error {
row := db.db.QueryRow(ctx, `SELECT count(*) FROM segments `+db.impl.AsOfSystemInterval(opts.AsOfSystemInterval))
return Error.Wrap(row.Scan(&result.SegmentCount))
})
err = errs.Combine(group.Wait()...)
return result, err
}

View File

@ -0,0 +1,93 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package metabase_test
import (
"testing"
"time"
"storj.io/common/testcontext"
"storj.io/private/dbutil"
"storj.io/storj/satellite/metabase"
"storj.io/storj/satellite/metabase/metabasetest"
)
func TestGetTableStats(t *testing.T) {
metabasetest.Run(t, func(ctx *testcontext.Context, t *testing.T, db *metabase.DB) {
t.Run("no data", func(t *testing.T) {
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
metabasetest.GetTableStats{
Result: metabase.TableStats{},
}.Check(ctx, t, db)
metabasetest.Verify{}.Check(ctx, t, db)
})
t.Run("data", func(t *testing.T) {
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
obj1 := metabasetest.RandObjectStream()
metabasetest.CreateTestObject{}.Run(ctx, t, db, obj1, 4)
metabasetest.GetTableStats{
Result: metabase.TableStats{
ObjectCount: 1,
SegmentCount: 4,
},
}.Check(ctx, t, db)
obj2 := metabasetest.RandObjectStream()
metabasetest.CreateTestObject{}.Run(ctx, t, db, obj2, 3)
metabasetest.GetTableStats{
Result: metabase.TableStats{
ObjectCount: 2,
SegmentCount: 7,
},
}.Check(ctx, t, db)
})
if db.Implementation() == dbutil.Cockroach {
t.Run("as of interval", func(t *testing.T) {
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
metabasetest.GetTableStats{
Opts: metabase.GetTableStats{
AsOfSystemInterval: -time.Microsecond,
},
Result: metabase.TableStats{
ObjectCount: 0,
SegmentCount: 0,
},
}.Check(ctx, t, db)
time.Sleep(2 * time.Second)
obj1 := metabasetest.RandObjectStream()
metabasetest.CreateTestObject{}.Run(ctx, t, db, obj1, 4)
metabasetest.GetTableStats{
Opts: metabase.GetTableStats{
AsOfSystemInterval: -1 * time.Second,
},
Result: metabase.TableStats{
ObjectCount: 0,
SegmentCount: 0,
},
}.Check(ctx, t, db)
metabasetest.GetTableStats{
Opts: metabase.GetTableStats{
AsOfSystemInterval: -time.Microsecond,
},
Result: metabase.TableStats{
ObjectCount: 1,
SegmentCount: 4,
},
}.Check(ctx, t, db)
})
}
})
}