reduce milestone pushed

This commit is contained in:
Elio Bischof 2023-06-28 11:35:22 +02:00
parent 51a9a54cfd
commit a14871ce46
No known key found for this signature in database
GPG Key ID: 7B383FDE4DDBF1BD
13 changed files with 262 additions and 353 deletions

View File

@ -49,7 +49,7 @@ Projections:
NotificationsQuotas: NotificationsQuotas:
# RequeueEvery: 1s # RequeueEvery: 1s
Telemetry: Telemetry:
RequeueEvery: 10s # RequeueEvery: 1s
DefaultInstance: DefaultInstance:
LoginPolicy: LoginPolicy:

View File

@ -6,11 +6,11 @@ import (
"github.com/zitadel/zitadel/internal/repository/milestone" "github.com/zitadel/zitadel/internal/repository/milestone"
) )
// MilestonePushed writes a new event with a new milestone.Aggregate to the eventstore // MilestonePushed writes a new milestone.PushedEvent with a new milestone.Aggregate to the eventstore
func (c *Commands) MilestonePushed( func (c *Commands) MilestonePushed(
ctx context.Context, ctx context.Context,
instanceID string, instanceID string,
eventType milestone.PushedEventType, msType milestone.Type,
endpoints []string, endpoints []string,
primaryDomain string, primaryDomain string,
) error { ) error {
@ -18,10 +18,6 @@ func (c *Commands) MilestonePushed(
if err != nil { if err != nil {
return err return err
} }
pushedEvent, err := milestone.NewPushedEventByType(ctx, eventType, milestone.NewAggregate(id, instanceID, instanceID), endpoints, primaryDomain) _, err = c.eventstore.Push(ctx, milestone.NewPushedEvent(ctx, milestone.NewAggregate(id, instanceID, instanceID), msType, endpoints, primaryDomain))
if err != nil {
return err
}
_, err = c.eventstore.Push(ctx, pushedEvent)
return err return err
} }

View File

@ -52,7 +52,7 @@ type StatementHandler struct {
bulkLimit uint64 bulkLimit uint64
subscribe bool reduceScheduledPseudoEvent bool
} }
func NewStatementHandler( func NewStatementHandler(
@ -61,37 +61,40 @@ func NewStatementHandler(
) StatementHandler { ) StatementHandler {
aggregateTypes := make([]eventstore.AggregateType, 0, len(config.Reducers)) aggregateTypes := make([]eventstore.AggregateType, 0, len(config.Reducers))
reduces := make(map[eventstore.EventType]handler.Reduce, len(config.Reducers)) reduces := make(map[eventstore.EventType]handler.Reduce, len(config.Reducers))
subscribe := true reduceScheduledPseudoEvent := false
for _, aggReducer := range config.Reducers { for _, aggReducer := range config.Reducers {
aggregateTypes = append(aggregateTypes, aggReducer.Aggregate) aggregateTypes = append(aggregateTypes, aggReducer.Aggregate)
if aggReducer.Aggregate == pseudo.AggregateType { if aggReducer.Aggregate == pseudo.AggregateType {
subscribe = false reduceScheduledPseudoEvent = true
if len(config.Reducers) != 1 ||
len(aggReducer.EventRedusers) != 1 ||
aggReducer.EventRedusers[0].Event != pseudo.ScheduledEventType {
panic("if a pseudo.AggregateType is reduced, exactly one event reducer for pseudo.ScheduledEventType is supported and no other aggregate can be reduced")
}
} }
for _, eventReducer := range aggReducer.EventRedusers { for _, eventReducer := range aggReducer.EventRedusers {
if eventReducer.Event == pseudo.TimestampEventType {
subscribe = false
}
reduces[eventReducer.Event] = eventReducer.Reduce reduces[eventReducer.Event] = eventReducer.Reduce
} }
} }
h := StatementHandler{ h := StatementHandler{
client: config.Client, client: config.Client,
sequenceTable: config.SequenceTable, sequenceTable: config.SequenceTable,
maxFailureCount: config.MaxFailureCount, maxFailureCount: config.MaxFailureCount,
currentSequenceStmt: fmt.Sprintf(currentSequenceStmtFormat, config.SequenceTable), currentSequenceStmt: fmt.Sprintf(currentSequenceStmtFormat, config.SequenceTable),
updateSequencesBaseStmt: fmt.Sprintf(updateCurrentSequencesStmtFormat, config.SequenceTable), updateSequencesBaseStmt: fmt.Sprintf(updateCurrentSequencesStmtFormat, config.SequenceTable),
failureCountStmt: fmt.Sprintf(failureCountStmtFormat, config.FailedEventsTable), failureCountStmt: fmt.Sprintf(failureCountStmtFormat, config.FailedEventsTable),
setFailureCountStmt: fmt.Sprintf(setFailureCountStmtFormat, config.FailedEventsTable), setFailureCountStmt: fmt.Sprintf(setFailureCountStmtFormat, config.FailedEventsTable),
aggregates: aggregateTypes, aggregates: aggregateTypes,
reduces: reduces, reduces: reduces,
bulkLimit: config.BulkLimit, bulkLimit: config.BulkLimit,
Locker: NewLocker(config.Client.DB, config.LockTable, config.ProjectionName), Locker: NewLocker(config.Client.DB, config.LockTable, config.ProjectionName),
initCheck: config.InitCheck, initCheck: config.InitCheck,
initialized: make(chan bool), initialized: make(chan bool),
reduceScheduledPseudoEvent: reduceScheduledPseudoEvent,
} }
h.ProjectionHandler = handler.NewProjectionHandler(ctx, config.ProjectionHandlerConfig, h.reduce, h.Update, h.SearchQuery, h.Lock, h.Unlock, h.initialized, subscribe) h.ProjectionHandler = handler.NewProjectionHandler(ctx, config.ProjectionHandlerConfig, h.reduce, h.Update, h.SearchQuery, h.Lock, h.Unlock, h.initialized, reduceScheduledPseudoEvent)
return h return h
} }
@ -99,10 +102,9 @@ func NewStatementHandler(
func (h *StatementHandler) Start() { func (h *StatementHandler) Start() {
h.initialized <- true h.initialized <- true
close(h.initialized) close(h.initialized)
if !h.subscribe { if h.reduceScheduledPseudoEvent {
return h.Subscribe(h.aggregates...)
} }
h.Subscribe(h.aggregates...)
} }
func (h *StatementHandler) SearchQuery(ctx context.Context, instanceIDs []string) (*eventstore.SearchQueryBuilder, uint64, error) { func (h *StatementHandler) SearchQuery(ctx context.Context, instanceIDs []string) (*eventstore.SearchQueryBuilder, uint64, error) {
@ -111,7 +113,12 @@ func (h *StatementHandler) SearchQuery(ctx context.Context, instanceIDs []string
return nil, 0, err return nil, 0, err
} }
queryBuilder := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).Limit(h.bulkLimit).AllowTimeTravel() bulkLimit := h.bulkLimit
if h.reduceScheduledPseudoEvent {
bulkLimit = 1
}
queryBuilder := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).Limit(bulkLimit).AllowTimeTravel()
for _, aggregateType := range h.aggregates { for _, aggregateType := range h.aggregates {
for _, instanceID := range instanceIDs { for _, instanceID := range instanceIDs {
@ -124,13 +131,18 @@ func (h *StatementHandler) SearchQuery(ctx context.Context, instanceIDs []string
} }
queryBuilder. queryBuilder.
AddQuery(). AddQuery().
AggregateTypes(aggregateType).
SequenceGreater(seq). SequenceGreater(seq).
InstanceID(instanceID) InstanceID(instanceID)
if !h.reduceScheduledPseudoEvent {
queryBuilder.
AddQuery().
AggregateTypes(aggregateType)
}
} }
} }
return queryBuilder, h.bulkLimit, nil return queryBuilder, bulkLimit, nil
} }
// Update implements handler.Update // Update implements handler.Update

