← All changes
|
src/Framework/Migrations/MigrationsRunner.php
+113
-92
4.16.5.1
→
4.18.0
View file →
| @@ -11,8 +11,9 @@ | ||
| 11 | 11 | use Give\Log\Log; |
| 12 | 12 | use Give\MigrationLog\MigrationLogFactory; |
| 13 | 13 | use Give\MigrationLog\MigrationLogRepository; |
| 14 | 14 | use Give\MigrationLog\MigrationLogStatus; |
| 15 | +use WP_Upgrader; | |
| 15 | 16 | |
| 16 | 17 | /** |
| 17 | 18 | * Class MigrationsRunner |
| 18 | 19 | * |
| @@ -20,8 +21,13 @@ | ||
| 20 | 21 | */ |
| 21 | 22 | class MigrationsRunner |
| 22 | 23 | { |
| 23 | 24 | /** |
| 25 | + * @since 4.18.0 | |
| 26 | + */ | |
| 27 | + const LOCK_NAME = 'give_migrations'; | |
| 28 | + | |
| 29 | + /** | |
| 24 | 30 | * List of completed migrations. |
| 25 | 31 | * |
| 26 | 32 | * @since 2.9.0 |
| 27 | 33 | * |
| @@ -69,8 +75,9 @@ | ||
| 69 | 75 | |
| 70 | 76 | /** |
| 71 | 77 | * Run database migrations. |
| 72 | 78 | * |
| 79 | + * @since 4.18.0 hold a lock so concurrent requests do not run the same migration twice | |
| 73 | 80 | * @since 4.0.0 add support for batch processing |
| 74 | 81 | * @since 2.9.0 |
| 75 | 82 | */ |
| 76 | 83 | public function run() |
| @@ -85,118 +92,132 @@ | ||
| 85 | 92 | )) { |
| 86 | 93 | return; |
| 87 | 94 | } |
| 88 | 95 | |
| 89 | - $migrations = $this->migrationRegister->getMigrations(); | |
| 96 | + if ( ! class_exists('WP_Upgrader')) { | |
| 97 | + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; | |
| 98 | + } | |
| 90 | 99 | |
| 91 | - foreach ($migrations as $migrationClass) { | |
| 92 | - $migrationId = $migrationClass::id(); | |
| 100 | + // Migrations run on every request, so without this a slow one (an ALTER TABLE over millions of | |
| 101 | + // rows) starts again in every request that arrives while it is still running. | |
| 102 | + if ( ! WP_Upgrader::create_lock(self::LOCK_NAME)) { | |
| 103 | + return; | |
| 104 | + } | |
| 93 | 105 | |
| 94 | - if (in_array($migrationId, $this->completedMigrations, true)) { | |
| 95 | - continue; | |
| 96 | - } | |
| 106 | + try { | |
| 107 | + $migrations = $this->migrationRegister->getMigrations(); | |
| 97 | 108 | |
| 98 | - $migrationLog = $this->migrationLogFactory->make($migrationId); | |
| 109 | + foreach ($migrations as $migrationClass) { | |
| 110 | + $migrationId = $migrationClass::id(); | |
| 99 | 111 | |
| 100 | - try { | |
| 101 | - /** | |
| 102 | - * @var Migration|BatchMigration $migration | |
| 103 | - */ | |
| 104 | - $migration = give($migrationClass); | |
| 112 | + if (in_array($migrationId, $this->completedMigrations, true)) { | |
| 113 | + continue; | |
| 114 | + } | |
| 105 | 115 | |
| 106 | - if ($migration instanceof BatchMigration) { | |
| 107 | - $status = (new BatchMigrationRunner($migration))->run(); | |
| 116 | + $migrationLog = $this->migrationLogFactory->make($migrationId); | |
| 108 | 117 | |
| 109 | - if ($status === MigrationLogStatus::RUNNING) { | |
| 110 | - give()->notices->register_notice( | |
| 111 | - [ | |
| 112 | - 'id' => $migrationId, | |
| 113 | - 'description' => esc_html__('GiveWP is running database updates in the background. You will be notified as soon as it completes.', | |
| 114 | - 'give'), | |
| 115 | - ] | |
| 116 | - ); | |
| 118 | + try { | |
| 119 | + /** | |
| 120 | + * @var Migration|BatchMigration $migration | |
| 121 | + */ | |
| 122 | + $migration = give($migrationClass); | |
| 117 | 123 | |
| 118 | - // Update status to RUNNING | |
| 119 | - if (MigrationLogStatus::RUNNING !== $migrationLog->getStatus()) { | |
| 120 | - $migrationLog->setStatus(MigrationLogStatus::RUNNING); | |
| 121 | - $migrationLog->save(); | |
| 124 | + if ($migration instanceof BatchMigration) { | |
| 125 | + $status = (new BatchMigrationRunner($migration))->run(); | |
| 126 | + | |
| 127 | + if ($status === MigrationLogStatus::RUNNING) { | |
| 128 | + give()->notices->register_notice( | |
| 129 | + [ | |
| 130 | + 'id' => $migrationId, | |
| 131 | + 'description' => esc_html__('GiveWP is running database updates in the background. You will be notified as soon as it completes.', | |
| 132 | + 'give'), | |
| 133 | + ] | |
| 134 | + ); | |
| 135 | + | |
| 136 | + // Update status to RUNNING | |
| 137 | + if (MigrationLogStatus::RUNNING !== $migrationLog->getStatus()) { | |
| 138 | + $migrationLog->setStatus(MigrationLogStatus::RUNNING); | |
| 139 | + $migrationLog->save(); | |
| 140 | + } | |
| 141 | + | |
| 142 | + break; | |
| 122 | 143 | } |
| 123 | 144 | |
| 124 | - break; | |
| 125 | - } | |
| 145 | + if ($status === MigrationLogStatus::INCOMPLETE) { | |
| 146 | + $listTableLink = sprintf( | |
| 147 | + '<a href="%s">%s</a>', | |
| 148 | + admin_url('edit.php?post_type=give_forms&page=give-tools&tab=data'), | |
| 149 | + esc_html__('Resume update', 'give') | |
| 150 | + ); | |
| 126 | 151 | |
| 127 | - if ($status === MigrationLogStatus::INCOMPLETE) { | |
| 128 | - $listTableLink = sprintf( | |
| 129 | - '<a href="%s">%s</a>', | |
| 130 | - admin_url('edit.php?post_type=give_forms&page=give-tools&tab=data'), | |
| 131 | - esc_html__('Resume update', 'give') | |
| 132 | - ); | |
| 152 | + give()->notices->register_notice( | |
| 153 | + [ | |
| 154 | + 'id' => $migrationId, | |
| 155 | + 'type' => 'warning', | |
| 156 | + 'description' => sprintf( | |
| 157 | + __('Incomplete database update: "%s". %s', 'give'), | |
| 158 | + $migration::title(), | |
| 159 | + $listTableLink | |
| 160 | + ), | |
| 161 | + ] | |
| 162 | + ); | |
| 163 | + } | |
| 133 | 164 | |
| 134 | - give()->notices->register_notice( | |
| 135 | - [ | |
| 136 | - 'id' => $migrationId, | |
| 137 | - 'type' => 'warning', | |
| 138 | - 'description' => sprintf( | |
| 139 | - __('Incomplete database update: "%s". %s', 'give'), | |
| 140 | - $migration::title(), | |
| 141 | - $listTableLink | |
| 142 | - ), | |
| 143 | - ] | |
| 144 | - ); | |
| 165 | + $migrationLog->setStatus($status); | |
| 166 | + } else { | |
| 167 | + $migration->run(); | |
| 168 | + $migrationLog->setStatus(MigrationLogStatus::SUCCESS); | |
| 145 | 169 | } |
| 170 | + } catch (Exception $exception) { | |
| 171 | + DB::rollback(); | |
| 172 | + $migrationLog | |
| 173 | + ->setStatus(MigrationLogStatus::FAILED) | |
| 174 | + ->setError([ | |
| 175 | + 'status' => __('Migration failed', 'give'), | |
| 176 | + 'error' => [ | |
| 177 | + 'message' => $exception->getMessage(), | |
| 178 | + 'code' => $exception->getCode(), | |
| 179 | + 'file' => $exception->getFile(), | |
| 180 | + 'line' => $exception->getLine(), | |
| 181 | + ], | |
| 182 | + ]); | |
| 146 | 183 | |
| 147 | - $migrationLog->setStatus($status); | |
| 148 | - } else { | |
| 149 | - $migration->run(); | |
| 150 | - $migrationLog->setStatus(MigrationLogStatus::SUCCESS); | |
| 184 | + give()->notices->register_notice( | |
| 185 | + [ | |
| 186 | + 'id' => 'migration-failure', | |
| 187 | + 'description' => sprintf( | |
| 188 | + '%1$s <a href="https://givewp.com/support/">https://givewp.com/support</a>', | |
| 189 | + esc_html__( | |
| 190 | + 'There was a problem running the migrations. Please reach out to GiveWP support for assistance:', | |
| 191 | + 'give' | |
| 192 | + ) | |
| 193 | + ), | |
| 194 | + ] | |
| 195 | + ); | |
| 151 | 196 | } |
| 152 | - } catch (Exception $exception) { | |
| 153 | - DB::rollback(); | |
| 154 | - $migrationLog | |
| 155 | - ->setStatus(MigrationLogStatus::FAILED) | |
| 156 | - ->setError([ | |
| 157 | - 'status' => __('Migration failed', 'give'), | |
| 158 | - 'error' => [ | |
| 159 | - 'message' => $exception->getMessage(), | |
| 160 | - 'code' => $exception->getCode(), | |
| 161 | - 'file' => $exception->getFile(), | |
| 162 | - 'line' => $exception->getLine(), | |
| 163 | - ], | |
| 164 | - ]); | |
| 165 | 197 | |
| 166 | - give()->notices->register_notice( | |
| 167 | - [ | |
| 168 | - 'id' => 'migration-failure', | |
| 169 | - 'description' => sprintf( | |
| 170 | - '%1$s <a href="https://givewp.com/support/">https://givewp.com/support</a>', | |
| 171 | - esc_html__( | |
| 172 | - 'There was a problem running the migrations. Please reach out to GiveWP support for assistance:', | |
| 173 | - 'give' | |
| 174 | - ) | |
| 175 | - ), | |
| 176 | - ] | |
| 177 | - ); | |
| 178 | - } | |
| 198 | + try { | |
| 199 | + $migrationLog->save(); | |
| 200 | + } catch (DatabaseQueryException $e) { | |
| 201 | + Log::error( | |
| 202 | + 'Failed to save migration log', | |
| 203 | + [ | |
| 204 | + 'Error Message' => $e->getMessage(), | |
| 205 | + 'Query Errors' => $e->getQueryErrors(), | |
| 206 | + ] | |
| 207 | + ); | |
| 208 | + } | |
| 179 | 209 | |
| 180 | - try { | |
| 181 | - $migrationLog->save(); | |
| 182 | - } catch (DatabaseQueryException $e) { | |
| 183 | - Log::error( | |
| 184 | - 'Failed to save migration log', | |
| 185 | - [ | |
| 186 | - 'Error Message' => $e->getMessage(), | |
| 187 | - 'Query Errors' => $e->getQueryErrors(), | |
| 188 | - ] | |
| 189 | - ); | |
| 190 | - } | |
| 210 | + // Stop Migration Runner if migration has failed | |
| 211 | + if ($migrationLog->getStatus() === MigrationLogStatus::FAILED) { | |
| 212 | + break; | |
| 213 | + } | |
| 191 | 214 | |
| 192 | - // Stop Migration Runner if migration has failed | |
| 193 | - if ($migrationLog->getStatus() === MigrationLogStatus::FAILED) { | |
| 194 | - break; | |
| 215 | + // Commit transaction if successful | |
| 216 | + DB::commit(); | |
| 195 | 217 | } |
| 196 | - | |
| 197 | - // Commit transaction if successful | |
| 198 | - DB::commit(); | |
| 218 | + } finally { | |
| 219 | + WP_Upgrader::release_lock(self::LOCK_NAME); | |
| 199 | 220 | } |
| 200 | 221 | } |
| 201 | 222 | |
| 202 | 223 | /** |