zitadel/internal/repository/milestone/events.go
Tim Möhlmann 32bad3feb3
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
perf(milestones): refactor (#8788)
# 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
2024-10-28 08:29:34 +00:00

140 lines
3.1 KiB
Go

package milestone
import (
"context"
"time"
"github.com/zitadel/zitadel/internal/eventstore"
)
//go:generate enumer -type Type -json -linecomment -transform=snake
type Type int
const (
InstanceCreated Type = iota
AuthenticationSucceededOnInstance
ProjectCreated
ApplicationCreated
AuthenticationSucceededOnApplication
InstanceDeleted
)
const (
eventTypePrefix = "milestone."
ReachedEventType = eventTypePrefix + "reached"
PushedEventType = eventTypePrefix + "pushed"
)
type ReachedEvent struct {
*eventstore.BaseEvent `json:"-"`
MilestoneType Type `json:"type"`
ReachedDate *time.Time `json:"reachedDate,omitempty"` // Defaults to [eventstore.BaseEvent.Creation] when empty
}
// Payload implements eventstore.Command.
func (e *ReachedEvent) Payload() any {
return e
}
func (e *ReachedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func (e *ReachedEvent) SetBaseEvent(b *eventstore.BaseEvent) {
e.BaseEvent = b
}
func (e *ReachedEvent) GetReachedDate() time.Time {
if e.ReachedDate != nil {
return *e.ReachedDate
}
return e.Creation
}
func NewReachedEvent(
ctx context.Context,
aggregate *Aggregate,
typ Type,
) *ReachedEvent {
return NewReachedEventWithDate(ctx, aggregate, typ, nil)
}
// NewReachedEventWithDate creates a [ReachedEvent] with a fixed Reached Date.
func NewReachedEventWithDate(
ctx context.Context,
aggregate *Aggregate,
typ Type,
reachedDate *time.Time,
) *ReachedEvent {
return &ReachedEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
&aggregate.Aggregate,
ReachedEventType,
),
MilestoneType: typ,
ReachedDate: reachedDate,
}
}
type PushedEvent struct {
*eventstore.BaseEvent `json:"-"`
MilestoneType Type `json:"type"`
ExternalDomain string `json:"externalDomain"`
PrimaryDomain string `json:"primaryDomain"`
Endpoints []string `json:"endpoints"`
PushedDate *time.Time `json:"pushedDate,omitempty"` // Defaults to [eventstore.BaseEvent.Creation] when empty
}
// Payload implements eventstore.Command.
func (p *PushedEvent) Payload() any {
return p
}
func (p *PushedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func (p *PushedEvent) SetBaseEvent(b *eventstore.BaseEvent) {
p.BaseEvent = b
}
func (e *PushedEvent) GetPushedDate() time.Time {
if e.PushedDate != nil {
return *e.PushedDate
}
return e.Creation
}
func NewPushedEvent(
ctx context.Context,
aggregate *Aggregate,
typ Type,
endpoints []string,
externalDomain string,
) *PushedEvent {
return NewPushedEventWithDate(ctx, aggregate, typ, endpoints, externalDomain, nil)
}
// NewPushedEventWithDate creates a [PushedEvent] with a fixed Pushed Date.
func NewPushedEventWithDate(
ctx context.Context,
aggregate *Aggregate,
typ Type,
endpoints []string,
externalDomain string,
pushedDate *time.Time,
) *PushedEvent {
return &PushedEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
&aggregate.Aggregate,
PushedEventType,
),
MilestoneType: typ,
Endpoints: endpoints,
ExternalDomain: externalDomain,
PushedDate: pushedDate,
}
}