View File

@ -51,19 +51,20 @@ type NowFunc func() time.Time
type ProjectionHandler struct { type ProjectionHandler struct {
Handler Handler
ProjectionName string ProjectionName string
reduce Reduce reduce Reduce
update Update update Update
searchQuery SearchQuery searchQuery SearchQuery
triggerProjection *time.Timer triggerProjection *time.Timer
lock Lock lock Lock
unlock Unlock unlock Unlock
requeueAfter time.Duration requeueAfter time.Duration
retryFailedAfter time.Duration retryFailedAfter time.Duration
retries int retries int
concurrentInstances int concurrentInstances int
handleActiveInstances time.Duration handleActiveInstances time.Duration
nowFunc NowFunc nowFunc NowFunc
reduceScheduledPseudoEvent bool
} }
func NewProjectionHandler( func NewProjectionHandler(
@ -75,32 +76,33 @@ func NewProjectionHandler(
lock Lock, lock Lock,
unlock Unlock, unlock Unlock,
initialized <-chan bool, initialized <-chan bool,
subscribe bool, reduceScheduledPseudoEvent bool,
) *ProjectionHandler { ) *ProjectionHandler {
concurrentInstances := int(config.ConcurrentInstances) concurrentInstances := int(config.ConcurrentInstances)
if concurrentInstances < 1 { if concurrentInstances < 1 {
concurrentInstances = 1 concurrentInstances = 1
} }
h := &ProjectionHandler{ h := &ProjectionHandler{
Handler: NewHandler(config.HandlerConfig), Handler: NewHandler(config.HandlerConfig),
ProjectionName: config.ProjectionName, ProjectionName: config.ProjectionName,
reduce: reduce, reduce: reduce,
update: update, update: update,
searchQuery: query, searchQuery: query,
lock: lock, lock: lock,
unlock: unlock, unlock: unlock,
requeueAfter: config.RequeueEvery, requeueAfter: config.RequeueEvery,
triggerProjection: time.NewTimer(0), // first trigger is instant on startup triggerProjection: time.NewTimer(0), // first trigger is instant on startup
retryFailedAfter: config.RetryFailedAfter, retryFailedAfter: config.RetryFailedAfter,
retries: int(config.Retries), retries: int(config.Retries),
concurrentInstances: concurrentInstances, concurrentInstances: concurrentInstances,
handleActiveInstances: config.HandleActiveInstances, handleActiveInstances: config.HandleActiveInstances,
nowFunc: time.Now, nowFunc: time.Now,
reduceScheduledPseudoEvent: reduceScheduledPseudoEvent,
} }
go func() { go func() {
<-initialized <-initialized
if subscribe { if h.reduceScheduledPseudoEvent {
go h.subscribe(ctx) go h.subscribe(ctx)
} }
go h.schedule(ctx) go h.schedule(ctx)
@ -116,9 +118,6 @@ func (h *ProjectionHandler) Trigger(ctx context.Context, instances ...string) er
if len(instances) > 0 { if len(instances) > 0 {
ids = instances ids = instances
} }
if h.searchQuery == nil {
return h.processTimestamp(ctx, ids...)
}
return h.processEvents(ctx, ids...) return h.processEvents(ctx, ids...)
} }
@ -141,11 +140,6 @@ func (h *ProjectionHandler) processEvents(ctx context.Context, ids ...string) er
} }
} }
func (h *ProjectionHandler) processTimestamp(ctx context.Context, instances ...string) error {
_, err := h.Process(ctx, pseudo.NewTimestampEvent(h.nowFunc(), instances...))
return err
}
// Process handles multiple events by reducing them to statements and updating the projection // Process handles multiple events by reducing them to statements and updating the projection
func (h *ProjectionHandler) Process(ctx context.Context, events ...eventstore.Event) (index int, err error) { func (h *ProjectionHandler) Process(ctx context.Context, events ...eventstore.Event) (index int, err error) {
if len(events) == 0 { if len(events) == 0 {
@ -182,6 +176,9 @@ func (h *ProjectionHandler) FetchEvents(ctx context.Context, instances ...string
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
if h.reduceScheduledPseudoEvent {
events[0] = pseudo.NewScheduledEvent(ctx, time.Now(), instances...)
}
return events, int(eventsLimit) == len(events), err return events, int(eventsLimit) == len(events), err
} }

View File

@ -72,7 +72,7 @@ func (t *telemetryPusher) reducers() []handler.AggregateReducer {
return []handler.AggregateReducer{{ return []handler.AggregateReducer{{
Aggregate: pseudo.AggregateType, Aggregate: pseudo.AggregateType,
EventRedusers: []handler.EventReducer{{ EventRedusers: []handler.EventReducer{{
Event: pseudo.TimestampEventType, Event: pseudo.ScheduledEventType,
Reduce: t.pushMilestones, Reduce: t.pushMilestones,
}}, }},
}} }}
@ -80,7 +80,7 @@ func (t *telemetryPusher) reducers() []handler.AggregateReducer {
func (t *telemetryPusher) pushMilestones(event eventstore.Event) (*handler.Statement, error) { func (t *telemetryPusher) pushMilestones(event eventstore.Event) (*handler.Statement, error) {
ctx := call.WithTimestamp(context.Background()) ctx := call.WithTimestamp(context.Background())
timestampEvent, ok := event.(pseudo.TimestampEvent) scheduledEvent, ok := event.(*pseudo.ScheduledEvent)
if !ok { if !ok {
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-lDTs5", "reduce.wrong.event.type %s", event.Type()) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-lDTs5", "reduce.wrong.event.type %s", event.Type())
} }
@ -97,9 +97,9 @@ func (t *telemetryPusher) pushMilestones(event eventstore.Event) (*handler.State
if err != nil { if err != nil {
return nil, err return nil, err
} }
unpushedMilestones, err := t.queries.Queries.SearchMilestones(ctx, timestampEvent.InstanceIDs, &query.MilestonesSearchQueries{ unpushedMilestones, err := t.queries.Queries.SearchMilestones(ctx, scheduledEvent.InstanceIDs, &query.MilestonesSearchQueries{
SearchRequest: query.SearchRequest{ SearchRequest: query.SearchRequest{
Offset: 100, Limit: 100,
SortingColumn: query.MilestoneReachedDateColID, SortingColumn: query.MilestoneReachedDateColID,
Asc: true, Asc: true,
}, },
@ -110,7 +110,7 @@ func (t *telemetryPusher) pushMilestones(event eventstore.Event) (*handler.State
} }
var errs int var errs int
for _, ms := range unpushedMilestones.Milestones { for _, ms := range unpushedMilestones.Milestones {
if err = t.pushMilestone(ctx, ms); err != nil { if err = t.pushMilestone(ctx, scheduledEvent, ms); err != nil {
errs++ errs++
logging.Warnf("pushing milestone %+v failed: %s", *ms, err.Error()) logging.Warnf("pushing milestone %+v failed: %s", *ms, err.Error())
} }
@ -119,10 +119,10 @@ func (t *telemetryPusher) pushMilestones(event eventstore.Event) (*handler.State
return nil, fmt.Errorf("pushing %d of %d milestones failed", errs, unpushedMilestones.Count) return nil, fmt.Errorf("pushing %d of %d milestones failed", errs, unpushedMilestones.Count)
} }
return crdb.NewNoOpStatement(timestampEvent), nil return crdb.NewNoOpStatement(scheduledEvent), nil
} }
func (t *telemetryPusher) pushMilestone(ctx context.Context, ms *query.Milestone) error { func (t *telemetryPusher) pushMilestone(ctx context.Context, event *pseudo.ScheduledEvent, ms *query.Milestone) error {
for _, endpoint := range t.endpoints { for _, endpoint := range t.endpoints {
if err := types.SendJSON( if err := types.SendJSON(
ctx, ctx,
@ -133,12 +133,12 @@ func (t *telemetryPusher) pushMilestone(ctx context.Context, ms *query.Milestone
t.queries.GetFileSystemProvider, t.queries.GetFileSystemProvider,
t.queries.GetLogProvider, t.queries.GetLogProvider,
ms, ms,
nil, event,
t.metricSuccessfulDeliveriesJSON, t.metricSuccessfulDeliveriesJSON,
t.metricFailedDeliveriesJSON, t.metricFailedDeliveriesJSON,
).WithoutTemplate(); err != nil { ).WithoutTemplate(); err != nil {
return err return err
} }
} }
return t.commands.MilestonePushed(ctx, ms.InstanceID, ms.MilestoneType, t.endpoints, ms.PrimaryDomain) return t.commands.MilestonePushed(ctx, ms.InstanceID, ms.Type, t.endpoints, ms.PrimaryDomain)
} }

View File

@ -26,7 +26,7 @@ type Milestones struct {
type Milestone struct { type Milestone struct {
InstanceID string InstanceID string
MilestoneType milestone.PushedEventType Type milestone.Type
ReachedDate time.Time ReachedDate time.Time
PushedDate time.Time PushedDate time.Time
PrimaryDomain string PrimaryDomain string
@ -55,7 +55,7 @@ var (
table: milestonesTable, table: milestonesTable,
} }
MilestoneTypeColID = Column{ MilestoneTypeColID = Column{
name: projection.MilestoneColumnMilestoneType, name: projection.MilestoneColumnType,
table: milestonesTable, table: milestonesTable,
} }
MilestonePrimaryDomainColID = Column{ MilestonePrimaryDomainColID = Column{
@ -80,10 +80,14 @@ func (q *Queries) SearchMilestones(ctx context.Context, instanceIDs []string, qu
if len(instanceIDs) == 0 { if len(instanceIDs) == 0 {
instanceIDs = []string{authz.GetInstance(ctx).InstanceID()} instanceIDs = []string{authz.GetInstance(ctx).InstanceID()}
} }
stmt, args, err := queries.toQuery(query). instanceIDParams := make([]string, len(instanceIDs))
Where(sq.Eq{ instanceIDArgs := make([]interface{}, len(instanceIDs))
MilestoneInstanceIDColID.identifier(): fmt.Sprintf("IN (%s)", strings.Join(instanceIDs, ",")), for idx := range instanceIDs {
}).ToSql() instanceIDParams[idx] = fmt.Sprintf("$%d", idx+1)
instanceIDArgs[idx] = instanceIDs[idx]
}
expr := fmt.Sprintf("%s IN (%s)", MilestoneInstanceIDColID.name, strings.Join(instanceIDParams, ","))
stmt, args, err := queries.toQuery(query).Where(sq.Expr(expr, instanceIDArgs...)).ToSql()
if err != nil { if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-A9i5k", "Errors.Query.SQLStatement") return nil, errors.ThrowInternal(err, "QUERY-A9i5k", "Errors.Query.SQLStatement")
} }
@ -102,28 +106,37 @@ func (q *Queries) SearchMilestones(ctx context.Context, instanceIDs []string, qu
func prepareMilestonesQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*Milestones, error)) { func prepareMilestonesQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Rows) (*Milestones, error)) {
return sq.Select( return sq.Select(
MilestoneInstanceIDColID.identifier(),
MilestonePrimaryDomainColID.identifier(), MilestonePrimaryDomainColID.identifier(),
MilestoneReachedDateColID.identifier(), MilestoneReachedDateColID.identifier(),
MilestonePushedDateColID.identifier(), MilestonePushedDateColID.identifier(),
MilestoneTypeColID.identifier(), MilestoneTypeColID.identifier(),
countColumn.identifier(), countColumn.identifier(),
). ).
From(notificationPolicyTable.identifier() + db.Timetravel(call.Took(ctx))). From(milestonesTable.identifier() + db.Timetravel(call.Took(ctx))).
PlaceholderFormat(sq.Dollar), PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*Milestones, error) { func(rows *sql.Rows) (*Milestones, error) {
milestones := make([]*Milestone, 0) milestones := make([]*Milestone, 0)
var count uint64 var count uint64
for rows.Next() { for rows.Next() {
m := new(Milestone) m := new(Milestone)
reachedDate := sql.NullTime{}
pushedDate := sql.NullTime{}
primaryDomain := sql.NullString{}
err := rows.Scan( err := rows.Scan(
&m.PrimaryDomain, &m.InstanceID,
&m.ReachedDate, &primaryDomain,
&m.MilestoneType, &reachedDate,
&pushedDate,
&m.Type,
&count, &count,
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
m.PrimaryDomain = primaryDomain.String
m.ReachedDate = reachedDate.Time
m.ReachedDate = pushedDate.Time
milestones = append(milestones, m) milestones = append(milestones, m)
} }
if err := rows.Close(); err != nil { if err := rows.Close(); err != nil {

View File

@ -21,7 +21,7 @@ const (
MilestonesProjectionTable = "projections.milestones" MilestonesProjectionTable = "projections.milestones"
MilestoneColumnInstanceID = "instance_id" MilestoneColumnInstanceID = "instance_id"
MilestoneColumnMilestoneType = "milestone_type" MilestoneColumnType = "type"
MilestoneColumnPrimaryDomain = "primary_domain" MilestoneColumnPrimaryDomain = "primary_domain"
MilestoneColumnReachedDate = "reached_date" MilestoneColumnReachedDate = "reached_date"
MilestoneColumnPushedDate = "pushed_date" MilestoneColumnPushedDate = "pushed_date"
@ -38,12 +38,12 @@ func newMilestoneProjection(ctx context.Context, config crdb.StatementHandlerCon
config.InitCheck = crdb.NewMultiTableCheck( config.InitCheck = crdb.NewMultiTableCheck(
crdb.NewTable([]*crdb.Column{ crdb.NewTable([]*crdb.Column{
crdb.NewColumn(MilestoneColumnInstanceID, crdb.ColumnTypeText), crdb.NewColumn(MilestoneColumnInstanceID, crdb.ColumnTypeText),
crdb.NewColumn(MilestoneColumnMilestoneType, crdb.ColumnTypeText), crdb.NewColumn(MilestoneColumnType, crdb.ColumnTypeEnum),
crdb.NewColumn(MilestoneColumnReachedDate, crdb.ColumnTypeTimestamp, crdb.Nullable()), crdb.NewColumn(MilestoneColumnReachedDate, crdb.ColumnTypeTimestamp, crdb.Nullable()),
crdb.NewColumn(MilestoneColumnPushedDate, crdb.ColumnTypeTimestamp, crdb.Nullable()), crdb.NewColumn(MilestoneColumnPushedDate, crdb.ColumnTypeTimestamp, crdb.Nullable()),
crdb.NewColumn(MilestoneColumnPrimaryDomain, crdb.ColumnTypeText, crdb.Nullable()), crdb.NewColumn(MilestoneColumnPrimaryDomain, crdb.ColumnTypeText, crdb.Nullable()),
}, },
crdb.NewPrimaryKey(MilestoneColumnInstanceID, MilestoneColumnMilestoneType), crdb.NewPrimaryKey(MilestoneColumnInstanceID, MilestoneColumnType),
), ),
) )
p.StatementHandler = crdb.NewStatementHandler(ctx, config) p.StatementHandler = crdb.NewStatementHandler(ctx, config)
@ -65,7 +65,7 @@ func (p *milestoneProjection) reducers() []handler.AggregateReducer {
}, },
{ {
Event: instance.InstanceRemovedEventType, Event: instance.InstanceRemovedEventType,
Reduce: p.milestoneReached(milestone.PushedInstanceDeletedEventType), Reduce: p.milestoneReached(milestone.InstanceDeleted),
}, },
}, },
}, },
@ -74,11 +74,11 @@ func (p *milestoneProjection) reducers() []handler.AggregateReducer {
EventRedusers: []handler.EventReducer{ EventRedusers: []handler.EventReducer{
{ {
Event: project.ProjectAddedType, Event: project.ProjectAddedType,
Reduce: p.milestoneReached(milestone.PushedProjectCreatedEventType), Reduce: p.milestoneReached(milestone.ProjectCreated),
}, },
{ {
Event: project.ApplicationAddedType, Event: project.ApplicationAddedType,
Reduce: p.milestoneReached(milestone.PushedApplicationCreatedEventType), Reduce: p.milestoneReached(milestone.ApplicationCreated),
}, },
}, },
}, },
@ -91,23 +91,31 @@ func (p *milestoneProjection) reducers() []handler.AggregateReducer {
}, },
}, },
}, },
{
Aggregate: milestone.AggregateType,
EventRedusers: []handler.EventReducer{
{
Event: milestone.PushedEventType,
Reduce: p.reducePushed,
},
},
},
} }
} }
func (p *milestoneProjection) reduceInstanceAdded(event eventstore.Event) (*handler.Statement, error) { func (p *milestoneProjection) reduceInstanceAdded(event eventstore.Event) (*handler.Statement, error) {
printEvent(event)
e, ok := event.(*instance.InstanceAddedEvent) e, ok := event.(*instance.InstanceAddedEvent)
if !ok { if !ok {
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-JbHGS", "reduce.wrong.event.type %s", instance.InstanceAddedEventType) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-JbHGS", "reduce.wrong.event.type %s", instance.InstanceAddedEventType)
} }
allTypes := milestone.PushedEventTypes() allTypes := milestone.AllTypes()
statements := make([]func(eventstore.Event) crdb.Exec, 0, len(allTypes)) statements := make([]func(eventstore.Event) crdb.Exec, 0, len(allTypes))
for _, ms := range allTypes { for _, msType := range allTypes {
createColumns := []handler.Column{ createColumns := []handler.Column{
handler.NewCol(MilestoneColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(MilestoneColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(MilestoneColumnMilestoneType, ms), handler.NewCol(MilestoneColumnType, msType),
} }
if ms == milestone.PushedInstanceCreatedEventType { if msType == milestone.InstanceCreated {
createColumns = append(createColumns, handler.NewCol(MilestoneColumnReachedDate, event.CreationDate())) createColumns = append(createColumns, handler.NewCol(MilestoneColumnReachedDate, event.CreationDate()))
} }
statements = append(statements, crdb.AddCreateStatement(createColumns)) statements = append(statements, crdb.AddCreateStatement(createColumns))
@ -116,21 +124,20 @@ func (p *milestoneProjection) reduceInstanceAdded(event eventstore.Event) (*hand
} }
func (p *milestoneProjection) reduceInstanceDomainPrimarySet(event eventstore.Event) (*handler.Statement, error) { func (p *milestoneProjection) reduceInstanceDomainPrimarySet(event eventstore.Event) (*handler.Statement, error) {
printEvent(event)
e, ok := event.(*instance.DomainPrimarySetEvent) e, ok := event.(*instance.DomainPrimarySetEvent)
if !ok { if !ok {
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sfrgf", "reduce.wrong.event.type %s", instance.InstanceDomainPrimarySetEventType) return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-Sfrgf", "reduce.wrong.event.type %s", instance.InstanceDomainPrimarySetEventType)
} }
allTypes := milestone.PushedEventTypes() allTypes := milestone.AllTypes()
statements := make([]func(eventstore.Event) crdb.Exec, 0, len(allTypes)) statements := make([]func(eventstore.Event) crdb.Exec, 0, len(allTypes))
for _, ms := range allTypes { for _, msType := range allTypes {
statements = append(statements, crdb.AddUpdateStatement( statements = append(statements, crdb.AddUpdateStatement(
[]handler.Column{ []handler.Column{
handler.NewCol(MilestoneColumnPrimaryDomain, e.Domain), handler.NewCol(MilestoneColumnPrimaryDomain, e.Domain),
}, },
[]handler.Condition{ []handler.Condition{
handler.NewCond(MilestoneColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(MilestoneColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCond(MilestoneColumnMilestoneType, ms), handler.NewCond(MilestoneColumnType, msType),
crdb.NewIsNullCond(MilestoneColumnPushedDate), crdb.NewIsNullCond(MilestoneColumnPushedDate),
}, },
)) ))
@ -138,9 +145,8 @@ func (p *milestoneProjection) reduceInstanceDomainPrimarySet(event eventstore.Ev
return crdb.NewMultiStatement(e, statements...), nil return crdb.NewMultiStatement(e, statements...), nil
} }
func (p *milestoneProjection) milestoneReached(eventType milestone.PushedEventType) func(event eventstore.Event) (*handler.Statement, error) { func (p *milestoneProjection) milestoneReached(msType milestone.Type) func(event eventstore.Event) (*handler.Statement, error) {
return func(event eventstore.Event) (*handler.Statement, error) { return func(event eventstore.Event) (*handler.Statement, error) {
printEvent(event)
if event.EditorUser() == "" || event.EditorService() == "" { if event.EditorUser() == "" || event.EditorService() == "" {
return crdb.NewNoOpStatement(event), nil return crdb.NewNoOpStatement(event), nil
} }
@ -151,7 +157,7 @@ func (p *milestoneProjection) milestoneReached(eventType milestone.PushedEventTy
}, },
[]handler.Condition{ []handler.Condition{
handler.NewCond(MilestoneColumnInstanceID, event.Aggregate().InstanceID), handler.NewCond(MilestoneColumnInstanceID, event.Aggregate().InstanceID),
handler.NewCond(MilestoneColumnMilestoneType, eventType), handler.NewCond(MilestoneColumnType, msType),
crdb.NewIsNullCond(MilestoneColumnReachedDate), crdb.NewIsNullCond(MilestoneColumnReachedDate),
crdb.NewIsNullCond(MilestoneColumnPushedDate), crdb.NewIsNullCond(MilestoneColumnPushedDate),
}, },
@ -159,13 +165,25 @@ func (p *milestoneProjection) milestoneReached(eventType milestone.PushedEventTy
} }
} }
func (p *milestoneProjection) reduceUserTokenAdded(event eventstore.Event) (*handler.Statement, error) { func (p *milestoneProjection) reducePushed(event eventstore.Event) (*handler.Statement, error) {
printEvent(event) printEvent(event)
return crdb.NewNoOpStatement(event), nil e, ok := event.(*milestone.PushedEvent)
if !ok {
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-XJGXK", "reduce.wrong.event.type %s", milestone.PushedEventType)
}
return crdb.NewUpdateStatement(
event,
[]handler.Column{
handler.NewCol(MilestoneColumnPushedDate, event.CreationDate()),
},
[]handler.Condition{
handler.NewCond(MilestoneColumnInstanceID, event.Aggregate().InstanceID),
handler.NewCond(MilestoneColumnType, e.MilestoneType),
},
), nil
} }
func (p *milestoneProjection) reduceInstanceRemoved(event eventstore.Event) (*handler.Statement, error) { func (p *milestoneProjection) reduceUserTokenAdded(event eventstore.Event) (*handler.Statement, error) {
printEvent(event)
return crdb.NewNoOpStatement(event), nil return crdb.NewNoOpStatement(event), nil
} }

View File

@ -2,215 +2,49 @@ package milestone
import ( import (
"context" "context"
"fmt"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
) )
type PushedEventType eventstore.EventType
const ( const (
eventTypePrefix = PushedEventType("milestone.pushed.") eventTypePrefix = eventstore.EventType("milestone.")
PushedInstanceCreatedEventType = eventTypePrefix + "instance.created" PushedEventType = eventTypePrefix + "pushed"
PushedAuthenticationSucceededOnInstanceEventType = eventTypePrefix + "instance.authentication.succeeded"
PushedProjectCreatedEventType = eventTypePrefix + "project.created"
PushedApplicationCreatedEventType = eventTypePrefix + "application.created"
PushedAuthenticationSucceededOnApplicationEventType = eventTypePrefix + "application.authentication.succeeded"
PushedInstanceDeletedEventType = eventTypePrefix + "instance.deleted"
) )
func PushedEventTypes() []PushedEventType { type PushedEvent struct {
return []PushedEventType{ *eventstore.BaseEvent `json:"-"`
PushedInstanceCreatedEventType, MilestoneType Type `json:"type"`
PushedAuthenticationSucceededOnInstanceEventType, PrimaryDomain string `json:"primaryDomain"`
PushedProjectCreatedEventType, Endpoints []string `json:"endpoints"`
PushedApplicationCreatedEventType,
PushedAuthenticationSucceededOnApplicationEventType,
PushedInstanceDeletedEventType,
}
} }
type PushedEvent interface { func (p *PushedEvent) Data() interface{} {
eventstore.Command return p
IsMilestoneEvent()
} }
type basePushedEvent struct { func (p *PushedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
eventstore.BaseEvent `json:"-"`
PrimaryDomain string `json:"primaryDomain"`
Endpoints []string `json:"endpoints"`
}
func (b *basePushedEvent) Data() interface{} {
return b
}
func (b *basePushedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil return nil
} }
func (b *basePushedEvent) SetBaseEvent(base *eventstore.BaseEvent) { func (p *PushedEvent) SetBaseEvent(b *eventstore.BaseEvent) {
b.BaseEvent = *base p.BaseEvent = b
} }
func NewPushedEventByType( func NewPushedEvent(
ctx context.Context,
eventType PushedEventType,
aggregate *Aggregate,
endpoints []string,
primaryDomain string,
) (PushedEvent, error) {
switch eventType {
case PushedInstanceCreatedEventType:
return NewInstanceCreatedPushedEvent(ctx, aggregate, endpoints, primaryDomain), nil
case PushedAuthenticationSucceededOnInstanceEventType:
return NewAuthenticationSucceededOnInstancePushedEvent(ctx, aggregate, endpoints, primaryDomain), nil
case PushedProjectCreatedEventType:
return NewProjectCreatedPushedEvent(ctx, aggregate, endpoints, primaryDomain), nil
case PushedApplicationCreatedEventType:
return NewApplicationCreatedPushedEvent(ctx, aggregate, endpoints, primaryDomain), nil
case PushedAuthenticationSucceededOnApplicationEventType:
return NewAuthenticationSucceededOnApplicationPushedEvent(ctx, aggregate, endpoints, primaryDomain), nil
case PushedInstanceDeletedEventType:
return NewInstanceDeletedPushedEvent(ctx, aggregate, endpoints, primaryDomain), nil
}
return nil, fmt.Errorf("unknown event type %s", eventType)
}
type InstanceCreatedPushedEvent struct{ basePushedEvent }
func (e *InstanceCreatedPushedEvent) IsMilestoneEvent() {}
func NewInstanceCreatedPushedEvent(
ctx context.Context, ctx context.Context,
aggregate *Aggregate, aggregate *Aggregate,
msType Type,
endpoints []string, endpoints []string,
primaryDomain string, primaryDomain string,
) *InstanceCreatedPushedEvent { ) *PushedEvent {
return &InstanceCreatedPushedEvent{ return &PushedEvent{
basePushedEvent: basePushedEvent{ BaseEvent: eventstore.NewBaseEventForPush(
BaseEvent: *eventstore.NewBaseEventForPush( ctx,
ctx, &aggregate.Aggregate,
&aggregate.Aggregate, PushedEventType,
eventstore.EventType(PushedInstanceCreatedEventType), ),
), MilestoneType: msType,
Endpoints: endpoints, Endpoints: endpoints,
PrimaryDomain: primaryDomain, PrimaryDomain: primaryDomain,
},
}
}
type AuthenticationSucceededOnInstancePushedEvent struct{ basePushedEvent }
func (e *AuthenticationSucceededOnInstancePushedEvent) IsMilestoneEvent() {}
func NewAuthenticationSucceededOnInstancePushedEvent(
ctx context.Context,
aggregate *Aggregate,
endpoints []string,
primaryDomain string,
) *AuthenticationSucceededOnInstancePushedEvent {
return &AuthenticationSucceededOnInstancePushedEvent{
basePushedEvent: basePushedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
&aggregate.Aggregate,
eventstore.EventType(PushedAuthenticationSucceededOnInstanceEventType),
),
Endpoints: endpoints,
PrimaryDomain: primaryDomain,
},
}
}
type ProjectCreatedPushedEvent struct{ basePushedEvent }
func (e *ProjectCreatedPushedEvent) IsMilestoneEvent() {}
func NewProjectCreatedPushedEvent(
ctx context.Context,
aggregate *Aggregate,
endpoints []string,
primaryDomain string,
) *ProjectCreatedPushedEvent {
return &ProjectCreatedPushedEvent{
basePushedEvent: basePushedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
&aggregate.Aggregate,
eventstore.EventType(PushedProjectCreatedEventType),
),
Endpoints: endpoints,
PrimaryDomain: primaryDomain,
},
}
}
type ApplicationCreatedPushedEvent struct{ basePushedEvent }
func (e *ApplicationCreatedPushedEvent) IsMilestoneEvent() {}
func NewApplicationCreatedPushedEvent(
ctx context.Context,
aggregate *Aggregate,
endpoints []string,
primaryDomain string,
) *ApplicationCreatedPushedEvent {
return &ApplicationCreatedPushedEvent{
basePushedEvent: basePushedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
&aggregate.Aggregate,
eventstore.EventType(PushedApplicationCreatedEventType),
),
Endpoints: endpoints,
PrimaryDomain: primaryDomain,
},
}
}
type AuthenticationSucceededOnApplicationPushedEvent struct{ basePushedEvent }
func (e *AuthenticationSucceededOnApplicationPushedEvent) IsMilestoneEvent() {}
func NewAuthenticationSucceededOnApplicationPushedEvent(
ctx context.Context,
aggregate *Aggregate,
endpoints []string,
primaryDomain string,
) *AuthenticationSucceededOnApplicationPushedEvent {
return &AuthenticationSucceededOnApplicationPushedEvent{
basePushedEvent: basePushedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
&aggregate.Aggregate,
eventstore.EventType(PushedAuthenticationSucceededOnApplicationEventType),
),
Endpoints: endpoints,
PrimaryDomain: primaryDomain,
},
}
}
type InstanceDeletedPushedEvent struct{ basePushedEvent }
func (e *InstanceDeletedPushedEvent) IsMilestoneEvent() {}
func NewInstanceDeletedPushedEvent(
ctx context.Context,
aggregate *Aggregate,
endpoints []string,
primaryDomain string,
) *InstanceDeletedPushedEvent {
return &InstanceDeletedPushedEvent{
basePushedEvent: basePushedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
&aggregate.Aggregate,
eventstore.EventType(PushedInstanceDeletedEventType),
),
Endpoints: endpoints,
PrimaryDomain: primaryDomain,
},
} }
} }

View File

@ -5,10 +5,5 @@ import (
) )
func RegisterEventMappers(es *eventstore.Eventstore) { func RegisterEventMappers(es *eventstore.Eventstore) {
es.RegisterFilterEventMapper(AggregateType, eventstore.EventType(PushedProjectCreatedEventType), eventstore.GenericEventMapper[InstanceCreatedPushedEvent]). es.RegisterFilterEventMapper(AggregateType, PushedEventType, eventstore.GenericEventMapper[PushedEvent])
RegisterFilterEventMapper(AggregateType, eventstore.EventType(PushedAuthenticationSucceededOnInstanceEventType), eventstore.GenericEventMapper[AuthenticationSucceededOnInstancePushedEvent]).
RegisterFilterEventMapper(AggregateType, eventstore.EventType(PushedProjectCreatedEventType), eventstore.GenericEventMapper[ProjectCreatedPushedEvent]).
RegisterFilterEventMapper(AggregateType, eventstore.EventType(PushedApplicationCreatedEventType), eventstore.GenericEventMapper[ApplicationCreatedPushedEvent]).
RegisterFilterEventMapper(AggregateType, eventstore.EventType(PushedAuthenticationSucceededOnApplicationEventType), eventstore.GenericEventMapper[AuthenticationSucceededOnApplicationPushedEvent]).
RegisterFilterEventMapper(AggregateType, eventstore.EventType(PushedInstanceDeletedEventType), eventstore.GenericEventMapper[InstanceDeletedPushedEvent])
} }

View File

@ -0,0 +1,26 @@
//go:generate stringer -type Type
package milestone
type Type int
const (
unknown Type = iota
InstanceCreated
AuthenticationSucceededOnInstance
ProjectCreated
ApplicationCreated
AuthenticationSucceededOnApplication
InstanceDeleted
typesCount
)
func AllTypes() []Type {
types := make([]Type, typesCount-1)
for i := Type(1); i < typesCount; i++ {
types[i-1] = i
}
return types
}

View File

@ -0,0 +1,30 @@
// Code generated by "stringer -type Type"; DO NOT EDIT.
package milestone
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[unknown-0]
_ = x[InstanceCreated-1]
_ = x[AuthenticationSucceededOnInstance-2]
_ = x[ProjectCreated-3]
_ = x[ApplicationCreated-4]
_ = x[AuthenticationSucceededOnApplication-5]
_ = x[InstanceDeleted-6]
_ = x[typesCount-7]
}
const _Type_name = "unknownInstanceCreatedAuthenticationSucceededOnInstanceProjectCreatedApplicationCreatedAuthenticationSucceededOnApplicationInstanceDeletedtypesCount"
var _Type_index = [...]uint8{0, 7, 22, 55, 69, 87, 123, 138, 148}
func (i Type) String() string {
if i < 0 || i >= Type(len(_Type_index)-1) {
return "Type(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Type_name[_Type_index[i]:_Type_index[i+1]]
}

View File

@ -1,5 +1,21 @@
package pseudo package pseudo
import "github.com/zitadel/zitadel/internal/eventstore"
const ( const (
AggregateType = "pseudo" AggregateType = "pseudo"
AggregateVersion = "v1"
) )
type Aggregate struct {
eventstore.Aggregate
}
func NewAggregate() *Aggregate {
return &Aggregate{
Aggregate: eventstore.Aggregate{
Type: AggregateType,
Version: AggregateVersion,
},
}
}

View File

@ -1,6 +1,7 @@
package pseudo package pseudo
import ( import (
"context"
"time" "time"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
@ -8,57 +9,28 @@ import (
const ( const (
eventTypePrefix = eventstore.EventType("pseudo.") eventTypePrefix = eventstore.EventType("pseudo.")
TimestampEventType = eventTypePrefix + "timestamp" ScheduledEventType = eventTypePrefix + "timestamp"
) )
var _ eventstore.Event = (*TimestampEvent)(nil) var _ eventstore.Event = (*ScheduledEvent)(nil)
type TimestampEvent struct { type ScheduledEvent struct {
Timestamp time.Time *eventstore.BaseEvent `json:"-"`
InstanceIDs []string Timestamp time.Time `json:"-"`
InstanceIDs []string `json:"-"`
} }
func (t TimestampEvent) Aggregate() eventstore.Aggregate { func NewScheduledEvent(
panic("TimestampEvent is not a real event") ctx context.Context,
}
func (t TimestampEvent) EditorService() string {
panic("TimestampEvent is not a real event")
}
func (t TimestampEvent) EditorUser() string {
panic("TimestampEvent is not a real event")
}
func (t TimestampEvent) Type() eventstore.EventType {
panic("TimestampEvent is not a real event")
}
func (t TimestampEvent) Sequence() uint64 {
panic("TimestampEvent is not a real event")
}
func (t TimestampEvent) CreationDate() time.Time {
panic("TimestampEvent is not a real event")
}
func (t TimestampEvent) PreviousAggregateSequence() uint64 {
panic("TimestampEvent is not a real event")
}
func (t TimestampEvent) PreviousAggregateTypeSequence() uint64 {
panic("TimestampEvent is not a real event")
}
func (t TimestampEvent) DataAsBytes() []byte {
panic("TimestampEvent is not a real event")
}
func NewTimestampEvent(
timestamp time.Time, timestamp time.Time,
instanceIDs ...string, instanceIDs ...string,
) *TimestampEvent { ) *ScheduledEvent {
return &TimestampEvent{ return &ScheduledEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
&NewAggregate().Aggregate,
ScheduledEventType,
),
Timestamp: timestamp, Timestamp: timestamp,
InstanceIDs: instanceIDs, InstanceIDs: instanceIDs,
} }