forked from authzed/spicedb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stats.go
148 lines (124 loc) · 3.98 KB
/
stats.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package crdb
import (
"context"
"fmt"
"slices"
"github.com/Masterminds/squirrel"
"github.com/jackc/pgx/v5"
"github.com/rs/zerolog/log"
pgxcommon "github.com/authzed/spicedb/internal/datastore/postgres/common"
"github.com/authzed/spicedb/pkg/datastore"
)
const (
tableMetadata = "metadata"
colUniqueID = "unique_id"
)
var queryReadUniqueID = psql.Select(colUniqueID).From(tableMetadata)
func (cds *crdbDatastore) UniqueID(ctx context.Context) (string, error) {
if cds.uniqueID.Load() == nil {
sql, args, err := queryReadUniqueID.ToSql()
if err != nil {
return "", fmt.Errorf("unable to prepare unique ID sql: %w", err)
}
var uniqueID string
if err := cds.readPool.QueryRowFunc(ctx, func(ctx context.Context, row pgx.Row) error {
return row.Scan(&uniqueID)
}, sql, args...); err != nil {
return "", fmt.Errorf("unable to query unique ID: %w", err)
}
cds.uniqueID.Store(&uniqueID)
return uniqueID, nil
}
return *cds.uniqueID.Load(), nil
}
func (cds *crdbDatastore) Statistics(ctx context.Context) (datastore.Stats, error) {
uniqueID, err := cds.UniqueID(ctx)
if err != nil {
return datastore.Stats{}, err
}
var nsDefs []datastore.RevisionedNamespace
if err := cds.readPool.BeginTxFunc(ctx, pgx.TxOptions{AccessMode: pgx.ReadOnly}, func(tx pgx.Tx) error {
_, err := tx.Exec(ctx, "SET TRANSACTION AS OF SYSTEM TIME follower_read_timestamp()")
if err != nil {
return fmt.Errorf("unable to read namespaces: %w", err)
}
nsDefs, err = loadAllNamespaces(ctx, pgxcommon.QuerierFuncsFor(tx), func(sb squirrel.SelectBuilder, fromStr string) squirrel.SelectBuilder {
return sb.From(fromStr)
})
if err != nil {
return fmt.Errorf("unable to read namespaces: %w", err)
}
return nil
}); err != nil {
return datastore.Stats{}, err
}
if cds.analyzeBeforeStatistics {
if err := cds.readPool.BeginTxFunc(ctx, pgx.TxOptions{AccessMode: pgx.ReadOnly}, func(tx pgx.Tx) error {
if _, err := tx.Exec(ctx, "ANALYZE "+tableTuple); err != nil {
return fmt.Errorf("unable to analyze tuple table: %w", err)
}
return nil
}); err != nil {
return datastore.Stats{}, err
}
}
var estimatedRelCount uint64
if err := cds.readPool.QueryFunc(ctx, func(ctx context.Context, rows pgx.Rows) error {
hasRows := false
for rows.Next() {
hasRows = true
values, err := rows.Values()
if err != nil {
log.Warn().Err(err).Msg("unable to read statistics")
return nil
}
// Find the row whose column_names contains the expected columns for the
// full relationship.
isFullRelationshipRow := false
for index, fd := range rows.FieldDescriptions() {
if fd.Name != "column_names" {
continue
}
columnNames, ok := values[index].([]any)
if !ok {
log.Warn().Msg("unable to read column names")
return nil
}
if slices.Contains(columnNames, "namespace") &&
slices.Contains(columnNames, "object_id") &&
slices.Contains(columnNames, "relation") &&
slices.Contains(columnNames, "userset_namespace") &&
slices.Contains(columnNames, "userset_object_id") &&
slices.Contains(columnNames, "userset_relation") {
isFullRelationshipRow = true
break
}
}
if !isFullRelationshipRow {
continue
}
// Read the estimated relationship count.
for index, fd := range rows.FieldDescriptions() {
if fd.Name != "row_count" {
continue
}
rowCount, ok := values[index].(int64)
if !ok {
log.Warn().Msg("unable to read row count")
return nil
}
estimatedRelCount = uint64(rowCount)
return nil
}
}
log.Warn().Bool("has-rows", hasRows).Msg("unable to find row count in statistics query result")
return nil
}, "SHOW STATISTICS FOR TABLE relation_tuple;"); err != nil {
return datastore.Stats{}, fmt.Errorf("unable to query unique estimated row count: %w", err)
}
return datastore.Stats{
UniqueID: uniqueID,
EstimatedRelationshipCount: estimatedRelCount,
ObjectTypeStatistics: datastore.ComputeObjectTypeStats(nsDefs),
}, nil
}