deferred

package
v0.61.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: Apache-2.0 Imports: 1 Imported by: 0

Documentation

Overview

Package deferred holds the bulk-load deferred-index manifest in a stand-alone package so each metadata plugin (sqlite, mysql, postgres) can import it without pulling in the parent metadata package, which side-effect-imports every plugin and would create an import cycle.

The parent metadata package owns the DeferredIndexManager interface that callers type-assert against. This package owns the data: what to drop, what to rebuild, and the sync_state key that records crash-recovery state.

Index

Constants

View Source
const SyncStateKey = "metadata_indexes_pending"

SyncStateKey is the sync_state row that marks an in-flight (or interrupted) deferred-index drop/rebuild cycle. The value is the string "true" while the rebuild is outstanding and is removed once every manifest entry is present.

View Source
const SyncStateValue = "true"

SyncStateValue is the literal sync_state value written while a drop/rebuild cycle is outstanding.

Variables

View Source
var Manifest = []Index{

	{
		Model: &models.Utxo{}, Field: "PaymentKey", Table: "utxo",
		Notes:    "API address lookup; not touched by UTxO insert or spend",
		Critical: true,
	},
	{
		Model: &models.Utxo{}, Field: "StakingKey", Table: "utxo",
		Notes:    "API stake lookup; not touched by UTxO insert or spend",
		Critical: true,
	},
	{
		Model: &models.Utxo{}, Field: "SpentAtTxId", Table: "utxo",
		Notes:    "Consumer-tx query and rollback repair (DeleteTransactionsAfterSlot); not used by spend predicate",
		Critical: true,
	},
	{
		Model: &models.Utxo{}, Field: "ReferencedByTxId", Table: "utxo",
		Notes:    "Reference-input query and rollback repair (DeleteTransactionsAfterSlot); not used by insert path",
		Critical: true,
	},
	{
		Model: &models.Utxo{}, Field: "CollateralByTxId", Table: "utxo",
		Notes:    "Collateral query and rollback repair (DeleteTransactionsAfterSlot); not used by insert path",
		Critical: true,
	},
	{
		Model: &models.Utxo{}, Field: "AddedSlot", Table: "utxo",
		Notes:    "Rollback range scan (DeleteUtxosAfterSlot); not used by insert or spend",
		Critical: true,
	},
	{
		Model: &models.Utxo{}, Field: "TransactionID", Table: "utxo",
		Notes: "FK reverse-lookup; cascades go the other direction during sync",
	},
	{
		Model: &models.Utxo{}, Name: "idx_utxo_deleted_staking_amount", Table: "utxo",
		Notes:    "Composite SearchUtxos index; primary utxorpc query path",
		Critical: true,
	},
	{
		Model: &models.Utxo{}, Name: "idx_utxo_staking_deleted_amount", Table: "utxo",
		Notes:    "DRep voting-power live UTxO SUM; live query path",
		Critical: true,
	},
	{
		Model: &models.Utxo{}, Name: "idx_utxo_deleted_payment_script", Table: "utxo",
		Notes:    "Script-locked supply SUM (blockfrost /network); live query path",
		Critical: true,
	},

	{
		Model: &models.Transaction{}, Field: "BlockHash", Table: "transaction",
		Notes:    "Block-tx grouping query (blockfrost /blocks/{id}/txs); not used by tx upsert",
		Critical: true,
	},
	{
		Model: &models.Transaction{}, Field: "Slot", Table: "transaction",
		Notes:    "Rollback range scan (DeleteTransactionsAfterSlot) and tx history ordering; not used by tx upsert",
		Critical: true,
	},

	{
		Model: &models.Asset{}, Field: "NameHex", Table: "asset",
		Notes: "Hex name lookup; query-only; no current WHERE name_hex=? path in API",
	},
	{
		Model: &models.Asset{}, Name: "idx_asset_policy_id", Table: "asset",
		Notes:    "Policy-id lookup (GetAssetsByPolicy); query-only; idx_asset_unique still covers import",
		Critical: true,
	},
	{
		Model: &models.Asset{}, Field: "Fingerprint", Table: "asset",
		Notes: "Fingerprint lookup; query-only; returned as response field, not a WHERE predicate",
	},
	{
		Model: &models.Asset{}, Field: "Amount", Table: "asset",
		Notes: "Amount range scan; query-only",
	},

	{
		Model: &models.Datum{}, Field: "AddedSlot", Table: "datum",
		Notes: "Rollback range scan; not used by datum upsert",
	},

	{
		Model: &models.Certificate{}, Field: "BlockHash", Table: "certs",
		Notes: "Block-cert query; not used by cert insert",
	},
	{
		Model: &models.Certificate{}, Field: "CertificateID", Table: "certs",
		Notes: "Polymorphic FK reverse-lookup; not enforced by DB",
	},
	{
		Model: &models.Certificate{}, Field: "Slot", Table: "certs",
		Notes:    "Rollback range scan (DeleteCertificatesAfterSlot); not used by cert insert",
		Critical: true,
	},
	{
		Model: &models.Certificate{}, Field: "CertType", Table: "certs",
		Notes: "Filter index; query-only",
	},

	{
		Model: &models.Redeemer{}, Field: "TransactionID", Table: "redeemer",
		Notes: "Witness query; FK enforced via parent Transaction",
	},
	{
		Model: &models.Redeemer{}, Field: "Index", Table: "redeemer",
		Notes: "Redeemer index lookup; query-only",
	},
	{
		Model: &models.Redeemer{}, Field: "Tag", Table: "redeemer",
		Notes: "Redeemer tag filter; query-only",
	},

	{
		Model: &models.KeyWitness{}, Field: "TransactionID", Table: "key_witness",
		Notes: "Witness query; FK cascade from Transaction handles inserts",
	},
	{
		Model: &models.KeyWitness{}, Field: "Type", Table: "key_witness",
		Notes: "Witness-type filter; query-only",
	},
	{
		Model: &models.WitnessScripts{}, Field: "ScriptHash", Table: "witness_scripts",
		Notes: "Script-hash lookup; query-only",
	},
	{
		Model: &models.WitnessScripts{}, Field: "TransactionID", Table: "witness_scripts",
		Notes: "Witness query; FK cascade from Transaction handles inserts",
	},
	{
		Model: &models.WitnessScripts{}, Field: "Type", Table: "witness_scripts",
		Notes: "Witness-type filter; query-only",
	},
}

