Skip to content

Commit

Permalink
Merge #97534
Browse files Browse the repository at this point in the history
97534: builtins: add `tenant_span_stats` generator function r=THardy98 a=THardy98

Part of: #94332
Part of: #94330

Added new builtin function `tenant_span_stats` that retrieves span
statistics for the current tenant. `tenant_span_stats` can be called as:
- `crdb_internal.tenant_span_stats()`: returns table span statistics for all of the tenants tables
- `crdb_internal.tenant_span_stats(database_id)`: returns table span statistics for the tenant's tables belonging to the specified database id
- `crdb_internal.tenant_span_stats(database_id, table_id)`: returns the tenant's table span statistics for the provided table id

Returned rows take the format:
```
[email protected]:26257/movr> select * from crdb_internal.tenant_span_stats(104);
  database_id | table_id | range_count | approximate_disk_bytes | live_bytes | total_bytes | live_percentage
--------------+----------+-------------+------------------------+------------+-------------+------------------
          104 |      106 |          27 |                  53866 |      16821 |       16821 |               1
          104 |      107 |          27 |                  45285 |      10932 |       10932 |               1
          104 |      108 |          27 |                 225645 |     528219 |      528219 |               1
          104 |      109 |           3 |                 130978 |     260826 |      260826 |               1
          104 |      110 |           3 |                 393815 |     708387 |      708387 |               1
          104 |      111 |           3 |                  30021 |       1413 |        1413 |               1
```

**Note**: This implementation is naive - it calls `SpanStats` for each table span. Future work will entail expanding the endpoint to be able to fetch span statistics for multiple spans at once.

Release note (sql change): new builtin function `tenants_span_stats`,
retrieves the span statistics for the current tenant.

Co-authored-by: Thomas Hardy <[email protected]>
  • Loading branch information
craig[bot] and Thomas Hardy committed Mar 2, 2023
2 parents f5ec545 + 5d38cb0 commit 5bae6a0
Show file tree
Hide file tree
Showing 9 changed files with 359 additions and 0 deletions.
6 changes: 6 additions & 0 deletions docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,12 @@ the locality flag on node startup. Returns an error if no region is set.</p>
</span></td><td>Stable</td></tr>
<tr><td><a name="crdb_internal.scan"></a><code>crdb_internal.scan(start_key: <a href="bytes.html">bytes</a>, end_key: <a href="bytes.html">bytes</a>) &rarr; tuple{bytes AS key, bytes AS value, string AS ts}</code></td><td><span class="funcdesc"><p>Returns the raw keys and values with their timestamp from the specified span</p>
</span></td><td>Stable</td></tr>
<tr><td><a name="crdb_internal.tenant_span_stats"></a><code>crdb_internal.tenant_span_stats() &rarr; tuple{int AS database_id, int AS table_id, int AS range_count, int AS approximate_disk_<a href="bytes.html">bytes</a>, int AS live_<a href="bytes.html">bytes</a>, int AS total_<a href="bytes.html">bytes</a>, float AS live_percentage}</code></td><td><span class="funcdesc"><p>Returns statistics (range count, disk size, live range bytes, total range bytes, live range byte percentage) for all of the tenant’s tables.</p>
</span></td><td>Stable</td></tr>
<tr><td><a name="crdb_internal.tenant_span_stats"></a><code>crdb_internal.tenant_span_stats(database_id: <a href="int.html">int</a>) &rarr; tuple{int AS database_id, int AS table_id, int AS range_count, int AS approximate_disk_<a href="bytes.html">bytes</a>, int AS live_<a href="bytes.html">bytes</a>, int AS total_<a href="bytes.html">bytes</a>, float AS live_percentage}</code></td><td><span class="funcdesc"><p>Returns statistics (range count, disk size, live range bytes, total range bytes, live range byte percentage) for tables of the provided database id.</p>
</span></td><td>Stable</td></tr>
<tr><td><a name="crdb_internal.tenant_span_stats"></a><code>crdb_internal.tenant_span_stats(database_id: <a href="int.html">int</a>, table_id: <a href="int.html">int</a>) &rarr; tuple{int AS database_id, int AS table_id, int AS range_count, int AS approximate_disk_<a href="bytes.html">bytes</a>, int AS live_<a href="bytes.html">bytes</a>, int AS total_<a href="bytes.html">bytes</a>, float AS live_percentage}</code></td><td><span class="funcdesc"><p>Returns statistics (range count, disk size, live range bytes, total range bytes, live range byte percentage) for the provided table id.</p>
</span></td><td>Stable</td></tr>
<tr><td><a name="crdb_internal.testing_callback"></a><code>crdb_internal.testing_callback(name: <a href="string.html">string</a>) &rarr; <a href="int.html">int</a></code></td><td><span class="funcdesc"><p>For internal CRDB testing only. The function calls a callback identified by <code>name</code> registered with the server by the test.</p>
</span></td><td>Volatile</td></tr>
<tr><td><a name="crdb_internal.unary_table"></a><code>crdb_internal.unary_table() &rarr; tuple</code></td><td><span class="funcdesc"><p>Produces a virtual table containing a single row with no values.</p>
Expand Down
7 changes: 7 additions & 0 deletions pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

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

