github.com/nokia/migrate/v4@v4.16.0/migrate.go (about) 1 // Package migrate reads migrations from sources and runs them against databases. 2 // Sources are defined by the `source.Driver` and databases by the `database.Driver` 3 // interface. The driver interfaces are kept "dumb", all migration logic is kept 4 // in this package. 5 package migrate 6 7 import ( 8 "errors" 9 "fmt" 10 "os" 11 "path/filepath" 12 "sync" 13 "time" 14 15 "github.com/hashicorp/go-multierror" 16 17 "github.com/nokia/migrate/v4/database" 18 iurl "github.com/nokia/migrate/v4/internal/url" 19 "github.com/nokia/migrate/v4/source" 20 ) 21 22 // DefaultPrefetchMigrations sets the number of migrations to pre-read 23 // from the source. This is helpful if the source is remote, but has little 24 // effect for a local source (i.e. file system). 25 // Please note that this setting has a major impact on the memory usage, 26 // since each pre-read migration is buffered in memory. See DefaultBufferSize. 27 var DefaultPrefetchMigrations = uint(10) 28 29 // DefaultLockTimeout sets the max time a database driver has to acquire a lock. 30 var DefaultLockTimeout = 15 * time.Second 31 32 var ( 33 ErrNoChange = errors.New("no change") 34 ErrNilVersion = errors.New("no migration") 35 ErrInvalidVersion = errors.New("version must be >= -1") 36 ErrLocked = errors.New("database locked") 37 ErrLockTimeout = errors.New("timeout: can't acquire database lock") 38 ) 39 40 // ErrShortLimit is an error returned when not enough migrations 41 // can be returned by a source for a given limit. 42 type ErrShortLimit struct { 43 Short uint 44 } 45 46 // Error implements the error interface. 47 func (e ErrShortLimit) Error() string { 48 return fmt.Sprintf("limit %v short", e.Short) 49 } 50 51 type ErrDirty struct { 52 Version int 53 } 54 55 func (e ErrDirty) Error() string { 56 return fmt.Sprintf("Dirty database version %v. Fix and force version.", e.Version) 57 } 58 59 type Migrate struct { 60 sourceName string 61 sourceDrv source.Driver 62 databaseName string 63 databaseDrv database.Driver 64 65 // Log accepts a Logger interface 66 Log Logger 67 68 // GracefulStop accepts `true` and will stop executing migrations 69 // as soon as possible at a safe break point, so that the database 70 // is not corrupted. 71 GracefulStop chan bool 72 isLockedMu *sync.Mutex 73 74 isGracefulStop bool 75 isLocked bool 76 77 // PrefetchMigrations defaults to DefaultPrefetchMigrations, 78 // but can be set per Migrate instance. 79 PrefetchMigrations uint 80 81 // LockTimeout defaults to DefaultLockTimeout, 82 // but can be set per Migrate instance. 83 LockTimeout time.Duration 84 85 // Current application release 86 AppReleaseStr string 87 } 88 89 // New returns a new Migrate instance from a source URL and a database URL. 90 // The URL scheme is defined by each driver. 91 func New(sourceURL, databaseURL string) (*Migrate, error) { 92 m := newCommon() 93 94 sourceName, err := iurl.SchemeFromURL(sourceURL) 95 if err != nil { 96 return nil, err 97 } 98 m.sourceName = sourceName 99 100 databaseName, err := iurl.SchemeFromURL(databaseURL) 101 if err != nil { 102 return nil, err 103 } 104 m.databaseName = databaseName 105 106 sourceDrv, err := source.Open(sourceURL) 107 if err != nil { 108 return nil, err 109 } 110 m.sourceDrv = sourceDrv 111 112 databaseDrv, err := database.Open(databaseURL) 113 if err != nil { 114 return nil, err 115 } 116 m.databaseDrv = databaseDrv 117 118 return m, nil 119 } 120 121 // NewWithDatabaseInstance returns a new Migrate instance from a source URL 122 // and an existing database instance. The source URL scheme is defined by each driver. 123 // Use any string that can serve as an identifier during logging as databaseName. 124 // You are responsible for closing the underlying database client if necessary. 125 func NewWithDatabaseInstance(sourceURL string, databaseName string, databaseInstance database.Driver) (*Migrate, error) { 126 m := newCommon() 127 128 sourceName, err := iurl.SchemeFromURL(sourceURL) 129 if err != nil { 130 return nil, err 131 } 132 m.sourceName = sourceName 133 134 m.databaseName = databaseName 135 136 sourceDrv, err := source.Open(sourceURL) 137 if err != nil { 138 return nil, err 139 } 140 m.sourceDrv = sourceDrv 141 142 m.databaseDrv = databaseInstance 143 144 return m, nil 145 } 146 147 // NewWithSourceInstance returns a new Migrate instance from an existing source instance 148 // and a database URL. The database URL scheme is defined by each driver. 149 // Use any string that can serve as an identifier during logging as sourceName. 150 // You are responsible for closing the underlying source client if necessary. 151 func NewWithSourceInstance(sourceName string, sourceInstance source.Driver, databaseURL string) (*Migrate, error) { 152 m := newCommon() 153 154 databaseName, err := iurl.SchemeFromURL(databaseURL) 155 if err != nil { 156 return nil, err 157 } 158 m.databaseName = databaseName 159 160 m.sourceName = sourceName 161 162 databaseDrv, err := database.Open(databaseURL) 163 if err != nil { 164 return nil, err 165 } 166 m.databaseDrv = databaseDrv 167 168 m.sourceDrv = sourceInstance 169 170 return m, nil 171 } 172 173 // NewWithInstance returns a new Migrate instance from an existing source and 174 // database instance. Use any string that can serve as an identifier during logging 175 // as sourceName and databaseName. You are responsible for closing down 176 // the underlying source and database client if necessary. 177 func NewWithInstance(sourceName string, sourceInstance source.Driver, databaseName string, databaseInstance database.Driver) (*Migrate, error) { 178 m := newCommon() 179 180 m.sourceName = sourceName 181 m.databaseName = databaseName 182 183 m.sourceDrv = sourceInstance 184 m.databaseDrv = databaseInstance 185 186 return m, nil 187 } 188 189 func newCommon() *Migrate { 190 return &Migrate{ 191 GracefulStop: make(chan bool, 1), 192 PrefetchMigrations: DefaultPrefetchMigrations, 193 LockTimeout: DefaultLockTimeout, 194 isLockedMu: &sync.Mutex{}, 195 } 196 } 197 198 // Close closes the source and the database. 199 func (m *Migrate) Close() (source error, database error) { 200 databaseSrvClose := make(chan error) 201 sourceSrvClose := make(chan error) 202 203 m.logVerbosePrintf("Closing source and database\n") 204 205 go func() { 206 databaseSrvClose <- m.databaseDrv.Close() 207 }() 208 209 go func() { 210 sourceSrvClose <- m.sourceDrv.Close() 211 }() 212 213 return <-sourceSrvClose, <-databaseSrvClose 214 } 215 216 // Migrate looks at the currently active migration version, 217 // then migrates either up or down to the specified version. 218 func (m *Migrate) Migrate(version uint) error { 219 if err := m.lock(); err != nil { 220 return err 221 } 222 223 curVersion, dirty, err := m.databaseDrv.Version() 224 if err != nil { 225 return m.unlockErr(err) 226 } 227 228 if dirty { 229 return m.unlockErr(ErrDirty{curVersion}) 230 } 231 232 ret := make(chan interface{}, m.PrefetchMigrations) 233 go m.read(curVersion, int(version), ret) 234 235 return m.unlockErr(m.runMigrations(ret)) 236 } 237 238 // Steps looks at the currently active migration version. 239 // It will migrate up if n > 0, and down if n < 0. 240 func (m *Migrate) Steps(n int) error { 241 if n == 0 { 242 return ErrNoChange 243 } 244 245 if err := m.lock(); err != nil { 246 return err 247 } 248 249 curVersion, dirty, err := m.databaseDrv.Version() 250 if err != nil { 251 return m.unlockErr(err) 252 } 253 254 if dirty { 255 return m.unlockErr(ErrDirty{curVersion}) 256 } 257 258 ret := make(chan interface{}, m.PrefetchMigrations) 259 260 if n > 0 { 261 go m.readUp(curVersion, n, ret) 262 } else { 263 go m.readDown(curVersion, -n, ret) 264 } 265 266 return m.unlockErr(m.runMigrations(ret)) 267 } 268 269 // Up looks at the currently active migration version 270 // and will migrate all the way up (applying all up migrations). 271 func (m *Migrate) Up() error { 272 if err := m.lock(); err != nil { 273 return err 274 } 275 276 curVersion, dirty, err := m.databaseDrv.Version() 277 if err != nil { 278 return m.unlockErr(err) 279 } 280 281 if curVersion >= 0 { 282 if dirty { 283 m.sourceDrv.MarkSkipMigrations(uint(curVersion-1), source.Up) 284 } else { 285 m.sourceDrv.MarkSkipMigrations(uint(curVersion), source.Up) 286 } 287 } 288 289 if dirty { 290 return m.unlockErr(ErrDirty{curVersion}) 291 } 292 293 ret := make(chan interface{}, m.PrefetchMigrations) 294 go m.readUp(curVersion, -1, ret) 295 m.sourceDrv.PrintSummary(source.Up) 296 err = m.runMigrations(ret) 297 m.sourceDrv.PrintSummary(source.Up) 298 return m.unlockErr(err) 299 } 300 301 // Down looks at the currently active migration version 302 // and will migrate all the way down (applying all down migrations). 303 func (m *Migrate) Down() error { 304 if err := m.lock(); err != nil { 305 return err 306 } 307 308 curVersion, dirty, err := m.databaseDrv.Version() 309 if err != nil { 310 return m.unlockErr(err) 311 } 312 313 if dirty { 314 return m.unlockErr(ErrDirty{curVersion}) 315 } 316 317 ret := make(chan interface{}, m.PrefetchMigrations) 318 go m.readDown(curVersion, -1, ret) 319 m.sourceDrv.PrintSummary(source.Down) 320 err = m.runMigrations(ret) 321 m.sourceDrv.PrintSummary(source.Down) 322 return m.unlockErr(err) 323 } 324 325 // Drop deletes everything in the database. 326 func (m *Migrate) Drop() error { 327 if err := m.lock(); err != nil { 328 return err 329 } 330 if err := m.databaseDrv.Drop(); err != nil { 331 return m.unlockErr(err) 332 } 333 return m.unlock() 334 } 335 336 // Run runs any migration provided by you against the database. 337 // It does not check any currently active version in database. 338 // Usually you don't need this function at all. Use Migrate, 339 // Steps, Up or Down instead. 340 func (m *Migrate) Run(migration ...*Migration) error { 341 if len(migration) == 0 { 342 return ErrNoChange 343 } 344 345 if err := m.lock(); err != nil { 346 return err 347 } 348 349 curVersion, dirty, err := m.databaseDrv.Version() 350 if err != nil { 351 return m.unlockErr(err) 352 } 353 354 if dirty { 355 return m.unlockErr(ErrDirty{curVersion}) 356 } 357 358 ret := make(chan interface{}, m.PrefetchMigrations) 359 360 go func() { 361 defer close(ret) 362 for _, migr := range migration { 363 if m.PrefetchMigrations > 0 && migr.Body != nil { 364 m.logVerbosePrintf("Start buffering %v\n", migr.LogString()) 365 } else { 366 m.logVerbosePrintf("Scheduled %v\n", migr.LogString()) 367 } 368 369 ret <- migr 370 go func(migr *Migration) { 371 if err := migr.Buffer(); err != nil { 372 m.logErr(err) 373 } 374 }(migr) 375 } 376 }() 377 378 return m.unlockErr(m.runMigrations(ret)) 379 } 380 381 // Force sets a migration version. 382 // It does not check any currently active version in database. 383 // It resets the dirty state to false. 384 func (m *Migrate) Force(version int) error { 385 if version < -1 { 386 return ErrInvalidVersion 387 } 388 389 if err := m.lock(); err != nil { 390 return err 391 } 392 393 if err := m.databaseDrv.SetVersion(version, false); err != nil { 394 return m.unlockErr(err) 395 } 396 397 return m.unlock() 398 } 399 400 // Version returns the currently active migration version. 401 // If no migration has been applied, yet, it will return ErrNilVersion. 402 func (m *Migrate) Version() (version uint, dirty bool, err error) { 403 v, d, err := m.databaseDrv.Version() 404 if err != nil { 405 return 0, false, err 406 } 407 408 if v == database.NilVersion { 409 return 0, false, ErrNilVersion 410 } 411 412 return suint(v), d, nil 413 } 414 415 // read reads either up or down migrations from source `from` to `to`. 416 // Each migration is then written to the ret channel. 417 // If an error occurs during reading, that error is written to the ret channel, too. 418 // Once read is done reading it will close the ret channel. 419 func (m *Migrate) read(from int, to int, ret chan<- interface{}) { 420 defer close(ret) 421 422 // check if from version exists 423 if from >= 0 { 424 if err := m.versionExists(suint(from)); err != nil { 425 ret <- err 426 return 427 } 428 } 429 430 // check if to version exists 431 if to >= 0 { 432 if err := m.versionExists(suint(to)); err != nil { 433 ret <- err 434 return 435 } 436 } 437 438 // no change? 439 if from == to { 440 ret <- ErrNoChange 441 return 442 } 443 444 if from < to { 445 // it's going up 446 // apply first migration if from is nil version 447 if from == -1 { 448 firstVersion, err := m.sourceDrv.First() 449 if err != nil { 450 ret <- err 451 return 452 } 453 454 migr, err := m.newMigration(firstVersion, int(firstVersion)) 455 if err != nil { 456 ret <- err 457 return 458 } 459 460 ret <- migr 461 go func() { 462 if err := migr.Buffer(); err != nil { 463 m.logErr(err) 464 } 465 }() 466 467 from = int(firstVersion) 468 } 469 470 // run until we reach target ... 471 for from < to { 472 if m.stop() { 473 return 474 } 475 476 next, err := m.sourceDrv.Next(suint(from)) 477 if err != nil { 478 ret <- err 479 return 480 } 481 482 migr, err := m.newMigration(next, int(next)) 483 if err != nil { 484 ret <- err 485 return 486 } 487 488 ret <- migr 489 go func() { 490 if err := migr.Buffer(); err != nil { 491 m.logErr(err) 492 } 493 }() 494 495 from = int(next) 496 } 497 498 } else { 499 // it's going down 500 // run until we reach target ... 501 for from > to && from >= 0 { 502 if m.stop() { 503 return 504 } 505 506 prev, err := m.sourceDrv.Prev(suint(from)) 507 if errors.Is(err, os.ErrNotExist) && to == -1 { 508 // apply nil migration 509 migr, err := m.newMigration(suint(from), -1) 510 if err != nil { 511 ret <- err 512 return 513 } 514 ret <- migr 515 go func() { 516 if err := migr.Buffer(); err != nil { 517 m.logErr(err) 518 } 519 }() 520 521 return 522 523 } else if err != nil { 524 ret <- err 525 return 526 } 527 528 migr, err := m.newMigration(suint(from), int(prev)) 529 if err != nil { 530 ret <- err 531 return 532 } 533 534 ret <- migr 535 go func() { 536 if err := migr.Buffer(); err != nil { 537 m.logErr(err) 538 } 539 }() 540 541 from = int(prev) 542 } 543 } 544 } 545 546 // readUp reads up migrations from `from` limitted by `limit`. 547 // limit can be -1, implying no limit and reading until there are no more migrations. 548 // Each migration is then written to the ret channel. 549 // If an error occurs during reading, that error is written to the ret channel, too. 550 // Once readUp is done reading it will close the ret channel. 551 func (m *Migrate) readUp(from int, limit int, ret chan<- interface{}) { 552 defer close(ret) 553 554 // check if from version exists 555 if from >= 0 { 556 if err := m.versionExists(suint(from)); err != nil { 557 ret <- err 558 return 559 } 560 } 561 562 if limit == 0 { 563 ret <- ErrNoChange 564 return 565 } 566 567 count := 0 568 for count < limit || limit == -1 { 569 if m.stop() { 570 return 571 } 572 573 // apply first migration if from is nil version 574 if from == -1 { 575 firstVersion, err := m.sourceDrv.First() 576 if err != nil { 577 ret <- err 578 return 579 } 580 581 migr, err := m.newMigration(firstVersion, int(firstVersion)) 582 if err != nil { 583 ret <- err 584 return 585 } 586 587 ret <- migr 588 go func() { 589 if err := migr.Buffer(); err != nil { 590 m.logErr(err) 591 } 592 }() 593 from = int(firstVersion) 594 count++ 595 continue 596 } 597 598 // apply next migration 599 next, err := m.sourceDrv.Next(suint(from)) 600 if errors.Is(err, os.ErrNotExist) { 601 // no limit, but no migrations applied? 602 if limit == -1 && count == 0 { 603 ret <- ErrNoChange 604 return 605 } 606 607 // no limit, reached end 608 if limit == -1 { 609 return 610 } 611 612 // reached end, and didn't apply any migrations 613 if limit > 0 && count == 0 { 614 ret <- os.ErrNotExist 615 return 616 } 617 618 // applied less migrations than limit? 619 if count < limit { 620 ret <- ErrShortLimit{suint(limit - count)} 621 return 622 } 623 } 624 if err != nil { 625 ret <- err 626 return 627 } 628 629 migr, err := m.newMigration(next, int(next)) 630 if err != nil { 631 ret <- err 632 return 633 } 634 635 ret <- migr 636 go func() { 637 if err := migr.Buffer(); err != nil { 638 m.logErr(err) 639 } 640 }() 641 from = int(next) 642 count++ 643 } 644 } 645 646 // readDown reads down migrations from `from` limitted by `limit`. 647 // limit can be -1, implying no limit and reading until there are no more migrations. 648 // Each migration is then written to the ret channel. 649 // If an error occurs during reading, that error is written to the ret channel, too. 650 // Once readDown is done reading it will close the ret channel. 651 func (m *Migrate) readDown(from int, limit int, ret chan<- interface{}) { 652 defer close(ret) 653 654 // check if from version exists 655 if from >= 0 { 656 if err := m.versionExists(suint(from)); err != nil { 657 ret <- err 658 return 659 } 660 } 661 662 if limit == 0 { 663 ret <- ErrNoChange 664 return 665 } 666 667 // no change if already at nil version 668 if from == -1 && limit == -1 { 669 ret <- ErrNoChange 670 return 671 } 672 673 // can't go over limit if already at nil version 674 if from == -1 && limit > 0 { 675 ret <- os.ErrNotExist 676 return 677 } 678 679 count := 0 680 for count < limit || limit == -1 { 681 if m.stop() { 682 return 683 } 684 685 prev, err := m.sourceDrv.Prev(suint(from)) 686 if errors.Is(err, os.ErrNotExist) { 687 // no limit or haven't reached limit, apply "first" migration 688 if limit == -1 || limit-count > 0 { 689 firstVersion, err := m.sourceDrv.First() 690 if err != nil { 691 ret <- err 692 return 693 } 694 695 migr, err := m.newMigration(firstVersion, -1) 696 if err != nil { 697 ret <- err 698 return 699 } 700 ret <- migr 701 go func() { 702 if err := migr.Buffer(); err != nil { 703 m.logErr(err) 704 } 705 }() 706 count++ 707 } 708 709 if count < limit { 710 ret <- ErrShortLimit{suint(limit - count)} 711 } 712 return 713 } 714 if err != nil { 715 ret <- err 716 return 717 } 718 719 migr, err := m.newMigration(suint(from), int(prev)) 720 if err != nil { 721 ret <- err 722 return 723 } 724 725 ret <- migr 726 go func() { 727 if err := migr.Buffer(); err != nil { 728 m.logErr(err) 729 } 730 }() 731 from = int(prev) 732 count++ 733 } 734 } 735 736 // runMigrations reads *Migration and error from a channel. Any other type 737 // sent on this channel will result in a panic. Each migration is then 738 // proxied to the database driver and run against the database. 739 // Before running a newly received migration it will check if it's supposed 740 // to stop execution because it might have received a stop signal on the 741 // GracefulStop channel. 742 func (m *Migrate) runMigrations(ret <-chan interface{}) error { 743 for r := range ret { 744 745 if m.stop() { 746 return nil 747 } 748 749 switch r := r.(type) { 750 case error: 751 return r 752 753 case *Migration: 754 migr := r 755 756 // set version with dirty state 757 if err := m.databaseDrv.SetVersion(migr.TargetVersion, true); err != nil { 758 return err 759 } 760 761 if migr.Body != nil { 762 m.logVerbosePrintf("Read and execute %v\n", migr.LogString()) 763 if err := m.databaseDrv.Run(migr.BufferedBody); err != nil { 764 m.sourceDrv.UpdateStatus(migr.Version, source.Failed, err.Error()) 765 return err 766 } 767 } else if migr.MigrationFunc != nil { 768 m.logVerbosePrintf("Running Migration function %v\n", migr.LogString()) 769 if err := m.databaseDrv.RunFunctionMigration(migr.MigrationFunc); err != nil { 770 m.sourceDrv.UpdateStatus(migr.Version, source.Failed, err.Error()) 771 return err 772 } 773 } 774 775 // set clean state 776 if err := m.databaseDrv.SetVersion(migr.TargetVersion, false); err != nil { 777 return err 778 } 779 780 endTime := time.Now() 781 readTime := migr.FinishedReading.Sub(migr.StartedBuffering) 782 runTime := endTime.Sub(migr.FinishedReading) 783 784 // update status 785 if migr.Skipped { 786 m.sourceDrv.UpdateStatus(migr.Version, source.Skipped, "") 787 } else { 788 m.sourceDrv.UpdateStatus(migr.Version, source.Done, "") 789 } 790 // log either verbose or normal 791 if m.Log != nil { 792 if m.Log.Verbose() { 793 m.logPrintf("Finished %v (read %v, ran %v)\n", migr.LogString(), readTime, runTime) 794 } else { 795 m.logPrintf("%v (%v)\n", migr.LogString(), readTime+runTime) 796 } 797 } 798 799 default: 800 return fmt.Errorf("unknown type: %T with value: %+v", r, r) 801 } 802 } 803 return nil 804 } 805 806 // versionExists checks the source if either the up or down migration for 807 // the specified migration version exists. 808 func (m *Migrate) versionExists(version uint) (result error) { 809 // try up migration first 810 up, _, _, _, err := m.sourceDrv.ReadUp(version) 811 if err == nil { 812 defer func() { 813 if up != nil { 814 if errClose := up.Close(); errClose != nil { 815 result = multierror.Append(result, errClose) 816 } 817 } 818 }() 819 } 820 if errors.Is(err, os.ErrExist) { 821 return nil 822 } else if !errors.Is(err, os.ErrNotExist) { 823 return err 824 } 825 826 // then try down migration 827 down, _, _, _, err := m.sourceDrv.ReadDown(version) 828 if err == nil { 829 defer func() { 830 if down != nil { 831 if errClose := down.Close(); errClose != nil { 832 result = multierror.Append(result, errClose) 833 } 834 } 835 }() 836 } 837 if errors.Is(err, os.ErrExist) { 838 return nil 839 } else if !errors.Is(err, os.ErrNotExist) { 840 return err 841 } 842 843 err = fmt.Errorf("no migration found for version %d: %w", version, err) 844 m.logErr(err) 845 return err 846 } 847 848 // stop returns true if no more migrations should be run against the database 849 // because a stop signal was received on the GracefulStop channel. 850 // Calls are cheap and this function is not blocking. 851 func (m *Migrate) stop() bool { 852 if m.isGracefulStop { 853 return true 854 } 855 856 select { 857 case <-m.GracefulStop: 858 m.isGracefulStop = true 859 return true 860 861 default: 862 return false 863 } 864 } 865 866 // newMigration is a helper func that returns a *Migration for the 867 // specified version and targetVersion. 868 func (m *Migrate) newMigration(version uint, targetVersion int) (*Migration, error) { 869 var migr *Migration 870 871 if targetVersion >= int(version) { 872 r, identifier, loc, fn, err := m.sourceDrv.ReadUp(version) 873 // skip up migration based on current release 874 skipMgr := m.skipMigration(loc) 875 if errors.Is(err, os.ErrNotExist) { 876 // create "empty" migration 877 migr, err = NewMigration(nil, "", version, targetVersion) 878 if err != nil { 879 return nil, err 880 } 881 882 } else if skipMgr { 883 fmt.Printf("Skiping migration for :%v\n", loc) 884 migr = NewSkippedMigration("", version, targetVersion) 885 } else if err != nil { 886 return nil, err 887 } else if fn != nil { 888 // create migration with function 889 migr = NewFuncMigration(fn, identifier, version, targetVersion) 890 } else { 891 // create migration from up source 892 migr, err = NewMigration(r, identifier, version, targetVersion) 893 if err != nil { 894 return nil, err 895 } 896 } 897 898 } else { 899 r, identifier, _, fn, err := m.sourceDrv.ReadDown(version) 900 // lets not skip down migration based on release string. 901 if errors.Is(err, os.ErrNotExist) { 902 // create "empty" migration 903 migr, err = NewMigration(nil, "", version, targetVersion) 904 if err != nil { 905 return nil, err 906 } 907 908 } else if err != nil { 909 return nil, err 910 } else if fn != nil { 911 // create migration with function 912 migr = NewFuncMigration(fn, identifier, version, targetVersion) 913 } else { 914 // create migration from down source 915 migr, err = NewMigration(r, identifier, version, targetVersion) 916 if err != nil { 917 return nil, err 918 } 919 } 920 } 921 922 if m.PrefetchMigrations > 0 && migr.Body != nil { 923 m.logVerbosePrintf("Start buffering %v\n", migr.LogString()) 924 } else { 925 m.logVerbosePrintf("Scheduled %v\n", migr.LogString()) 926 } 927 928 return migr, nil 929 } 930 931 func (m *Migrate) skipMigration(location string) bool { 932 parentDir := filepath.Dir(location) 933 if parentDir != "." && parentDir < m.AppReleaseStr { 934 return true 935 } 936 return false 937 } 938 939 // lock is a thread safe helper function to lock the database. 940 // It should be called as late as possible when running migrations. 941 func (m *Migrate) lock() error { 942 m.isLockedMu.Lock() 943 defer m.isLockedMu.Unlock() 944 945 if m.isLocked { 946 return ErrLocked 947 } 948 949 // create done channel, used in the timeout goroutine 950 done := make(chan bool, 1) 951 defer func() { 952 done <- true 953 }() 954 955 // use errchan to signal error back to this context 956 errchan := make(chan error, 2) 957 958 // start timeout goroutine 959 timeout := time.After(m.LockTimeout) 960 go func() { 961 for { 962 select { 963 case <-done: 964 return 965 case <-timeout: 966 errchan <- ErrLockTimeout 967 return 968 } 969 } 970 }() 971 972 // now try to acquire the lock 973 go func() { 974 if err := m.databaseDrv.Lock(); err != nil { 975 errchan <- err 976 } else { 977 errchan <- nil 978 } 979 }() 980 981 // wait until we either receive ErrLockTimeout or error from Lock operation 982 err := <-errchan 983 if err == nil { 984 m.isLocked = true 985 } 986 return err 987 } 988 989 // unlock is a thread safe helper function to unlock the database. 990 // It should be called as early as possible when no more migrations are 991 // expected to be executed. 992 func (m *Migrate) unlock() error { 993 m.isLockedMu.Lock() 994 defer m.isLockedMu.Unlock() 995 996 if err := m.databaseDrv.Unlock(); err != nil { 997 // BUG: Can potentially create a deadlock. Add a timeout. 998 return err 999 } 1000 1001 m.isLocked = false 1002 return nil 1003 } 1004 1005 // unlockErr calls unlock and returns a combined error 1006 // if a prevErr is not nil. 1007 func (m *Migrate) unlockErr(prevErr error) error { 1008 if err := m.unlock(); err != nil { 1009 return multierror.Append(prevErr, err) 1010 } 1011 return prevErr 1012 } 1013 1014 // logPrintf writes to m.Log if not nil 1015 func (m *Migrate) logPrintf(format string, v ...interface{}) { 1016 if m.Log != nil { 1017 m.Log.Printf(format, v...) 1018 } 1019 } 1020 1021 // logVerbosePrintf writes to m.Log if not nil. Use for verbose logging output. 1022 func (m *Migrate) logVerbosePrintf(format string, v ...interface{}) { 1023 if m.Log != nil && m.Log.Verbose() { 1024 m.Log.Printf(format, v...) 1025 } 1026 } 1027 1028 // logErr writes error to m.Log if not nil 1029 func (m *Migrate) logErr(err error) { 1030 if m.Log != nil { 1031 m.Log.Printf("error: %v", err) 1032 } 1033 } 1034 1035 func (m *Migrate) GetDBDriver() database.Driver { 1036 return m.databaseDrv 1037 }