-
Notifications
You must be signed in to change notification settings - Fork 4
CORE-746 Quicksilver re-migration #3525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/quicksilver_mitigation
Are you sure you want to change the base?
Changes from all commits
86a580d
8077277
b3d6745
b771a16
c248969
e1ff056
c237a98
4064e7f
6db7b4b
f1096eb
d3860f4
a29e6f7
c1f5883
d1d3549
e71112d
ab92223
624001c
a8ea5ed
7a07f42
e2323cb
c31025b
d292fad
aa37c29
b70b4df
655320b
76b0c38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| package org.broadinstitute.dsde.rawls.dataaccess.slick | ||
|
|
||
| import org.broadinstitute.dsde.rawls.model.{AttributeName, MigratedEntity} | ||
| import slick.jdbc.MySQLProfile.api._ | ||
|
|
||
| import java.sql.Timestamp | ||
| import java.util.UUID | ||
|
|
||
| /** | ||
| * README: | ||
| * 1. Restore a Rawls db backup to a temporary CloudSQL instance. | ||
| * 2. Change the passwords for rawls, root, and readonly users in your temporary CloudSQL | ||
| * instance for safety. | ||
| * 3. Start a local cloud-sql-proxy connecting to the temporary CloudSQL instance. | ||
| * 4. Modify the `slick.db` section of rawls.conf to point at your local cloud-sql-proxy. | ||
| * You'll probably need to use `host.docker.internal` for the hostname in the url, | ||
| * and update the password to what you set in step 2. | ||
| * 5. Start Rawls locally via `config/docker-rsync-local-rawls.sh` | ||
| * 6. Call the `/api/workspaces/quicksilverValidation` API in your locally running Rawls | ||
| * 7. Watch your logs | ||
| * | ||
| */ | ||
| /* | ||
| -- ----- SQL to run prior to executing the re-migration ----- -- | ||
|
|
||
| create table CURRENT_MIGRATION ( | ||
| workspace_id binary(16) | ||
| ) comment="Holds state for the QuicksilverMigrationMonitor"; | ||
|
|
||
| create table MIGRATION_WORKSPACES ( | ||
| PRIMARY KEY (`id`), | ||
| id binary(16) | ||
| ) comment="All workspaces that need a re-migration"; | ||
|
|
||
| insert into MIGRATION_WORKSPACES(id) | ||
| select distinct workspace_id from ENTITY where deleted = b'0' order by workspace_id asc; | ||
|
|
||
| create table ENTITY_CORRECTIONS ( | ||
| `id` bigint unsigned NOT NULL AUTO_INCREMENT, | ||
| `workspace_id` binary(16) NOT NULL, | ||
| `entity_type` varchar(254) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin DEFAULT NULL, | ||
| `name` varchar(254) NOT NULL, | ||
| `attributes` json DEFAULT NULL, | ||
| `status` varchar(254) NOT NULL default 'OUTSTANDING', | ||
| PRIMARY KEY (`id`), | ||
| UNIQUE KEY `idx_corrections_entity_type_name` (`workspace_id`,`entity_type`,`name`), | ||
| KEY `idx_corrections_status` (`status`) | ||
| ) comment="Re-migrated entities. Contains only those active entities that had lists, and only their list attributes"; | ||
| */ | ||
| trait CompactEntityValidation { | ||
| this: CompactEntityQuery => | ||
|
|
||
| def mostRecentMigration: ReadAction[Timestamp] = | ||
| sql"""select max(LAST_UPDATED) | ||
| from WORKSPACE_SETTINGS | ||
| where SETTING_TYPE='CompactDataTables'""".as[Timestamp].head | ||
|
|
||
| def getRecentlyMigratedWorkspaces(maxDate: Timestamp, hours: Int): ReadAction[Seq[UUID]] = | ||
| sql"""select WORKSPACE_ID | ||
| from WORKSPACE_SETTINGS | ||
| where SETTING_TYPE='CompactDataTables' | ||
| and LAST_UPDATED > DATE_SUB($maxDate, INTERVAL $hours HOUR)""".as[UUID] | ||
|
Comment on lines
+53
to
+62
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ignore; this is for ad-hoc validation of the existing migrations |
||
|
|
||
| // ***** for tracking re-migration process | ||
| def getCurrentMigration: ReadAction[Option[UUID]] = | ||
| sql"""select workspace_id from CURRENT_MIGRATION""".as[UUID].headOption | ||
|
|
||
| def bootstrapCurrentMigration: ReadWriteAction[Int] = | ||
| sql"""insert into CURRENT_MIGRATION(workspace_id) | ||
| select id from MIGRATION_WORKSPACES order by id asc limit 1 """.asUpdate | ||
|
|
||
| def nextMigration(previousWorkspaceId: UUID): ReadAction[Option[UUID]] = | ||
| sql"""select id from MIGRATION_WORKSPACES where id > $previousWorkspaceId order by id asc limit 1""" | ||
| .as[UUID] | ||
| .headOption | ||
|
|
||
| def updateCurrentMigration(workspaceId: UUID): ReadWriteAction[Int] = | ||
| sql"""update CURRENT_MIGRATION set workspace_id = $workspaceId""".asUpdate | ||
|
|
||
| // ***** for persisting to the ENTITY_CORRECTIONS table | ||
| def saveMigratedEntities(migratedEntities: Seq[MigratedEntity]): ReadWriteAction[Int] = { | ||
| val startSql = sql"""insert into ENTITY_CORRECTIONS(workspace_id, entity_type, name, attributes) | ||
| values """ | ||
|
|
||
| val paramSql = reduceSqlActionsWithDelim( | ||
| migratedEntities map { migratedEntity => | ||
| sql"""(${migratedEntity.workspaceId}, ${migratedEntity.entityType}, ${migratedEntity.entityName}, ${migratedEntity.serializedAttributes})""" | ||
| }, | ||
| sql"," | ||
| ) | ||
|
|
||
| concatSqlActions(startSql, paramSql).asUpdate | ||
| } | ||
|
|
||
| def restartWorkspace(workspaceId: UUID): ReadWriteAction[Int] = | ||
| sql"""delete from ENTITY_CORRECTIONS where workspace_id = $workspaceId""".asUpdate | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -110,6 +110,45 @@ class EntityManager(providerBuilders: Set[EntityProviderBuilder[_ <: EntityProvi | |
| } | ||
| } | ||
| } | ||
|
|
||
| def getLocalProvider(requestArguments: EntityRequestArguments)(implicit | ||
| executionContext: ExecutionContext | ||
| ): Future[EntityProvider] = { | ||
| val targetTagFuture = Future(typeTag[LocalEntityProvider]) | ||
| getSpecificProvider(targetTagFuture, requestArguments) | ||
| } | ||
|
|
||
| def getCompactProvider(requestArguments: EntityRequestArguments)(implicit | ||
| executionContext: ExecutionContext | ||
| ): Future[EntityProvider] = { | ||
| val targetTagFuture = Future(typeTag[CompactEntityProvider]) | ||
| getSpecificProvider(targetTagFuture, requestArguments) | ||
| } | ||
|
|
||
| def getSpecificProvider(targetTagFuture: Future[TypeTag[_ <: EntityProvider]], | ||
| requestArguments: EntityRequestArguments | ||
| )(implicit | ||
| executionContext: ExecutionContext | ||
| ): Future[EntityProvider] = | ||
| targetTagFuture map { targetTag => | ||
| providerBuilders.find(_.builds == targetTag) match { | ||
| case None => | ||
| throw new DataEntityException( | ||
| s"no entity provider available for ${requestArguments.workspace.toWorkspaceName}" | ||
| ) | ||
| case Some(builder) => | ||
| builder.build(requestArguments) match { | ||
| case Success(provider) => | ||
| // Wrap the provider with AuditLoggingEntityProvider | ||
| new AuditLoggingEntityProvider(provider, requestArguments, metricsPrefix) | ||
| case Failure(regrets: DataEntityException) => | ||
| throw new RawlsExceptionWithErrorReport(ErrorReport(regrets.code, regrets.getMessage)) | ||
| case Failure(ex: Throwable) => | ||
| throw new RawlsExceptionWithErrorReport(ErrorReport(StatusCodes.InternalServerError, ex.getMessage)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. some helper code to get exactly the provider we want, regardless of the state of the workspace |
||
| } | ||
|
|
||
| object EntityManager { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ignore; this is for ad-hoc validation of the existing migrations