2 changes: 2 additions & 0 deletions pkg/sql/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,8 @@ func (p *planner) HasOwnershipOnSchema(
return hasOwnership, nil
}

// HasViewActivityOrViewActivityRedactedRole implements the AuthorizationAccessor interface.
// Requires a valid transaction to be open.
func (p *planner) HasViewActivityOrViewActivityRedactedRole(ctx context.Context) (bool, error) {
if hasAdmin, err := p.HasAdminRole(ctx); err != nil {
return hasAdmin, err
Expand Down
21 changes: 21 additions & 0 deletions pkg/sql/faketreeeval/evalctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,20 @@ func (ep *DummyEvalPlanner) GetRangeDescByID(
return
}

// SpanStats is part of the eval.Planner interface.
func (ep *DummyEvalPlanner) SpanStats(
context.Context, roachpb.RKey, roachpb.RKey,
) (stats *roachpb.SpanStatsResponse, err error) {
return
}

// GetDetailsForSpanStats is part of the eval.Planner interface.
func (ep *DummyEvalPlanner) GetDetailsForSpanStats(
context.Context, int, int,
) (it eval.InternalRows, err error) {
return
}

// DummyPrivilegedAccessor implements the tree.PrivilegedAccessor interface by returning errors.
type DummyPrivilegedAccessor struct{}

Expand Down Expand Up @@ -532,6 +546,13 @@ func (ep *DummySessionAccessor) HasRoleOption(
return false, errors.WithStack(errEvalSessionVar)
}

// HasViewActivityOrViewActivityRedactedRole is part of the eval.SessionAccessor interface.
func (ep *DummySessionAccessor) HasViewActivityOrViewActivityRedactedRole(
context.Context,
) (bool, error) {
return false, errors.WithStack(errEvalSessionVar)
}

// DummyClientNoticeSender implements the eval.ClientNoticeSender interface.
type DummyClientNoticeSender struct{}

Expand Down
130 changes: 130 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/tenant_span_stats
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# LogicTest: 3node-tenant

# Create a second database.
statement ok
CREATE DATABASE a

# Create a table for database.
statement ok
CREATE TABLE a.b (id INT PRIMARY KEY)

# Insert data into the table
statement ok
INSERT INTO a.b SELECT generate_series(1, 10)

# Create a second table for database.
statement ok
CREATE TABLE a.c (id INT PRIMARY KEY)

# SELECT * FROM crdb_internal.tenant_span_stats: span stats for all of the tenant's tables.
# Assert the schema.
query IIIIIIR colnames
SELECT * FROM crdb_internal.tenant_span_stats() LIMIT 0
----
database_id table_id range_count approximate_disk_bytes live_bytes total_bytes live_percentage

# SELECT DISTINCT(database_id) FROM crdb_internal.tenant_span_stats:
# Assert that we are collecting span stats for tables across multiple databases.
query I colnames
SELECT DISTINCT(database_id) FROM crdb_internal.tenant_span_stats()
----
database_id
1
106

# SELECT database_id, table_id FROM crdb_internal.tenant_span_stats(106):
# Assert that we are collecting span stats scoped to the provided database id.
query II colnames
SELECT database_id, table_id FROM crdb_internal.tenant_span_stats(106)
----
database_id table_id
106 108
106 109

# SELECT database_id, table_id FROM crdb_internal.tenant_span_stats(106, 108):
# Assert that we are collecting span stats scoped to the provided database/table id combo.
query II colnames
SELECT database_id, table_id FROM crdb_internal.tenant_span_stats(106, 108)
----
database_id table_id
106 108

# Assert that we cannot provide an invalid database id.
query error pq: provided database id must be greater than or equal to 1
SELECT database_id, table_id FROM crdb_internal.tenant_span_stats(0)

# Assert that we cannot provide an invalid table id.
query error pq: provided table id must be greater than or equal to 1
SELECT database_id, table_id FROM crdb_internal.tenant_span_stats(1, -1)

# Assert that we cannot provide an invalid database id with a valid table id.
query error pq: provided database id must be greater than or equal to 1
SELECT database_id, table_id FROM crdb_internal.tenant_span_stats(-1, 1)

# SELECT * FROM crdb_internal.tenant_span_stats(1000):
# Assert that we get empty rows for a database id that does not correspond to a database.
query IIIIIIR colnames
SELECT * FROM crdb_internal.tenant_span_stats(1000)
----
database_id table_id range_count approximate_disk_bytes live_bytes total_bytes live_percentage

# SELECT * FROM crdb_internal.tenant_span_stats(1, 1000):
# Assert that we get empty rows for a table id that does not correspond to a table.
query IIIIIIR colnames
SELECT * FROM crdb_internal.tenant_span_stats(1, 1000)
----
database_id table_id range_count approximate_disk_bytes live_bytes total_bytes live_percentage

# Select everything from a table to have 'live bytes'.
statement ok
SELECT * FROM a.b;

# Assert that we are collecting non-zero span stats scoped to the provided database/table id combo.
query IIBBBB colnames
SELECT database_id, table_id, range_count > 0 as range_count, live_bytes > 0 as live_bytes, total_bytes > 0 as total_bytes, live_percentage > 0 as live_percentage FROM crdb_internal.tenant_span_stats(106, 108)
----
database_id table_id range_count live_bytes total_bytes live_percentage
106 108 true true true true

# Create a second user without VIEWACTIVITY permission.
statement ok
CREATE USER testuser2

# Switch to user2
user testuser2

# Assert that the user2 doesn't have permission to use this builtin.
query error pq: user needs ADMIN role or the VIEWACTIVITY/VIEWACTIVITYREDACTED permission to view span statistics
SELECT * FROM crdb_internal.tenant_span_stats() LIMIT 0

# Switch to root
user root

# Grant VIEWACTIVITY permission to second user.
statement ok
ALTER ROLE testuser2 WITH VIEWACTIVITY

user testuser2

# Assert that the user2 has permission to use this builtin.
query IIIIIIR colnames
SELECT * FROM crdb_internal.tenant_span_stats() LIMIT 0
----
database_id table_id range_count approximate_disk_bytes live_bytes total_bytes live_percentage

# Switch to root
user root

# Remove VIEWACTIVITY permission from second user.
statement ok
ALTER ROLE testuser2 WITH NOVIEWACTIVITY

# Grant VIEWACTIVITYREDACTED permission to second user.
statement ok
ALTER ROLE testuser2 WITH VIEWACTIVITYREDACTED

# Assert that the user2 has permission to use this builtin.
query IIIIIIR colnames
SELECT * FROM crdb_internal.tenant_span_stats() LIMIT 0
----
database_id table_id range_count approximate_disk_bytes live_bytes total_bytes live_percentage
44 changes: 44 additions & 0 deletions pkg/sql/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/keyvisualizer"
"github.com/cockroachdb/cockroach/pkg/kv"
"github.com/cockroachdb/cockroach/pkg/repstream"
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/security/username"
"github.com/cockroachdb/cockroach/pkg/server/serverpb"
"github.com/cockroachdb/cockroach/pkg/spanconfig"
Expand Down Expand Up @@ -834,3 +835,46 @@ func (p *planner) GetReplicationStreamManager(
func (p *planner) GetStreamIngestManager(ctx context.Context) (eval.StreamIngestManager, error) {
return repstream.GetStreamIngestManager(ctx, p.EvalContext(), p.InternalSQLTxn())
}

// SpanStats returns a stats for the given span of keys.
func (p *planner) SpanStats(
ctx context.Context, startKey roachpb.RKey, endKey roachpb.RKey,
) (*roachpb.SpanStatsResponse, error) {
req := &roachpb.SpanStatsRequest{
NodeID: "0",
StartKey: startKey,
EndKey: endKey,
}
return p.ExecCfg().TenantStatusServer.SpanStats(ctx, req)
}

// GetDetailsForSpanStats ensures that the given database and table id exist.
// No rows will be returned for database/table ids that do not correspond to an actual
// database/table.
func (p *planner) GetDetailsForSpanStats(
ctx context.Context, dbId int, tableId int,
) (eval.InternalRows, error) {
query := `SELECT parent_id, table_id FROM crdb_internal.tables`
var args []interface{}

if tableId != 0 {
query += ` WHERE parent_id = $1 AND table_id = $2`
args = append(args, dbId, tableId)
} else if dbId != 0 {
query += ` WHERE parent_id = $1`
args = append(args, dbId)
} else {
// Some tables belonging to crdb_internal.tables are not affiliated with a database
// and have a parent_id of 0 (usually crdb_internal or pg catalog tables), which aren't useful to the user.
query += ` WHERE parent_id != $1`
args = append(args, dbId)
}

return p.QueryIteratorEx(
ctx,
"crdb_internal.database_span_stats",
sessiondata.NoSessionDataOverride,
query,
args...,
)
}
3 changes: 3 additions & 0 deletions pkg/sql/sem/builtins/fixed_oids.go
Original file line number Diff line number Diff line change
Expand Up @@ -2335,6 +2335,9 @@ var builtinOidsArray = []string{
2361: `geography(geometry: geometry) -> geography`,
2362: `geography(geography: geography) -> geography`,
2363: `geography(bytes: bytes) -> geography`,
2364: `crdb_internal.tenant_span_stats() -> tuple{int AS database_id, int AS table_id, int AS range_count, int AS approximate_disk_bytes, int AS live_bytes, int AS total_bytes, float AS live_percentage}`,
2365: `crdb_internal.tenant_span_stats(database_id: int) -> tuple{int AS database_id, int AS table_id, int AS range_count, int AS approximate_disk_bytes, int AS live_bytes, int AS total_bytes, float AS live_percentage}`,
2366: `crdb_internal.tenant_span_stats(database_id: int, table_id: int) -> tuple{int AS database_id, int AS table_id, int AS range_count, int AS approximate_disk_bytes, int AS live_bytes, int AS total_bytes, float AS live_percentage}`,
}

var builtinOidsBySignature map[string]oid.Oid
Expand Down
Loading

0 comments on commit 5bae6a0

Please sign in to comment.