From e0e5665e173de1a8a92cbd5ac0c395bd6360b82b Mon Sep 17 00:00:00 2001 From: Silvan Date: Fri, 8 Mar 2024 14:33:53 +0100 Subject: [PATCH] fix(eventstore): consider `IsGlobal`-flag of constraints (#7518) * fix(eventstore): consider `IsGlobal`-flag of constraints * fix(setup): set `instance_domain`-constraint global (cherry picked from commit 60ee2610f2ae67e2e33ac8c08df739c44d397809) --- cmd/setup/23.go | 27 ++++++++ cmd/setup/23.sql | 1 + cmd/setup/config.go | 39 +++++------ cmd/setup/setup.go | 72 +++++++++----------- internal/eventstore/v3/unique_constraints.go | 16 +++-- 5 files changed, 92 insertions(+), 63 deletions(-) create mode 100644 cmd/setup/23.go create mode 100644 cmd/setup/23.sql diff --git a/cmd/setup/23.go b/cmd/setup/23.go new file mode 100644 index 0000000000..4c8268e26e --- /dev/null +++ b/cmd/setup/23.go @@ -0,0 +1,27 @@ +package setup + +import ( + "context" + _ "embed" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" +) + +var ( + //go:embed 23.sql + correctGlobalUniqueConstraints string +) + +type CorrectGlobalUniqueConstraints struct { + dbClient *database.DB +} + +func (mig *CorrectGlobalUniqueConstraints) Execute(ctx context.Context, _ eventstore.Event) error { + _, err := mig.dbClient.ExecContext(ctx, correctGlobalUniqueConstraints) + return err +} + +func (mig *CorrectGlobalUniqueConstraints) String() string { + return "23_correct_global_unique_constraints" +} diff --git a/cmd/setup/23.sql b/cmd/setup/23.sql new file mode 100644 index 0000000000..7494860d5e --- /dev/null +++ b/cmd/setup/23.sql @@ -0,0 +1 @@ +UPDATE eventstore.unique_constraints SET instance_id = '' WHERE unique_type = 'instance_domain'; \ No newline at end of file diff --git a/cmd/setup/config.go b/cmd/setup/config.go index 10ec0328ca..07022cac4f 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -82,25 +82,26 @@ func MustNewConfig(v *viper.Viper) *Config { } type Steps struct { - s1ProjectionTable *ProjectionTable - s2AssetsTable *AssetTable - FirstInstance *FirstInstance - s5LastFailed *LastFailed - s6OwnerRemoveColumns *OwnerRemoveColumns - s7LogstoreTables *LogstoreTables - s8AuthTokens *AuthTokenIndexes - CorrectCreationDate *CorrectCreationDate - s12AddOTPColumns *AddOTPColumns - s13FixQuotaProjection *FixQuotaConstraints - s14NewEventsTable *NewEventsTable - s15CurrentStates *CurrentProjectionState - s16UniqueConstraintsLower *UniqueConstraintToLower - s17AddOffsetToUniqueConstraints *AddOffsetToCurrentStates - s18AddLowerFieldsToLoginNames *AddLowerFieldsToLoginNames - s19AddCurrentStatesIndex *AddCurrentSequencesIndex - s20AddByUserSessionIndex *AddByUserIndexToSession - s21AddBlockFieldToLimits *AddBlockFieldToLimits - s22ActiveInstancesIndex *ActiveInstanceEvents + s1ProjectionTable *ProjectionTable + s2AssetsTable *AssetTable + FirstInstance *FirstInstance + s5LastFailed *LastFailed + s6OwnerRemoveColumns *OwnerRemoveColumns + s7LogstoreTables *LogstoreTables + s8AuthTokens *AuthTokenIndexes + CorrectCreationDate *CorrectCreationDate + s12AddOTPColumns *AddOTPColumns + s13FixQuotaProjection *FixQuotaConstraints + s14NewEventsTable *NewEventsTable + s15CurrentStates *CurrentProjectionState + s16UniqueConstraintsLower *UniqueConstraintToLower + s17AddOffsetToUniqueConstraints *AddOffsetToCurrentStates + s18AddLowerFieldsToLoginNames *AddLowerFieldsToLoginNames + s19AddCurrentStatesIndex *AddCurrentSequencesIndex + s20AddByUserSessionIndex *AddByUserIndexToSession + s21AddBlockFieldToLimits *AddBlockFieldToLimits + s22ActiveInstancesIndex *ActiveInstanceEvents + s23CorrectGlobalUniqueConstraints *CorrectGlobalUniqueConstraints } func MustNewSteps(v *viper.Viper) *Steps { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index b158441ed9..5dc8a0228b 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -135,6 +135,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.s20AddByUserSessionIndex = &AddByUserIndexToSession{dbClient: queryDBClient} steps.s21AddBlockFieldToLimits = &AddBlockFieldToLimits{dbClient: queryDBClient} steps.s22ActiveInstancesIndex = &ActiveInstanceEvents{dbClient: queryDBClient} + steps.s23CorrectGlobalUniqueConstraints = &CorrectGlobalUniqueConstraints{dbClient: esPusherDBClient} err = projection.Create(ctx, projectionDBClient, eventstoreClient, config.Projections, nil, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -153,49 +154,39 @@ func Setup(config *Config, steps *Steps, masterKey string) { }, } - err = migration.Migrate(ctx, eventstoreClient, steps.s14NewEventsTable) - logging.WithFields("name", steps.s14NewEventsTable.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s1ProjectionTable) - logging.WithFields("name", steps.s1ProjectionTable.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s2AssetsTable) - logging.WithFields("name", steps.s2AssetsTable.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.FirstInstance) - logging.WithFields("name", steps.FirstInstance.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s5LastFailed) - logging.WithFields("name", steps.s5LastFailed.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s6OwnerRemoveColumns) - logging.WithFields("name", steps.s6OwnerRemoveColumns.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s7LogstoreTables) - logging.WithFields("name", steps.s7LogstoreTables.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s8AuthTokens) - logging.WithFields("name", steps.s8AuthTokens.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s12AddOTPColumns) - logging.WithFields("name", steps.s12AddOTPColumns.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s13FixQuotaProjection) - logging.WithFields("name", steps.s13FixQuotaProjection.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s15CurrentStates) - logging.WithFields("name", steps.s15CurrentStates.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s16UniqueConstraintsLower) - logging.WithFields("name", steps.s16UniqueConstraintsLower.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s17AddOffsetToUniqueConstraints) - logging.WithFields("name", steps.s17AddOffsetToUniqueConstraints.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s19AddCurrentStatesIndex) - logging.WithFields("name", steps.s19AddCurrentStatesIndex.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s20AddByUserSessionIndex) - logging.WithFields("name", steps.s20AddByUserSessionIndex.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s22ActiveInstancesIndex) - logging.WithFields("name", steps.s22ActiveInstancesIndex.String()).OnError(err).Fatal("migration failed") + for _, step := range []migration.Migration{ + steps.s14NewEventsTable, + steps.s1ProjectionTable, + steps.s2AssetsTable, + steps.FirstInstance, + steps.s5LastFailed, + steps.s6OwnerRemoveColumns, + steps.s7LogstoreTables, + steps.s8AuthTokens, + steps.s12AddOTPColumns, + steps.s13FixQuotaProjection, + steps.s15CurrentStates, + steps.s16UniqueConstraintsLower, + steps.s17AddOffsetToUniqueConstraints, + steps.s19AddCurrentStatesIndex, + steps.s20AddByUserSessionIndex, + steps.s22ActiveInstancesIndex, + steps.s23CorrectGlobalUniqueConstraints, + } { + mustExecuteMigration(ctx, eventstoreClient, step, "migration failed") + } for _, repeatableStep := range repeatableSteps { - err = migration.Migrate(ctx, eventstoreClient, repeatableStep) - logging.OnError(err).Fatalf("unable to migrate repeatable step: %s", repeatableStep.String()) + mustExecuteMigration(ctx, eventstoreClient, repeatableStep, "unable to migrate repeatable step") } // These steps are executed after the repeatable steps because they add fields projections - err = migration.Migrate(ctx, eventstoreClient, steps.s18AddLowerFieldsToLoginNames) - logging.WithFields("name", steps.s18AddLowerFieldsToLoginNames.String()).OnError(err).Fatal("migration failed") - err = migration.Migrate(ctx, eventstoreClient, steps.s21AddBlockFieldToLimits) - logging.WithFields("name", steps.s21AddBlockFieldToLimits.String()).OnError(err).Fatal("migration failed") + for _, step := range []migration.Migration{ + steps.s18AddLowerFieldsToLoginNames, + steps.s21AddBlockFieldToLimits, + } { + mustExecuteMigration(ctx, eventstoreClient, step, "migration failed") + } // projection initialization must be done last, since the steps above might add required columns to the projections if config.InitProjections.Enabled { @@ -210,6 +201,11 @@ func Setup(config *Config, steps *Steps, masterKey string) { } } +func mustExecuteMigration(ctx context.Context, eventstoreClient *eventstore.Eventstore, step migration.Migration, errorMsg string) { + err := migration.Migrate(ctx, eventstoreClient, step) + logging.WithFields("name", step.String()).OnError(err).Fatal(errorMsg) +} + func readStmt(fs embed.FS, folder, typ, filename string) (string, error) { stmt, err := fs.ReadFile(folder + "/" + typ + "/" + filename) return string(stmt), err diff --git a/internal/eventstore/v3/unique_constraints.go b/internal/eventstore/v3/unique_constraints.go index 34967e9b39..d93d79cd40 100644 --- a/internal/eventstore/v3/unique_constraints.go +++ b/internal/eventstore/v3/unique_constraints.go @@ -35,20 +35,24 @@ func handleUniqueConstraints(ctx context.Context, tx *sql.Tx, commands []eventst for _, command := range commands { for _, constraint := range command.UniqueConstraints() { + instanceID := command.Aggregate().InstanceID + if constraint.IsGlobal { + instanceID = "" + } switch constraint.Action { case eventstore.UniqueConstraintAdd: constraint.UniqueField = strings.ToLower(constraint.UniqueField) addPlaceholders = append(addPlaceholders, fmt.Sprintf("($%d, $%d, $%d)", len(addArgs)+1, len(addArgs)+2, len(addArgs)+3)) - addArgs = append(addArgs, command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField) - addConstraints[fmt.Sprintf(uniqueConstraintPlaceholderFmt, command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField)] = constraint + addArgs = append(addArgs, instanceID, constraint.UniqueType, constraint.UniqueField) + addConstraints[fmt.Sprintf(uniqueConstraintPlaceholderFmt, instanceID, constraint.UniqueType, constraint.UniqueField)] = constraint case eventstore.UniqueConstraintRemove: deletePlaceholders = append(deletePlaceholders, fmt.Sprintf(deleteConstraintPlaceholdersStmt, len(deleteArgs)+1, len(deleteArgs)+2, len(deleteArgs)+3)) - deleteArgs = append(deleteArgs, command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField) - deleteConstraints[fmt.Sprintf(uniqueConstraintPlaceholderFmt, command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField)] = constraint + deleteArgs = append(deleteArgs, instanceID, constraint.UniqueType, constraint.UniqueField) + deleteConstraints[fmt.Sprintf(uniqueConstraintPlaceholderFmt, instanceID, constraint.UniqueType, constraint.UniqueField)] = constraint case eventstore.UniqueConstraintInstanceRemove: deletePlaceholders = append(deletePlaceholders, fmt.Sprintf("(instance_id = $%d)", len(deleteArgs)+1)) - deleteArgs = append(deleteArgs, command.Aggregate().InstanceID) - deleteConstraints[fmt.Sprintf(uniqueConstraintPlaceholderFmt, command.Aggregate().InstanceID, constraint.UniqueType, constraint.UniqueField)] = constraint + deleteArgs = append(deleteArgs, instanceID) + deleteConstraints[fmt.Sprintf(uniqueConstraintPlaceholderFmt, instanceID, constraint.UniqueType, constraint.UniqueField)] = constraint } } }