github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/postgres/migrations/zz_migration.0011_backfill_xid_add_indices.go (about)

     1  package migrations
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/jackc/pgx/v5"
     8  	"github.com/jackc/pgx/v5/pgconn"
     9  
    10  	log "github.com/authzed/spicedb/internal/logging"
    11  	"github.com/authzed/spicedb/pkg/migrate"
    12  )
    13  
    14  var (
    15  	addRelationTupleDefault = `
    16  		ALTER TABLE relation_tuple
    17  			ALTER COLUMN created_xid SET DEFAULT (pg_current_xact_id());`
    18  
    19  	addNamepsaceDefault = `
    20  		ALTER TABLE namespace_config
    21  			ALTER COLUMN created_xid SET DEFAULT (pg_current_xact_id());`
    22  
    23  	addCaveatDefault = `
    24  		ALTER TABLE caveat
    25  			ALTER COLUMN created_xid SET DEFAULT (pg_current_xact_id());`
    26  )
    27  
    28  var addBackfillIndices = []string{
    29  	addRelationTupleDefault,
    30  	addNamepsaceDefault,
    31  	addCaveatDefault,
    32  	`CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_backfill_rtt_temp
    33  		ON relation_tuple_transaction ( (snapshot IS NULL) )`,
    34  	`CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_backfill_ns_temp
    35  		ON namespace_config ( (created_xid IS NULL) )`,
    36  	`CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_backfill_tuple_temp
    37  		ON relation_tuple ( (created_xid IS NULL) )`,
    38  	`CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_backfill_caveat_temp
    39  		ON caveat ( (created_xid IS NULL) )`,
    40  }
    41  
    42  var backfills = []string{
    43  	`UPDATE relation_tuple_transaction 
    44  		SET xid = id::text::xid8, snapshot = CONCAT(id, ':', id, ':')::pg_snapshot
    45  		WHERE id IN (
    46  			SELECT id FROM relation_tuple_transaction
    47  			WHERE snapshot IS NULL
    48  			LIMIT %d
    49  			FOR UPDATE
    50  		);`,
    51  	`UPDATE relation_tuple 
    52  		SET deleted_xid = deleted_transaction::text::xid8,
    53  		created_xid = created_transaction::text::xid8
    54  		WHERE (namespace, object_id, relation, userset_namespace, userset_object_id,
    55  			   userset_relation, created_transaction, deleted_transaction
    56  		) IN (
    57  			SELECT namespace, object_id, relation, userset_namespace, userset_object_id,
    58  				userset_relation, created_transaction, deleted_transaction
    59  			FROM relation_tuple
    60  			WHERE created_xid IS NULL
    61  			LIMIT %d
    62  			FOR UPDATE
    63  		);`,
    64  	`UPDATE namespace_config 
    65  		SET deleted_xid = deleted_transaction::text::xid8,
    66  		created_xid = created_transaction::text::xid8
    67  		WHERE (namespace, created_transaction, deleted_transaction) IN (
    68  			SELECT namespace, created_transaction, deleted_transaction
    69  			FROM namespace_config
    70  			WHERE created_xid IS NULL
    71  			LIMIT %d
    72  			FOR UPDATE
    73  		);`,
    74  	`UPDATE caveat 
    75  		SET deleted_xid = deleted_transaction::text::xid8,
    76  		created_xid = created_transaction::text::xid8
    77  		WHERE (name, created_transaction, deleted_transaction) IN (
    78  			SELECT name, created_transaction, deleted_transaction
    79  			FROM caveat
    80  			WHERE created_xid IS NULL
    81  			LIMIT %d
    82  			FOR UPDATE
    83  		);`,
    84  }
    85  
    86  var addXIDIndices = []string{
    87  	// Replace the indices that are inherent from having a primary key constraint
    88  	`CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS ix_rttx_oldpk
    89  		ON relation_tuple_transaction (id)`,
    90  	`CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS ix_namespace_config_oldpk
    91  		ON namespace_config (id)`,
    92  
    93  	// Add indices that will eventually back our new constraints
    94  	`CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS ix_rttx_pk
    95  		ON relation_tuple_transaction (xid);`,
    96  	`CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS ix_namespace_config_pk
    97  		ON namespace_config (namespace, created_xid, deleted_xid);`,
    98  	`CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS ix_namespace_config_living
    99  		ON namespace_config (namespace, deleted_xid);`,
   100  	`CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS ix_relation_tuple_pk
   101  		ON relation_tuple (namespace, object_id, relation, userset_namespace, userset_object_id,
   102  						   userset_relation, created_xid, deleted_xid);`,
   103  	`CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS ix_relation_tuple_living
   104  		ON relation_tuple (namespace, object_id, relation, userset_namespace, userset_object_id,
   105  						   userset_relation, deleted_xid);`,
   106  	`CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS ix_caveat_unique
   107  		ON caveat (name, created_xid, deleted_xid);`,
   108  	`CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS ix_caveat_living
   109  		ON caveat (name, deleted_xid);`,
   110  }
   111  
   112  var dropBackfillIndices = []string{
   113  	"DROP INDEX ix_backfill_rtt_temp",
   114  	"DROP INDEX ix_backfill_ns_temp",
   115  	"DROP INDEX ix_backfill_tuple_temp",
   116  	"DROP INDEX ix_backfill_caveat_temp",
   117  }
   118  
   119  func init() {
   120  	if err := DatabaseMigrations.Register("backfill-xid-add-indices", "add-xid-columns",
   121  		func(ctx context.Context, conn *pgx.Conn) error {
   122  			for _, stmt := range addBackfillIndices {
   123  				if _, err := conn.Exec(ctx, stmt); err != nil {
   124  					return err
   125  				}
   126  			}
   127  
   128  			batchSize := ctx.Value(migrate.BackfillBatchSize).(uint64)
   129  			for _, stmt := range backfills {
   130  				concreteStmt := fmt.Sprintf(stmt, batchSize)
   131  
   132  				log.Ctx(ctx).Info().Str("statement", concreteStmt).Msg("starting backfill")
   133  
   134  				var r pgconn.CommandTag
   135  				var err error
   136  
   137  				for r, err = conn.Exec(ctx, concreteStmt); err == nil && r.RowsAffected() > 0; r, err = conn.Exec(ctx, concreteStmt) {
   138  					log.Ctx(ctx).Debug().Int64("count", r.RowsAffected()).Msg("updated rows")
   139  				}
   140  				if err != nil {
   141  					return err
   142  				}
   143  			}
   144  
   145  			for _, stmt := range append(addXIDIndices, dropBackfillIndices...) {
   146  				if _, err := conn.Exec(ctx, stmt); err != nil {
   147  					return err
   148  				}
   149  			}
   150  
   151  			return nil
   152  		},
   153  		noTxMigration,
   154  	); err != nil {
   155  		panic("failed to register migration: " + err.Error())
   156  	}
   157  }