-
Notifications
You must be signed in to change notification settings - Fork 291
/
Copy pathstats.go
88 lines (71 loc) · 2.6 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
package crdb
import (
"context"
"fmt"
"math/rand"
"github.com/jackc/pgx/v4"
"github.com/shopspring/decimal"
"github.com/authzed/spicedb/pkg/datastore"
"github.com/authzed/spicedb/pkg/datastore/revision"
corev1 "github.com/authzed/spicedb/pkg/proto/core/v1"
)
const (
tableMetadata = "metadata"
colUniqueID = "unique_id"
tableCounters = "relationship_estimate_counters"
colID = "id"
colCount = "count"
)
var (
queryReadUniqueID = psql.Select(colUniqueID).From(tableMetadata)
queryRelationshipEstimate = fmt.Sprintf("SELECT COALESCE(SUM(%s), 0) FROM %s", colCount, tableCounters)
upsertCounterQuery = psql.Insert(tableCounters).Columns(
colID,
colCount,
).Suffix(fmt.Sprintf("ON CONFLICT (%[1]s) DO UPDATE SET %[2]s = %[3]s.%[2]s + EXCLUDED.%[2]s RETURNING cluster_logical_timestamp()", colID, colCount, tableCounters))
)
func (cds *crdbDatastore) Statistics(ctx context.Context) (datastore.Stats, error) {
sql, args, err := queryReadUniqueID.ToSql()
if err != nil {
return datastore.Stats{}, fmt.Errorf("unable to prepare unique ID sql: %w", err)
}
var uniqueID string
var nsDefs []*corev1.NamespaceDefinition
var relCount uint64
if err := cds.pool.BeginTxFunc(ctx, pgx.TxOptions{AccessMode: pgx.ReadOnly}, func(tx pgx.Tx) error {
if err := tx.QueryRow(ctx, sql, args...).Scan(&uniqueID); err != nil {
return fmt.Errorf("unable to query unique ID: %w", err)
}
if err := tx.QueryRow(ctx, queryRelationshipEstimate).Scan(&relCount); err != nil {
return fmt.Errorf("unable to read relationship count: %w", err)
}
nsDefs, err = loadAllNamespaces(ctx, tx)
if err != nil {
return fmt.Errorf("unable to read namespaces: %w", err)
}
return nil
}); err != nil {
return datastore.Stats{}, err
}
return datastore.Stats{
UniqueID: uniqueID,
EstimatedRelationshipCount: relCount,
ObjectTypeStatistics: datastore.ComputeObjectTypeStats(nsDefs),
}, nil
}
func updateCounter(ctx context.Context, tx pgx.Tx, change int64) (revision.Decimal, error) {
counterID := make([]byte, 2)
_, err := rand.Read(counterID)
if err != nil {
return revision.NoRevision, fmt.Errorf("unable to select random counter: %w", err)
}
sql, args, err := upsertCounterQuery.Values(counterID, change).ToSql()
if err != nil {
return revision.NoRevision, fmt.Errorf("unable to prepare upsert counter sql: %w", err)
}
var timestamp decimal.Decimal
if err := tx.QueryRow(ctx, sql, args...).Scan(×tamp); err != nil {
return revision.NoRevision, fmt.Errorf("unable to executed upsert counter query: %w", err)
}
return revision.NewFromDecimal(timestamp), nil
}