diff --git a/satellite/metabase/db.go b/satellite/metabase/db.go index 8dedb822b..c186e9776 100644 --- a/satellite/metabase/db.go +++ b/satellite/metabase/db.go @@ -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. diff --git a/satellite/metabase/metabasetest/run.go b/satellite/metabase/metabasetest/run.go index f21e7b84e..a46bfd3fc 100644 --- a/satellite/metabase/metabasetest/run.go +++ b/satellite/metabase/metabasetest/run.go @@ -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) }) } } diff --git a/satellite/metabase/metabasetest/test.go b/satellite/metabase/metabasetest/test.go index 167b77e9c..305f71d82 100644 --- a/satellite/metabase/metabasetest/test.go +++ b/satellite/metabase/metabasetest/test.go @@ -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 +} diff --git a/satellite/metabase/stats.go b/satellite/metabase/stats.go new file mode 100644 index 000000000..e03e5f71a --- /dev/null +++ b/satellite/metabase/stats.go @@ -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 +} diff --git a/satellite/metabase/stats_test.go b/satellite/metabase/stats_test.go new file mode 100644 index 000000000..54160d15d --- /dev/null +++ b/satellite/metabase/stats_test.go @@ -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) + }) + } + }) +}