func isSchemaChangeRetryError(err error) bool { switch err { case errDescriptorNotFound: return false default: return !sqlbase.IsIntegrityConstraintError(err) } }
// Execute the entire schema change in steps. startBackfillNotification is // called before the backfill starts; it can be nil. func (sc SchemaChanger) exec( startBackfillNotification func() error, oldNameNotInUseNotification func(), ) error { // Acquire lease. lease, err := sc.AcquireLease() if err != nil { return err } needRelease := true // Always try to release lease. defer func(l *sqlbase.TableDescriptor_SchemaChangeLease) { // If the schema changer deleted the descriptor, there's no longer a lease to be // released. if !needRelease { return } if err := sc.ReleaseLease(*l); err != nil { log.Warning(err) } }(&lease) // Increment the version and unset tableDescriptor.UpVersion. desc, err := sc.MaybeIncrementVersion() if err != nil { return err } if desc.GetTable().Deleted() { lease, err = sc.ExtendLease(lease) if err != nil { return err } // Wait for everybody to see the version with the deleted bit set. When // this returns, nobody has any leases on the table, nor can get new leases, // so the table will no longer be modified. if err := sc.waitToUpdateLeases(); err != nil { return err } // Truncate the table and delete the descriptor. if err := sc.truncateAndDropTable(&lease, desc.GetTable()); err != nil { return err } needRelease = false return nil } if desc.GetTable().Renamed() { lease, err = sc.ExtendLease(lease) if err != nil { return err } // Wait for everyone to see the version with the new name. When this // returns, no new transactions will be using the old name for the table, so // the old name can now be re-used (by CREATE). if err := sc.waitToUpdateLeases(); err != nil { return err } if oldNameNotInUseNotification != nil { oldNameNotInUseNotification() } // Free up the old name(s). err := sc.db.Txn(func(txn *client.Txn) error { b := client.Batch{} for _, renameDetails := range desc.GetTable().Renames { tbKey := tableKey{ sqlbase.ID(renameDetails.OldParentID), renameDetails.OldName}.Key() b.Del(tbKey) } if err := txn.Run(&b); err != nil { return err } return nil }) if err != nil { return err } // Clean up - clear the descriptor's state. _, err = sc.leaseMgr.Publish(sc.tableID, func(desc *sqlbase.TableDescriptor) error { desc.Renames = nil return nil }) if err != nil { return err } } // Wait for the schema change to propagate to all nodes after this function // returns, so that the new schema is live everywhere. This is not needed for // correctness but is done to make the UI experience/tests predictable. defer func() { if err := sc.waitToUpdateLeases(); err != nil { log.Warning(err) } }() if sc.mutationID == sqlbase.InvalidMutationID { // Nothing more to do. return nil } // Another transaction might set the up_version bit again, // but we're no longer responsible for taking care of that. // Run through mutation state machine and backfill. err = sc.runStateMachineAndBackfill(&lease, startBackfillNotification) // Purge the mutations if the application of the mutations failed due to // an integrity constraint violation. All other errors are transient // errors that are resolved by retrying the backfill. if sqlbase.IsIntegrityConstraintError(err) { log.Warningf("reversing schema change due to irrecoverable error: %s", err) if errReverse := sc.reverseMutations(); errReverse != nil { // Although the backfill did hit an integrity constraint violation // and made a decision to reverse the mutations, // reverseMutations() failed. If exec() is called again the entire // schema change will be retried. return errReverse } // After this point the schema change has been reversed and any retry // of the schema change will act upon the reversed schema change. if errPurge := sc.runStateMachineAndBackfill( &lease, startBackfillNotification, ); errPurge != nil { // Don't return this error because we do want the caller to know // that an integrity constraint was violated with the original // schema change. The reversed schema change will be // retried via the async schema change manager. log.Warningf("error purging mutation: %s, after error: %s", errPurge, err) } } return err }