perf(milestones): refactor (#8788)
Some checks are pending
ZITADEL CI/CD / core (push) Waiting to run
ZITADEL CI/CD / console (push) Waiting to run
ZITADEL CI/CD / version (push) Waiting to run
ZITADEL CI/CD / compile (push) Blocked by required conditions
ZITADEL CI/CD / core-unit-test (push) Blocked by required conditions
ZITADEL CI/CD / core-integration-test (push) Blocked by required conditions
ZITADEL CI/CD / lint (push) Blocked by required conditions
ZITADEL CI/CD / container (push) Blocked by required conditions
ZITADEL CI/CD / e2e (push) Blocked by required conditions
ZITADEL CI/CD / release (push) Blocked by required conditions
Code Scanning / CodeQL-Build (go) (push) Waiting to run
Code Scanning / CodeQL-Build (javascript) (push) Waiting to run

# Which Problems Are Solved

Milestones used existing events from a number of aggregates. OIDC
session is one of them. We noticed in load-tests that the reduction of
the oidc_session.added event into the milestone projection is a costly
business with payload based conditionals. A milestone is reached once,
but even then we remain subscribed to the OIDC events. This requires the
projections.current_states to be updated continuously.


# How the Problems Are Solved

The milestone creation is refactored to use dedicated events instead.
The command side decides when a milestone is reached and creates the
reached event once for each milestone when required.

# Additional Changes

In order to prevent reached milestones being created twice, a migration
script is provided. When the old `projections.milestones` table exist,
the state is read from there and `v2` milestone aggregate events are
created, with the original reached and pushed dates.

# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/8800
This commit is contained in:
Tim Möhlmann
2024-10-28 09:29:34 +01:00
committed by GitHub
parent 54f1c0bc50
commit 32bad3feb3
46 changed files with 1612 additions and 756 deletions

View File

@@ -13,6 +13,7 @@ import (
"golang.org/x/text/language"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/cache/noop"
"github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
@@ -20,6 +21,7 @@ import (
"github.com/zitadel/zitadel/internal/id"
id_mock "github.com/zitadel/zitadel/internal/id/mock"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/milestone"
"github.com/zitadel/zitadel/internal/repository/org"
"github.com/zitadel/zitadel/internal/repository/project"
"github.com/zitadel/zitadel/internal/repository/user"
@@ -372,6 +374,7 @@ func setupInstanceEvents(ctx context.Context, instanceID, orgID, projectID, appI
setupInstanceElementsEvents(ctx, instanceID, instanceName, defaultLanguage),
orgEvents(ctx, instanceID, orgID, orgName, projectID, domain, externalSecure, true, true),
generatedDomainEvents(ctx, instanceID, orgID, projectID, appID, domain),
instanceCreatedMilestoneEvent(ctx, instanceID),
)
}
@@ -401,6 +404,12 @@ func generatedDomainEvents(ctx context.Context, instanceID, orgID, projectID, ap
}
}
func instanceCreatedMilestoneEvent(ctx context.Context, instanceID string) []eventstore.Command {
return []eventstore.Command{
milestone.NewReachedEvent(ctx, milestone.NewInstanceAggregate(instanceID), milestone.InstanceCreated),
}
}
func generatedDomainFilters(instanceID, orgID, projectID, appID, generatedDomain string) []expect {
return []expect{
expectFilter(),
@@ -1378,7 +1387,7 @@ func TestCommandSide_UpdateInstance(t *testing.T) {
func TestCommandSide_RemoveInstance(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(t *testing.T) *eventstore.Eventstore
}
type args struct {
ctx context.Context
@@ -1397,8 +1406,7 @@ func TestCommandSide_RemoveInstance(t *testing.T) {
{
name: "instance not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(),
),
},
@@ -1413,8 +1421,7 @@ func TestCommandSide_RemoveInstance(t *testing.T) {
{
name: "instance removed, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
instance.NewInstanceAddedEvent(
@@ -1444,8 +1451,7 @@ func TestCommandSide_RemoveInstance(t *testing.T) {
{
name: "instance remove, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusherWithInstanceID(
"INSTANCE",
@@ -1480,6 +1486,10 @@ func TestCommandSide_RemoveInstance(t *testing.T) {
"custom.domain",
},
),
milestone.NewReachedEvent(context.Background(),
milestone.NewInstanceAggregate("INSTANCE"),
milestone.InstanceDeleted,
),
),
),
},
@@ -1497,7 +1507,10 @@ func TestCommandSide_RemoveInstance(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
caches: &Caches{
milestones: noop.NewCache[milestoneIndex, string, *MilestonesReached](),
},
}
got, err := r.RemoveInstance(tt.args.ctx, tt.args.instanceID)
if tt.res.err == nil {