Manifest is the canonical list of metadata-store indexes that are dropped before bulk load and rebuilt before the database is marked ready.

The list is intentionally conservative: it targets the heaviest write paths (utxo, transaction, asset, datum, witness, certs/redeemer secondary indexes) where API backfill spends the bulk of its time.

Order matters at rebuild time only as a logging convenience; SQLite builds each index in a single statement and does not benefit from re-ordering.

Functions

This section is empty.

Types

type Index

type Index struct {
	// Model is the GORM model the index is attached to. Used by
	// GORM's migrator to resolve the index back to the underlying
	// SQL statement.
	Model any
	// Field is the Go struct field name on Model. When the index
	// is a single-column index defined inline (`gorm:"index"`),
	// GORM resolves Field to an auto-generated index name during
	// DropIndex/CreateIndex.
	Field string
	// Name is the literal index name. Used for composite/named
	// indexes (e.g. idx_utxo_deleted_staking_amount) where the
	// struct-tag name takes precedence over field resolution.
	// Leave empty for single-column auto-named indexes.
	Name string
	// Table is the SQL table name. Carried separately because
	// some backends can drop an index without going through GORM.
	Table string
	// Notes documents why this index is safe to defer. Surfaces
	// in the manifest test failure message when the
	// classification is questioned.
	Notes string
	// Critical marks indexes that must be present before the API
	// can serve traffic. Critical indexes are rebuilt first so
	// that the node can accept queries while the remaining lazy
	// indexes finish in the background.
	//
	// Criteria for Critical=true:
	//   - Any WHERE predicate on the index column used by a live
	//     API query path (blockfrost, utxorpc, ledger queries).
	//   - Any WHERE predicate used by the rollback path
	//     (DeleteXAfterSlot), since rollbacks can occur as soon
	//     as live sync resumes.
	//
	// Everything else is lazy: FK reverse-lookups,
	// witness/redeemer secondary indexes, and any column that is
	// only SELECTed or SET but never filtered.
	Critical bool
}

Index is one entry in the deferred-index manifest. Each entry names an index that is safe to drop while the database is in bulk-load mode (Mithril sync ledger-state import, immutable blob load, API-mode historical metadata backfill) and rebuild before the database is marked ready.

The manifest deliberately excludes:

  • Primary keys (autoincrement IDs).
  • Unique indexes that back ON CONFLICT clauses used during import (e.g. utxo.tx_id_output_idx, transaction.hash, asset.idx_asset_unique, datum.hash, script.hash, certs.uniq_tx_cert).
  • Indexes on resume-checkpoint tables (import_checkpoint.import_key, backfill_checkpoint.phase).
  • The utxo (tx_id, output_idx) lookup index, required to resolve transaction inputs during backfill UTxO spending.
  • Cross-row uniqueness constraints used by ledger-state import (pool_stake_snapshot, reward_snapshot, reward_pool_input, network_state, account.staking_key, drep.credential, etc.).

Adding a new index to a metadata GORM model? Decide on bulk-load behavior at the same time:

  1. Does any import path (ledger-state import, immutable blob load, backfill block replay) rely on the index for an ON CONFLICT target, FK enforcement, or constraint lookup? If yes, leave it out of the manifest.
  2. Does the index only serve API/query/rollback paths that do not run during Mithril sync? If yes, add it here.
  3. Composite indexes share state with their constituent columns. If a field has both a deferrable single-column query index and a protected composite unique index, give the single-column index an explicit name and list that name here instead of the field.

See deferred_test.go for the regression test that walks every model field and asserts the classification.

func CriticalManifest added in v0.51.0

func CriticalManifest() []Index

CriticalManifest returns the subset of Manifest entries that are marked Critical=true. These are the indexes that must be present before the API can serve traffic.

func (Index) ResolvedName

func (i Index) ResolvedName() string

ResolvedName returns the GORM-visible name for the index — the literal Name when set, otherwise the Field which GORM resolves to the auto-generated name through the struct's schema.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL