github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/mysql/stats.go (about) 1 package mysql 2 3 import ( 4 "context" 5 "database/sql" 6 "fmt" 7 8 "github.com/Masterminds/squirrel" 9 10 "github.com/authzed/spicedb/internal/datastore/common" 11 "github.com/authzed/spicedb/pkg/datastore" 12 ) 13 14 const ( 15 informationSchemaTableRowsColumn = "table_rows" 16 informationSchemaTablesTable = "INFORMATION_SCHEMA.TABLES" 17 informationSchemaTableNameColumn = "table_name" 18 19 metadataIDColumn = "id" 20 metadataUniqueIDColumn = "unique_id" 21 ) 22 23 func (mds *Datastore) Statistics(ctx context.Context) (datastore.Stats, error) { 24 if mds.analyzeBeforeStats { 25 _, err := mds.db.ExecContext(ctx, "ANALYZE TABLE "+mds.driver.RelationTuple()) 26 if err != nil { 27 return datastore.Stats{}, fmt.Errorf("unable to run ANALYZE TABLE: %w", err) 28 } 29 } 30 31 uniqueID, err := mds.getUniqueID(ctx) 32 if err != nil { 33 return datastore.Stats{}, err 34 } 35 36 query, args, err := sb. 37 Select(informationSchemaTableRowsColumn). 38 From(informationSchemaTablesTable). 39 Where(squirrel.Eq{informationSchemaTableNameColumn: mds.driver.RelationTuple()}). 40 ToSql() 41 if err != nil { 42 return datastore.Stats{}, err 43 } 44 45 var count sql.NullInt64 46 err = mds.db.QueryRowContext(ctx, query, args...).Scan(&count) 47 if err != nil { 48 return datastore.Stats{}, err 49 } 50 51 if !count.Valid || count.Int64 == 0 { 52 // If we get a count of zero, its possible the information schema table has not yet 53 // been updated, so we use a slower count(*) call. 54 query, args, err := mds.QueryBuilder.CountTupleQuery.ToSql() 55 if err != nil { 56 return datastore.Stats{}, err 57 } 58 err = mds.db.QueryRowContext(ctx, query, args...).Scan(&count) 59 if err != nil { 60 return datastore.Stats{}, err 61 } 62 } 63 64 nsQuery := mds.ReadNamespaceQuery.Where(squirrel.Eq{colDeletedTxn: liveDeletedTxnID}) 65 66 tx, err := mds.db.BeginTx(ctx, nil) 67 if err != nil { 68 return datastore.Stats{}, err 69 } 70 defer common.LogOnError(ctx, tx.Rollback) 71 72 nsDefs, err := loadAllNamespaces(ctx, tx, nsQuery) 73 if err != nil { 74 return datastore.Stats{}, fmt.Errorf("unable to load namespaces: %w", err) 75 } 76 77 return datastore.Stats{ 78 UniqueID: uniqueID, 79 ObjectTypeStatistics: datastore.ComputeObjectTypeStats(nsDefs), 80 EstimatedRelationshipCount: uint64(count.Int64), 81 }, nil 82 } 83 84 func (mds *Datastore) getUniqueID(ctx context.Context) (string, error) { 85 sql, args, err := sb.Select(metadataUniqueIDColumn).From(mds.driver.Metadata()).ToSql() 86 if err != nil { 87 return "", fmt.Errorf("unable to generate query sql: %w", err) 88 } 89 90 var uniqueID string 91 if err := mds.db.QueryRowContext(ctx, sql, args...).Scan(&uniqueID); err != nil { 92 return "", fmt.Errorf("unable to query unique ID: %w", err) 93 } 94 95 return uniqueID, nil 96 }