Skip to content

Commit

Permalink
1.update goose 2.use that implement goose provider for evm migration …
Browse files Browse the repository at this point in the history
…for isolation and caching 3.hack evm relayer to get a db and migrate it using 2
  • Loading branch information
krehermann committed Jun 19, 2024
1 parent a09bea7 commit ae0556b
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 430 deletions.
13 changes: 7 additions & 6 deletions core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ require (
cosmossdk.io/depinject v1.0.0-alpha.3 // indirect
cosmossdk.io/errors v1.0.0 // indirect
cosmossdk.io/math v1.0.1 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
Expand Down Expand Up @@ -233,6 +233,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
Expand All @@ -255,7 +256,7 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/pressly/goose/v3 v3.16.0 // indirect
github.com/pressly/goose/v3 v3.21.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
Expand Down Expand Up @@ -331,16 +332,16 @@ require (
go.uber.org/zap v1.26.0 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/oauth2 v0.17.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.18.0 // indirect
golang.org/x/tools v0.19.0 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect
Expand Down
115 changes: 34 additions & 81 deletions core/scripts/go.sum

Large diffs are not rendered by default.

22 changes: 18 additions & 4 deletions core/services/relay/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
pkgerrors "github.com/pkg/errors"
"golang.org/x/exp/maps"

Expand All @@ -33,6 +34,7 @@ import (
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big"
"github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm"
"github.com/smartcontractkit/chainlink/v2/core/config/env"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore"
"github.com/smartcontractkit/chainlink/v2/core/services/llo"
Expand All @@ -48,6 +50,7 @@ import (
reportcodecv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types"
"github.com/smartcontractkit/chainlink/v2/core/store/dialects"
evmdb "github.com/smartcontractkit/chainlink/v2/core/store/migrate/plugins/relayer/evm"
)

Expand Down Expand Up @@ -133,20 +136,31 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R
}
lggr = lggr.Named("Relayer")
// run the migrations for the relayer
// TODO: this is a hack. migration require a sql.DB because that's what goose uses
err = evmdb.Migrate(context.Background(), opts.DB, evmdb.Cfg{
// TODO: need a dburl in options. this is a hack. migration require a sql.DB because that's what goose uses
dburl := string(env.DatabaseURL)
if dburl == "" {
return nil, errors.New("missing DatabaseURL")
}
db, err := sql.Open(string(dialects.Postgres), dburl)
if err != nil {
return nil, fmt.Errorf("failed to open db: %w", err)
}

err = evmdb.Migrate(context.Background(), db, evmdb.Cfg{
Schema: "evm_" + chain.ID().String(),
ChainID: ubig.New(chain.ID()),
})

if err != nil {
return nil, fmt.Errorf("failed to migrate evm relayer for chain %s: %w", chain.ID().String(), err)
}

dbx := sqlx.NewDb(db, string(dialects.Postgres))
ds := sqlutil.WrapDataSource(dbx, lggr)
mercuryORM := mercury.NewORM(opts.DS)
lloORM := llo.NewORM(opts.DS, chain.ID())
cdcFactory := llo.NewChannelDefinitionCacheFactory(lggr, lloORM, chain.LogPoller())
relayer := &Relayer{
ds: opts.DS,
ds: ds,
chain: chain,
lggr: lggr,
ks: opts.CSAETHKeystore,
Expand Down
28 changes: 28 additions & 0 deletions core/store/migrate/plugins/relayer/evm/0002_initial.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func resolveDown(out io.Writer, val Cfg) error {
}

// Register0002 registers the migration with goose
/*
func Register0002(val Cfg) error {
upSQL := &bytes.Buffer{}
err := resolveUp(upSQL, val)
Expand All @@ -52,3 +53,30 @@ func Register0002(val Cfg) error {
goose.AddMigrationContext(upFunc, downFunc)
return nil
}
*/

func generate0002(val Cfg) (up *goose.GoFunc, down *goose.GoFunc, err error) {
upSQL := &bytes.Buffer{}
err = resolveUp(upSQL, val)
if err != nil {
return nil, nil, fmt.Errorf("failed to resolve up sql: %w", err)
}
upFunc := func(ctx context.Context, tx *sql.Tx) error {
_, terr := tx.ExecContext(ctx, upSQL.String())
return terr
}

downSQL := &bytes.Buffer{}
err = resolveDown(downSQL, val)
if err != nil {
return nil, nil, fmt.Errorf("failed to resolve down sql: %w", err)
}
downFunc := func(ctx context.Context, tx *sql.Tx) error {
_, terr := tx.ExecContext(ctx, downSQL.String())
return terr
}
up = &goose.GoFunc{RunTx: upFunc}
down = &goose.GoFunc{RunTx: downFunc}
//P goose.AddMigrationContext(upFunc, downFunc)
return up, down, nil
}
158 changes: 104 additions & 54 deletions core/store/migrate/plugins/relayer/evm/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,106 +6,156 @@ import (
"fmt"
"os"
"path/filepath"
"sync"

"github.com/pressly/goose/v3"
"github.com/pressly/goose/v3/database"
"gopkg.in/guregu/null.v4"
)

func setupPluginMigrations(cfg Cfg) error {
// reset the base fs and the global migrations
goose.SetBaseFS(nil) // we don't want to use the base fs for plugin migrations because the embedded fs contains templates, not sql files
goose.ResetGlobalMigrations()
goose.SetTableName(fmt.Sprintf("goose_migration_relayer_%s_%s", cfg.Schema, cfg.ChainID.String()))
err := Register0002(cfg)
// hacking, trying to make a provider instead of using global state
var mu sync.Mutex
var providerCache = make(map[string]*goose.Provider)

func newProvider(db *sql.DB, cfg Cfg) (*goose.Provider, error) {
mTable := fmt.Sprintf("goose_migration_evmrelayer_%s_%s", cfg.Schema, cfg.ChainID.String())

mu.Lock()
defer mu.Unlock()
if p, ok := providerCache[mTable]; ok {
return p, nil
}

store, err := database.NewStore(goose.DialectPostgres, mTable)
if err != nil {
return fmt.Errorf("failed to register migration 0002: %w", err)
return nil, fmt.Errorf("failed to create goose store for table %s: %w", mTable, err)
}
return nil
}

// Migrate migrates a subsystem of the chainlink database.
// It generates migrations based on the template for the subsystem and applies them to the database.
func Migrate(ctx context.Context, db *sql.DB, cfg Cfg) error {
tmpDir, err := os.MkdirTemp("", cfg.Schema)
goMigrations := make([]*goose.Migration, 0)
up0002, down0002, err := generate0002(cfg)
if err != nil {
return fmt.Errorf("failed to create temp dir: %w", err)
return nil, fmt.Errorf("failed to generate migration 0002 for cfg %v: %w", cfg, err)
}
defer os.RemoveAll(tmpDir)
goMigrations = append(goMigrations, goose.NewGoMigration(2, up0002, down0002))

err = setupPluginMigrations(cfg)
// note we are leaking here, but can't delete the temp dir until the migrations are actually executed
// maybe update the cache to store the temp dir and delete it when cache is deleted
tmpDir, err := os.MkdirTemp("", cfg.Schema)
if err != nil {
return fmt.Errorf("failed to setup plugin migrations: %w", err)
return nil, fmt.Errorf("failed to create temp dir: %w", err)
}

d := filepath.Join(tmpDir, cfg.Schema, cfg.ChainID.String())
err = os.MkdirAll(d, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create directory %s: %w", d, err)
return nil, fmt.Errorf("failed to create migration directory %s: %w", d, err)
}
migrations, err := generateMigrations(embeddedTmplFS, MigrationRootDir, d, cfg)
if err != nil {
return fmt.Errorf("failed to generate migrations for opt %v: %w", cfg, err)
return nil, fmt.Errorf("failed to generate migrations for opt %v: %w", cfg, err)
}
fmt.Printf("Generated migrations: %v\n", migrations)
fsys := os.DirFS(d)

err = goose.Up(db, d)
// hack to avoid global state. the goose lib doesn't allow to disable the global registry
// and also pass custom go migrations (wtf the point of disabling the global registry then?)
// https://github.com/pressly/goose/blob/3ad137847a4c242f09e425a12c15c7c7961d4b0f/provider.go#L119
goose.ResetGlobalMigrations()
p, err := goose.NewProvider(
"",
db, fsys,
goose.WithStore(store),
goose.WithDisableGlobalRegistry(true), // until/if we refactor the core migrations to use goose provider
goose.WithGoMigrations(goMigrations...))
if err != nil {
return fmt.Errorf("failed to do database migration: %w", err)
return nil, fmt.Errorf("failed to create goose provider: %w", err)
}
providerCache[mTable] = p
return p, nil
}

/*
func setupPluginMigrations(cfg Cfg) error {
// reset the base fs and the global migrations
goose.SetBaseFS(nil) // we don't want to use the base fs for plugin migrations because the embedded fs contains templates, not sql files
goose.ResetGlobalMigrations()
goose.SetTableName(fmt.Sprintf("goose_migration_relayer_%s_%s", cfg.Schema, cfg.ChainID.String()))
err := Register0002(cfg)
if err != nil {
return fmt.Errorf("failed to register migration 0002: %w", err)
}
return nil
}

func Rollback(ctx context.Context, db *sql.DB, version null.Int, cfg Cfg) error {
tmpDir, err := os.MkdirTemp("", cfg.Schema)
*/
// Migrate migrates a subsystem of the chainlink database.
// It generates migrations based on the template for the subsystem and applies them to the database.
func Migrate(ctx context.Context, db *sql.DB, cfg Cfg) error {
p, err := newProvider(db, cfg)
if err != nil {
return fmt.Errorf("failed to create temp dir: %w", err)
return fmt.Errorf("failed to create goose provider: %w", err)
}
if todo, _ := p.HasPending(ctx); !todo {
return nil
}
defer os.RemoveAll(tmpDir)
/*
tmpDir, err := os.MkdirTemp("", cfg.Schema)
if err != nil {
return fmt.Errorf("failed to create temp dir: %w", err)
}
defer os.RemoveAll(tmpDir)
err = setupPluginMigrations(cfg)
d := filepath.Join(tmpDir, cfg.Schema, cfg.ChainID.String())
err = os.MkdirAll(d, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create directory %s: %w", d, err)
}
migrations, err := generateMigrations(embeddedTmplFS, MigrationRootDir, d, cfg)
if err != nil {
return fmt.Errorf("failed to generate migrations for opt %v: %w", cfg, err)
}
fmt.Printf("Generated migrations: %v\n", migrations)
*/
// seems to be upside about global go migrations?
//goose.ResetGlobalMigrations()
r, err := p.Up(ctx)
if err != nil {
return fmt.Errorf("failed to setup plugin migrations: %w", err)
return fmt.Errorf("failed to do database migration: %w", err)
}
// TODO: should these be saved somewhere? if so where, if not if the db itself?)
d := filepath.Join(tmpDir, cfg.Schema, cfg.ChainID.String())
err = os.MkdirAll(d, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create directory %s: %w", d, err)
// todo: logger
for _, m := range r {
fmt.Println(m)
}
migrations, err := generateMigrations(embeddedTmplFS, MigrationRootDir, d, cfg)
return nil
}

func Rollback(ctx context.Context, db *sql.DB, version null.Int, cfg Cfg) error {
p, err := newProvider(db, cfg)
if err != nil {
return fmt.Errorf("failed to generate migrations for opt %v: %w", cfg, err)
return fmt.Errorf("failed to create goose provider: %w", err)
}

fmt.Printf("Generated migrations: %v\n", migrations)

if version.Valid {
return goose.DownTo(db, d, version.Int64)
_, err = p.DownTo(ctx, version.Int64)
} else {
_, err = p.Down(ctx)
}

return goose.Down(db, d)
return err
}

func Current(ctx context.Context, db *sql.DB, cfg Cfg) (int64, error) {
err := setupPluginMigrations(cfg)
p, err := newProvider(db, cfg)
if err != nil {
return -1, fmt.Errorf("failed to setup plugin migrations: %w", err)
return -1, fmt.Errorf("failed to create goose provider: %w", err)
}
// set the base fs only for status so that the templates are listed
// an alternative would be to somehow keep track of the erated sql files, but that would be more complex
// and error prone WRT to restarts
goose.SetBaseFS(embeddedTmplFS)
return goose.EnsureDBVersion(db)
return p.GetDBVersion(ctx)

}

Check failure on line 152 in core/store/migrate/plugins/relayer/evm/migrate.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary trailing newline (whitespace)

func Status(ctx context.Context, db *sql.DB, cfg Cfg) error {
err := setupPluginMigrations(cfg)
p, err := newProvider(db, cfg)
if err != nil {
return fmt.Errorf("failed to setup plugin migrations: %w", err)
return fmt.Errorf("failed to create goose provider: %w", err)
}
// set the base fs only for status so that the templates are listed
// an alternative would be to somehow keep track of the erated sql files, but that would be more complex
// and error prone WRT to restarts
goose.SetBaseFS(embeddedTmplFS)
return goose.Status(db, MigrationRootDir)
_, err = p.Status(ctx)
return err
}
4 changes: 0 additions & 4 deletions core/store/migrate/plugins/relayer/evm/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ type Cfg struct {
ChainID *big.Big
}

func RegisterSchemaMigration(val Cfg) error {
return Register0002(val)
}

var migrationSuffix = ".tmpl.sql"

func resolve(out io.Writer, in string, val Cfg) error {
Expand Down
Loading

0 comments on commit ae0556b

Please sign in to comment.