github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/postgres/stats.go (about)

     1  package postgres
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	sq "github.com/Masterminds/squirrel"
     8  	"github.com/jackc/pgx/v5"
     9  
    10  	pgxcommon "github.com/authzed/spicedb/internal/datastore/postgres/common"
    11  	"github.com/authzed/spicedb/pkg/datastore"
    12  )
    13  
    14  const (
    15  	tableMetadata = "metadata"
    16  	colUniqueID   = "unique_id"
    17  
    18  	tablePGClass = "pg_class"
    19  	colReltuples = "reltuples"
    20  	colRelname   = "relname"
    21  )
    22  
    23  var (
    24  	queryUniqueID          = psql.Select(colUniqueID).From(tableMetadata)
    25  	queryEstimatedRowCount = psql.
    26  				Select(colReltuples).
    27  				From(tablePGClass).
    28  				Where(sq.Eq{colRelname: tableTuple})
    29  )
    30  
    31  func (pgd *pgDatastore) datastoreUniqueID(ctx context.Context) (string, error) {
    32  	idSQL, idArgs, err := queryUniqueID.ToSql()
    33  	if err != nil {
    34  		return "", fmt.Errorf("unable to generate query sql: %w", err)
    35  	}
    36  
    37  	var uniqueID string
    38  	return uniqueID, pgx.BeginTxFunc(ctx, pgd.readPool, pgd.readTxOptions, func(tx pgx.Tx) error {
    39  		return tx.QueryRow(ctx, idSQL, idArgs...).Scan(&uniqueID)
    40  	})
    41  }
    42  
    43  func (pgd *pgDatastore) Statistics(ctx context.Context) (datastore.Stats, error) {
    44  	idSQL, idArgs, err := queryUniqueID.ToSql()
    45  	if err != nil {
    46  		return datastore.Stats{}, fmt.Errorf("unable to generate query sql: %w", err)
    47  	}
    48  
    49  	rowCountSQL, rowCountArgs, err := queryEstimatedRowCount.ToSql()
    50  	if err != nil {
    51  		return datastore.Stats{}, fmt.Errorf("unable to prepare row count sql: %w", err)
    52  	}
    53  
    54  	filterer := func(original sq.SelectBuilder) sq.SelectBuilder {
    55  		return original.Where(sq.Eq{colDeletedXid: liveDeletedTxnID})
    56  	}
    57  
    58  	var uniqueID string
    59  	var nsDefs []datastore.RevisionedNamespace
    60  	var relCount int64
    61  	if err := pgx.BeginTxFunc(ctx, pgd.readPool, pgd.readTxOptions, func(tx pgx.Tx) error {
    62  		if pgd.analyzeBeforeStatistics {
    63  			if _, err := tx.Exec(ctx, "ANALYZE "+tableTuple); err != nil {
    64  				return fmt.Errorf("unable to analyze tuple table: %w", err)
    65  			}
    66  		}
    67  
    68  		if err := tx.QueryRow(ctx, idSQL, idArgs...).Scan(&uniqueID); err != nil {
    69  			return fmt.Errorf("unable to query unique ID: %w", err)
    70  		}
    71  
    72  		nsDefsWithRevisions, err := loadAllNamespaces(ctx, pgxcommon.QuerierFuncsFor(tx), filterer)
    73  		if err != nil {
    74  			return fmt.Errorf("unable to load namespaces: %w", err)
    75  		}
    76  
    77  		nsDefs = nsDefsWithRevisions
    78  
    79  		if err := tx.QueryRow(ctx, rowCountSQL, rowCountArgs...).Scan(&relCount); err != nil {
    80  			return fmt.Errorf("unable to read relationship count: %w", err)
    81  		}
    82  
    83  		return nil
    84  	}); err != nil {
    85  		return datastore.Stats{}, err
    86  	}
    87  
    88  	// Sometimes relCount can be negative on postgres, truncate to 0
    89  	var relCountUint uint64
    90  	if relCount > 0 {
    91  		relCountUint = uint64(relCount)
    92  	}
    93  
    94  	return datastore.Stats{
    95  		UniqueID:                   uniqueID,
    96  		ObjectTypeStatistics:       datastore.ComputeObjectTypeStats(nsDefs),
    97  		EstimatedRelationshipCount: relCountUint,
    98  	}, nil
    99  }