Skip to content

Commit

Permalink
Replace AsLockless with Config.NoDatabaseLock
Browse files Browse the repository at this point in the history
  • Loading branch information
cristaloleg committed Sep 25, 2022
1 parent 6d16391 commit b3347c3
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 44 deletions.
14 changes: 7 additions & 7 deletions GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ Credits goes to [Postgres.ai](https://postgres.ai/) mentioning this feature at c

## Do not take database locks

If for some reason you don't want or you can't take lock on database (why???) there is `AsLocklessMigrator` function to achieve this:
If for some reason you don't want or you can't take lock on database there is `Config.NoDatabaseLock` field to achieve this:

```go
// let's take Postgres for example
m := dbump_pg.NewMigrator(...)

// volia, now m is a migrator that will not take a lock
m = dbump.AsLocklessMigrator(m)
cfg := dbump.Config{
NoDatabaseLock: true,
// set other fields
}

// pass m in config param as before
dbump.Run(...)
dbump.Run(context.Background(), cfg)
```

However, lock prevents from running few migrators at once, possible creating bad situations that's is hard to fix.

Also, not all migrators supports locks.

Also, not all migrators support locks.
67 changes: 33 additions & 34 deletions dbump.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ type Config struct {
// Only Migrator.DoStep method will be bounded with this timeout.
Timeout time.Duration

// NoDatabaseLock set to true will run migrations without taking a database lock.
// Default is false.
NoDatabaseLock bool

// DisableTx will run every migration not in a transaction.
// This completely depends on a specific Migrator implementation
// because not every database supports transaction, so this option can be no-op all the time.
Expand Down Expand Up @@ -118,11 +122,6 @@ const (
modeMaxPossible
)

// AsLocklessMigrator makes given migrator to not take a lock on database.
func AsLocklessMigrator(m Migrator) Migrator {
return &locklessMigrator{m}
}

// Run the Migrator with migration queries provided by the Loader.
func Run(ctx context.Context, config Config) error {
switch {
Expand Down Expand Up @@ -191,20 +190,12 @@ func (m *mig) load() ([]*Migration, error) {
}

func (m *mig) runMigrations(ctx context.Context, ms []*Migration) (err error) {
if err := m.LockDB(ctx); err != nil {
if !m.UseForce {
return fmt.Errorf("lock db: %w", err)
}
if err := m.UnlockDB(ctx); err != nil {
return fmt.Errorf("force unlock db: %w", err)
}
if err := m.LockDB(ctx); err != nil {
return fmt.Errorf("force lock db: %w", err)
}
if err := m.lockDB(ctx); err != nil {
return err
}

defer func() {
if errUnlock := m.UnlockDB(ctx); err == nil && errUnlock != nil {
if errUnlock := m.unlockDB(ctx); err == nil && errUnlock != nil {
err = fmt.Errorf("unlock db: %w", errUnlock)
}
}()
Expand All @@ -221,6 +212,32 @@ func (m *mig) runMigrations(ctx context.Context, ms []*Migration) (err error) {
return err
}

func (m *mig) lockDB(ctx context.Context) error {
if m.Config.NoDatabaseLock {
return nil
}

if err := m.LockDB(ctx); err != nil {
if !m.UseForce {
return fmt.Errorf("lock db: %w", err)
}
if err := m.UnlockDB(ctx); err != nil {
return fmt.Errorf("force unlock db: %w", err)
}
if err := m.LockDB(ctx); err != nil {
return fmt.Errorf("force lock db: %w", err)
}
}
return nil
}

func (m *mig) unlockDB(ctx context.Context) error {
if m.Config.NoDatabaseLock {
return nil
}
return m.UnlockDB(ctx)
}

func (m *mig) runMigrationsLocked(ctx context.Context, ms []*Migration) error {
curr, target, err := m.getCurrAndTargetVersions(ctx, len(ms))
if err != nil {
Expand Down Expand Up @@ -343,21 +360,3 @@ func (m *Migration) toStep(up, disableTx bool) Step {
DisableTx: disableTx,
}
}

type locklessMigrator struct {
m Migrator
}

func (llm *locklessMigrator) LockDB(ctx context.Context) error { return nil }
func (llm *locklessMigrator) UnlockDB(ctx context.Context) error { return nil }

func (llm *locklessMigrator) Init(ctx context.Context) error { return llm.m.Init(ctx) }
func (llm *locklessMigrator) Drop(ctx context.Context) error { return llm.m.Drop(ctx) }

func (llm *locklessMigrator) Version(ctx context.Context) (version int, err error) {
return llm.m.Version(ctx)
}

func (llm *locklessMigrator) DoStep(ctx context.Context, step Step) error {
return llm.m.DoStep(ctx, step)
}
7 changes: 4 additions & 3 deletions dbump_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,10 @@ func TestLockless(t *testing.T) {

mm := &tests.MockMigrator{}
cfg := dbump.Config{
Migrator: dbump.AsLocklessMigrator(mm),
Loader: dbump.NewSliceLoader(testdataMigrations),
Mode: dbump.ModeApplyAll,
Migrator: mm,
Loader: dbump.NewSliceLoader(testdataMigrations),
Mode: dbump.ModeApplyAll,
NoDatabaseLock: true,
}

failIfErr(t, dbump.Run(context.Background(), cfg))
Expand Down

0 comments on commit b3347c3

Please sign in to comment.