mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:37:32 +00:00
feat(eventstore): increase parallel write capabilities (#5940)
This implementation increases parallel write capabilities of the eventstore. Please have a look at the technical advisories: [05](https://zitadel.com/docs/support/advisory/a10005) and [06](https://zitadel.com/docs/support/advisory/a10006). The implementation of eventstore.push is rewritten and stored events are migrated to a new table `eventstore.events2`. If you are using cockroach: make sure that the database user of ZITADEL has `VIEWACTIVITY` grant. This is used to query events.
This commit is contained in:
@@ -78,13 +78,17 @@ HTTP1HostHeader: "host" # ZITADEL_HTTP1HOSTHEADER
|
||||
WebAuthNName: ZITADEL # ZITADEL_WEBAUTHN_NAME
|
||||
|
||||
Database:
|
||||
# This setting defines the ratio of how many connections defined below
|
||||
# are used to push events. ZITADEL manages two database connection pools
|
||||
# one to push events and one for the remaining queries.
|
||||
EventPushConnRatio: 0.2 # ZITADEL_DATABASE_COCKROACH_EVENTPUSHCONNRATIO
|
||||
# CockroachDB is the default database of ZITADEL
|
||||
cockroach:
|
||||
Host: localhost # ZITADEL_DATABASE_COCKROACH_HOST
|
||||
Port: 26257 # ZITADEL_DATABASE_COCKROACH_PORT
|
||||
Database: zitadel # ZITADEL_DATABASE_COCKROACH_DATABASE
|
||||
MaxOpenConns: 20 # ZITADEL_DATABASE_COCKROACH_MAXOPENCONNS
|
||||
MaxIdleConns: 10 # ZITADEL_DATABASE_COCKROACH_MAXIDLECONNS
|
||||
MaxOpenConns: 40 # ZITADEL_DATABASE_COCKROACH_MAXOPENCONNS
|
||||
MaxIdleConns: 20 # ZITADEL_DATABASE_COCKROACH_MAXIDLECONNS
|
||||
MaxConnLifetime: 30m # ZITADEL_DATABASE_COCKROACH_MAXCONNLIFETIME
|
||||
MaxConnIdleTime: 5m # ZITADEL_DATABASE_COCKROACH_MAXCONNIDLETIME
|
||||
Options: "" # ZITADEL_DATABASE_COCKROACH_OPTIONS
|
||||
@@ -177,14 +181,16 @@ AssetStorage:
|
||||
|
||||
# The Projections section defines the behavior for the scheduled and synchronous events projections.
|
||||
Projections:
|
||||
# The maximum duration a transaction remains open
|
||||
# before it spots left folding additional events
|
||||
# and updates the table.
|
||||
TransactionDuration: 500ms # ZITADEL_PROJECTIONS_TRANSACTIONDURATION
|
||||
# Time interval between scheduled projections
|
||||
RequeueEvery: 60s # ZITADEL_PROJECTIONS_REQUEUEEVERY
|
||||
# Time between retried database statements resulting from projected events
|
||||
RetryFailedAfter: 1s # ZITADEL_PROJECTIONS_RETRYFAILED
|
||||
# Retried execution number of database statements resulting from projected events
|
||||
MaxFailureCount: 5 # ZITADEL_PROJECTIONS_MAXFAILURECOUNT
|
||||
# Number of concurrent projection routines. Values of 0 and below are overwritten to 1
|
||||
ConcurrentInstances: 1 # ZITADEL_PROJECTIONS_CONCURRENTINSTANCES
|
||||
# Limit of returned events per query
|
||||
BulkLimit: 200 # ZITADEL_PROJECTIONS_BULKLIMIT
|
||||
# Only instances are projected, for which at least a projection-relevant event exists within the timeframe
|
||||
@@ -194,11 +200,17 @@ Projections:
|
||||
# In the Customizations section, all settings from above can be overwritten for each specific projection
|
||||
Customizations:
|
||||
Projects:
|
||||
BulkLimit: 2000 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_PROJECTS_BULKLIMIT
|
||||
TransactionDuration: 2s
|
||||
# The Notifications projection is used for sending emails and SMS to users
|
||||
Notifications:
|
||||
# As notification projections don't result in database statements, retries don't have an effect
|
||||
MaxFailureCount: 0 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONS_MAXFAILURECOUNT
|
||||
MaxFailureCount: 10 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONS_MAXFAILURECOUNT
|
||||
# Sending emails can take longer than 500ms
|
||||
TransactionDuration: 5s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONS_TRANSACTIONDURATION
|
||||
password_complexities:
|
||||
TransactionDuration: 2s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_PASSWORD_COMPLEXITIES_TRANSACTIONDURATION
|
||||
lockout_policy:
|
||||
TransactionDuration: 2s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_LOCKOUT_POLICY_TRANSACTIONDURATION
|
||||
# The NotificationsQuotas projection is used for calling quota webhooks
|
||||
NotificationsQuotas:
|
||||
# In case of failed deliveries, ZITADEL retries to send the data points to the configured endpoints, but only for active instances.
|
||||
@@ -207,9 +219,13 @@ Projections:
|
||||
# Defaults to 45 days
|
||||
HandleActiveInstances: 1080h # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONSQUOTAS_HANDLEACTIVEINSTANCES
|
||||
# As quota notification projections don't result in database statements, retries don't have an effect
|
||||
MaxFailureCount: 0 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONSQUOTAS_MAXFAILURECOUNT
|
||||
MaxFailureCount: 10 # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONSQUOTAS_MAXFAILURECOUNT
|
||||
# Quota notifications are not so time critical. Setting RequeueEvery every five minutes doesn't annoy the db too much.
|
||||
RequeueEvery: 300s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONSQUOTAS_REQUEUEEVERY
|
||||
# Sending emails can take longer than 500ms
|
||||
TransactionDuration: 5s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_NOTIFICATIONQUOTAS_TRANSACTIONDURATION
|
||||
milestones:
|
||||
BulkLimit: 50
|
||||
# The Telemetry projection is used for calling telemetry webhooks
|
||||
Telemetry:
|
||||
# In case of failed deliveries, ZITADEL retries to send the data points to the configured endpoints, but only for active instances.
|
||||
@@ -223,20 +239,34 @@ Projections:
|
||||
RequeueEvery: 3300s # ZITADEL_PROJECTIONS_CUSTOMIZATIONS_TELEMETRY_REQUEUEEVERY
|
||||
|
||||
Auth:
|
||||
# See Projections.BulkLimit
|
||||
SearchLimit: 1000 # ZITADEL_AUTH_SEARCHLIMIT
|
||||
Spooler:
|
||||
ConcurrentWorkers: 1 # ZITADEL_AUTH_SPOOLER_CONCURRENTWORKERS
|
||||
ConcurrentInstances: 1 # ZITADEL_AUTH_SPOOLER_CONCURRENTINSTANCES
|
||||
BulkLimit: 10000 # ZITADEL_AUTH_SPOOLER_BULKLIMIT
|
||||
FailureCountUntilSkip: 5 # ZITADEL_AUTH_SPOOLER_FAILURECOUNTUNTILSKIP
|
||||
# See Projections.TransationDuration
|
||||
TransactionDuration: 10s #ZITADEL_AUTH_SPOOLER_TRANSACTIONDURATION
|
||||
# See Projections.BulkLimit
|
||||
BulkLimit: 100 #ZITADEL_AUTH_SPOOLER_BULKLIMIT
|
||||
# See Projections.MaxFailureCount
|
||||
FailureCountUntilSkip: 5 #ZITADEL_AUTH_SPOOLER_FAILURECOUNTUNTILSKIP
|
||||
# Only instance are projected, for which at least a projection relevant event exists withing the timeframe
|
||||
# from HandleActiveInstances duration in the past until the projections current time
|
||||
# Defaults to twice the RequeueEvery duration
|
||||
HandleActiveInstances: 120s #ZITADEL_AUTH_SPOOLER_HANDLEACTIVEINSTANCES
|
||||
|
||||
Admin:
|
||||
# See Projections.BulkLimit
|
||||
SearchLimit: 1000 # ZITADEL_ADMIN_SEARCHLIMIT
|
||||
Spooler:
|
||||
ConcurrentWorkers: 1 # ZITADEL_ADMIN_SPOOLER_CONCURRENTWORKERS
|
||||
ConcurrentInstances: 1 # ZITADEL_ADMIN_SPOOLER_CONCURRENTINSTANCES
|
||||
BulkLimit: 10000 # ZITADEL_ADMIN_SPOOLER_BULKLIMIT
|
||||
FailureCountUntilSkip: 5 # ZITADEL_ADMIN_SPOOLER_FAILURECOUNTUNTILSKIP
|
||||
# See Projections.TransationDuration
|
||||
TransactionDuration: 10s
|
||||
# See Projections.BulkLimit
|
||||
BulkLimit: 200
|
||||
# See Projections.MaxFailureCount
|
||||
FailureCountUntilSkip: 5
|
||||
# Only instance are projected, for which at least a projection relevant event exists withing the timeframe
|
||||
# from HandleActiveInstances duration in the past until the projections current time
|
||||
# Defaults to twice the RequeueEvery duration
|
||||
HandleActiveInstances: 120s
|
||||
|
||||
UserAgentCookie:
|
||||
Name: zitadel.useragent # ZITADEL_USERAGENTCOOKIE_NAME
|
||||
@@ -322,10 +352,12 @@ Console:
|
||||
Notification:
|
||||
Repository:
|
||||
Spooler:
|
||||
ConcurrentWorkers: 1 # ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_CONCURRENTWORKERS
|
||||
ConcurrentInstances: 10 # ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_CONCURRENTINSTANCES
|
||||
BulkLimit: 10000 # ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_BULKLIMIT
|
||||
FailureCountUntilSkip: 5 # ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_FAILURECOUNTUNTILSKIP
|
||||
# See Projections.TransactionDuration
|
||||
TransactionDuration: 10s #ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_TRANSACTIONDURATION
|
||||
# See Projections.BulkLimit
|
||||
BulkLimit: 200 #ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_BULKLIMIT
|
||||
# See Projections.MaxFailureCount
|
||||
FailureCountUntilSkip: 5 #ZITADEL_NOTIFICATION_REPOSITORY_SPOOLER_FAILURECOUNTUNTILSKIP
|
||||
Handlers:
|
||||
|
||||
EncryptionKeys:
|
||||
@@ -477,8 +509,8 @@ Quotas:
|
||||
MaxBulkSize: 0 # ZITADEL_QUOTAS_EXECUTION_DEBOUNCE_MAXBULKSIZE
|
||||
|
||||
Eventstore:
|
||||
PushTimeout: 15s # ZITADEL_EVENTSTORE_PUSHTIMEOUT
|
||||
AllowOrderByCreationDate: false # ZITADEL_EVENTSTORE_ALLOWORDERBYCREATIONDATE
|
||||
# Sets the maximum duration of transactions pushing events
|
||||
PushTimeout: 15s #ZITADEL_EVENTSTORE_PUSHTIMEOUT
|
||||
|
||||
DefaultInstance:
|
||||
InstanceName: ZITADEL # ZITADEL_DEFAULTINSTANCE_INSTANCENAME
|
||||
|
@@ -1,13 +1,13 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgconn"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
func exec(db *sql.DB, stmt string, possibleErrCodes []string, args ...interface{}) error {
|
||||
func exec(db *database.DB, stmt string, possibleErrCodes []string, args ...interface{}) error {
|
||||
_, err := db.Exec(stmt, args...)
|
||||
pgErr := new(pgconn.PgError)
|
||||
if errors.As(err, &pgErr) {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -68,7 +67,7 @@ func InitAll(config *Config) {
|
||||
logging.OnError(err).Fatal("unable to initialize ZITADEL")
|
||||
}
|
||||
|
||||
func initialise(config database.Config, steps ...func(*sql.DB) error) error {
|
||||
func initialise(config database.Config, steps ...func(*database.DB) error) error {
|
||||
logging.Info("initialization started")
|
||||
|
||||
err := ReadStmts(config.Type())
|
||||
@@ -76,16 +75,16 @@ func initialise(config database.Config, steps ...func(*sql.DB) error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
db, err := database.Connect(config, true)
|
||||
db, err := database.Connect(config, true, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
return Init(db.DB, steps...)
|
||||
return Init(db, steps...)
|
||||
}
|
||||
|
||||
func Init(db *sql.DB, steps ...func(*sql.DB) error) error {
|
||||
func Init(db *database.DB, steps ...func(*database.DB) error) error {
|
||||
for _, step := range steps {
|
||||
if err := step(db); err != nil {
|
||||
return err
|
||||
|
@@ -1,17 +1,17 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
type db struct {
|
||||
mock sqlmock.Sqlmock
|
||||
db *sql.DB
|
||||
db *database.DB
|
||||
}
|
||||
|
||||
func prepareDB(t *testing.T, expectations ...expectation) db {
|
||||
@@ -25,7 +25,7 @@ func prepareDB(t *testing.T, expectations ...expectation) db {
|
||||
}
|
||||
return db{
|
||||
mock: mock,
|
||||
db: client,
|
||||
db: &database.DB{DB: client},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,20 @@ func expectExec(stmt string, err error, args ...driver.Value) expectation {
|
||||
}
|
||||
}
|
||||
|
||||
func expectQuery(stmt string, err error, columns []string, rows [][]driver.Value, args ...driver.Value) expectation {
|
||||
return func(m sqlmock.Sqlmock) {
|
||||
res := sqlmock.NewRows(columns)
|
||||
for _, row := range rows {
|
||||
res.AddRow(row...)
|
||||
}
|
||||
query := m.ExpectQuery(regexp.QuoteMeta(stmt)).WithArgs(args...).WillReturnRows(res)
|
||||
if err != nil {
|
||||
query.WillReturnError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expectBegin(err error) expectation {
|
||||
return func(m sqlmock.Sqlmock) {
|
||||
query := m.ExpectBegin()
|
||||
|
@@ -1,3 +1,4 @@
|
||||
-- replace the first %[1]s with the database
|
||||
-- replace the second \%[2]s with the user
|
||||
GRANT ALL ON DATABASE %[1]s TO %[2]s
|
||||
GRANT ALL ON DATABASE %[1]s TO %[2]s;
|
||||
GRANT SYSTEM VIEWACTIVITY TO %[2]s;
|
@@ -1,27 +1,21 @@
|
||||
SET experimental_enable_hash_sharded_indexes = on;
|
||||
CREATE TABLE IF NOT EXISTS eventstore.events2 (
|
||||
instance_id TEXT NOT NULL
|
||||
, aggregate_type TEXT NOT NULL
|
||||
, aggregate_id TEXT NOT NULL
|
||||
|
||||
, event_type TEXT NOT NULL
|
||||
, "sequence" BIGINT NOT NULL
|
||||
, revision SMALLINT NOT NULL
|
||||
, created_at TIMESTAMPTZ NOT NULL
|
||||
, payload JSONB
|
||||
, creator TEXT NOT NULL
|
||||
, "owner" TEXT NOT NULL
|
||||
|
||||
, "position" DECIMAL NOT NULL
|
||||
, in_tx_order INTEGER NOT NULL
|
||||
|
||||
CREATE TABLE IF NOT EXISTS eventstore.events (
|
||||
id UUID DEFAULT gen_random_uuid()
|
||||
, event_type TEXT NOT NULL
|
||||
, aggregate_type TEXT NOT NULL
|
||||
, aggregate_id TEXT NOT NULL
|
||||
, aggregate_version TEXT NOT NULL
|
||||
, event_sequence BIGINT NOT NULL
|
||||
, previous_aggregate_sequence BIGINT
|
||||
, previous_aggregate_type_sequence INT8
|
||||
, creation_date TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
, event_data JSONB
|
||||
, editor_user TEXT NOT NULL
|
||||
, editor_service TEXT NOT NULL
|
||||
, resource_owner TEXT NOT NULL
|
||||
, instance_id TEXT NOT NULL
|
||||
|
||||
, PRIMARY KEY (event_sequence DESC, instance_id) USING HASH WITH BUCKET_COUNT = 10
|
||||
, INDEX agg_type_agg_id (aggregate_type, aggregate_id, instance_id)
|
||||
, INDEX agg_type (aggregate_type, instance_id)
|
||||
, INDEX agg_type_seq (aggregate_type, event_sequence DESC, instance_id)
|
||||
STORING (id, event_type, aggregate_id, aggregate_version, previous_aggregate_sequence, creation_date, event_data, editor_user, editor_service, resource_owner, previous_aggregate_type_sequence)
|
||||
, INDEX max_sequence (aggregate_type, aggregate_id, event_sequence DESC, instance_id)
|
||||
, CONSTRAINT previous_sequence_unique UNIQUE (previous_aggregate_sequence DESC, instance_id)
|
||||
, CONSTRAINT prev_agg_type_seq_unique UNIQUE(previous_aggregate_type_sequence, instance_id)
|
||||
);
|
||||
, PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence")
|
||||
, INDEX es_active_instances (created_at DESC) STORING ("position")
|
||||
, INDEX es_wm (aggregate_id, instance_id, aggregate_type, event_type)
|
||||
, INDEX es_projection (instance_id, aggregate_type, event_type, "position" DESC)
|
||||
);
|
||||
|
@@ -1,25 +1,22 @@
|
||||
CREATE TABLE IF NOT EXISTS eventstore.events (
|
||||
id UUID DEFAULT gen_random_uuid()
|
||||
, event_type TEXT NOT NULL
|
||||
, aggregate_type TEXT NOT NULL
|
||||
, aggregate_id TEXT NOT NULL
|
||||
, aggregate_version TEXT NOT NULL
|
||||
, event_sequence BIGINT NOT NULL
|
||||
, previous_aggregate_sequence BIGINT
|
||||
, previous_aggregate_type_sequence INT8
|
||||
, creation_date TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
, event_data JSONB
|
||||
, editor_user TEXT NOT NULL
|
||||
, editor_service TEXT NOT NULL
|
||||
, resource_owner TEXT NOT NULL
|
||||
, instance_id TEXT NOT NULL
|
||||
CREATE TABLE IF NOT EXISTS eventstore.events2 (
|
||||
instance_id TEXT NOT NULL
|
||||
, aggregate_type TEXT NOT NULL
|
||||
, aggregate_id TEXT NOT NULL
|
||||
|
||||
, event_type TEXT NOT NULL
|
||||
, "sequence" BIGINT NOT NULL
|
||||
, revision SMALLINT NOT NULL
|
||||
, created_at TIMESTAMPTZ NOT NULL
|
||||
, payload JSONB
|
||||
, creator TEXT NOT NULL
|
||||
, "owner" TEXT NOT NULL
|
||||
|
||||
, "position" DECIMAL NOT NULL
|
||||
, in_tx_order INTEGER NOT NULL
|
||||
|
||||
, PRIMARY KEY (event_sequence, instance_id)
|
||||
, CONSTRAINT previous_sequence_unique UNIQUE(previous_aggregate_sequence, instance_id)
|
||||
, CONSTRAINT prev_agg_type_seq_unique UNIQUE(previous_aggregate_type_sequence, instance_id)
|
||||
, PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence")
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS agg_type_agg_id ON eventstore.events (aggregate_type, aggregate_id, instance_id);
|
||||
CREATE INDEX IF NOT EXISTS agg_type ON eventstore.events (aggregate_type, instance_id);
|
||||
CREATE INDEX IF NOT EXISTS agg_type_seq ON eventstore.events (aggregate_type, event_sequence DESC, instance_id);
|
||||
CREATE INDEX IF NOT EXISTS max_sequence ON eventstore.events (aggregate_type, aggregate_id, event_sequence DESC, instance_id);
|
||||
CREATE INDEX IF NOT EXISTS es_active_instances ON eventstore.events2 (created_at DESC, instance_id);
|
||||
CREATE INDEX IF NOT EXISTS es_wm ON eventstore.events2 (aggregate_id, instance_id, aggregate_type, event_type);
|
||||
CREATE INDEX IF NOT EXISTS es_projection ON eventstore.events2 (instance_id, aggregate_type, event_type, "position");
|
@@ -1,13 +1,14 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
func newDatabase() *cobra.Command {
|
||||
@@ -33,8 +34,8 @@ The user provided by flags needs priviledge to
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyDatabase(databaseName string) func(*sql.DB) error {
|
||||
return func(db *sql.DB) error {
|
||||
func VerifyDatabase(databaseName string) func(*database.DB) error {
|
||||
return func(db *database.DB) error {
|
||||
logging.WithFields("database", databaseName).Info("verify database")
|
||||
|
||||
return exec(db, fmt.Sprintf(string(databaseStmt), databaseName), []string{dbAlreadyExistsCode})
|
||||
|
@@ -1,13 +1,14 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
func newGrant() *cobra.Command {
|
||||
@@ -28,8 +29,8 @@ Prereqesits:
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyGrant(databaseName, username string) func(*sql.DB) error {
|
||||
return func(db *sql.DB) error {
|
||||
func VerifyGrant(databaseName, username string) func(*database.DB) error {
|
||||
return func(db *database.DB) error {
|
||||
logging.WithFields("user", username, "database", databaseName).Info("verify grant")
|
||||
|
||||
return exec(db, fmt.Sprintf(grantStmt, databaseName, username), nil)
|
||||
|
@@ -1,13 +1,14 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
func newUser() *cobra.Command {
|
||||
@@ -33,8 +34,8 @@ The user provided by flags needs priviledge to
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyUser(username, password string) func(*sql.DB) error {
|
||||
return func(db *sql.DB) error {
|
||||
func VerifyUser(username, password string) func(*database.DB) error {
|
||||
return func(db *database.DB) error {
|
||||
logging.WithFields("username", username).Info("verify user")
|
||||
|
||||
if password != "" {
|
||||
|
@@ -1,14 +1,13 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
@@ -29,58 +28,66 @@ Prereqesits:
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyZitadel(db *sql.DB, config database.Config) error {
|
||||
func VerifyZitadel(db *database.DB, config database.Config) error {
|
||||
err := ReadStmts(config.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logging.WithFields().Info("verify system")
|
||||
if err := exec(db, fmt.Sprintf(createSystemStmt, config.Username()), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logging.WithFields().Info("verify encryption keys")
|
||||
if err := createEncryptionKeys(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logging.WithFields().Info("verify projections")
|
||||
if err := exec(db, fmt.Sprintf(createProjectionsStmt, config.Username()), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logging.WithFields().Info("verify eventstore")
|
||||
if err := exec(db, fmt.Sprintf(createEventstoreStmt, config.Username()), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logging.WithFields().Info("verify events tables")
|
||||
if err := createEvents(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logging.WithFields().Info("verify system sequence")
|
||||
if err := exec(db, createSystemSequenceStmt, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logging.WithFields().Info("verify unique constraints")
|
||||
if err := exec(db, createUniqueConstraints, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyZitadel(config database.Config) error {
|
||||
logging.WithFields("database", config.DatabaseName()).Info("verify zitadel")
|
||||
|
||||
db, err := database.Connect(config, false)
|
||||
db, err := database.Connect(config, false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifyZitadel(db.DB, config); err != nil {
|
||||
if err := VerifyZitadel(db, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.Close()
|
||||
}
|
||||
|
||||
func createEncryptionKeys(db *sql.DB) error {
|
||||
func createEncryptionKeys(db *database.DB) error {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -93,16 +100,29 @@ func createEncryptionKeys(db *sql.DB) error {
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func createEvents(db *sql.DB) error {
|
||||
func createEvents(db *database.DB) (err error) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
rollbackErr := tx.Rollback()
|
||||
logging.OnError(rollbackErr).Debug("rollback failed")
|
||||
return
|
||||
}
|
||||
err = tx.Commit()
|
||||
}()
|
||||
|
||||
if _, err = tx.Exec(createEventsStmt); err != nil {
|
||||
tx.Rollback()
|
||||
// if events already exists events2 is created during a setup job
|
||||
var count int
|
||||
row := tx.QueryRow("SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'")
|
||||
if err = row.Scan(&count); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
if row.Err() != nil || count >= 1 {
|
||||
return row.Err()
|
||||
}
|
||||
_, err = tx.Exec(createEventsStmt)
|
||||
return err
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package initialise
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
@@ -30,11 +31,53 @@ func Test_verifyEvents(t *testing.T) {
|
||||
},
|
||||
targetErr: sql.ErrConnDone,
|
||||
},
|
||||
{
|
||||
name: "events already exists",
|
||||
args: args{
|
||||
db: prepareDB(t,
|
||||
expectBegin(nil),
|
||||
expectQuery(
|
||||
"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'",
|
||||
nil,
|
||||
[]string{"count"},
|
||||
[][]driver.Value{
|
||||
{1},
|
||||
},
|
||||
),
|
||||
expectCommit(nil),
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "events and events2 already exists",
|
||||
args: args{
|
||||
db: prepareDB(t,
|
||||
expectBegin(nil),
|
||||
expectQuery(
|
||||
"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'",
|
||||
nil,
|
||||
[]string{"count"},
|
||||
[][]driver.Value{
|
||||
{2},
|
||||
},
|
||||
),
|
||||
expectCommit(nil),
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create table fails",
|
||||
args: args{
|
||||
db: prepareDB(t,
|
||||
expectBegin(nil),
|
||||
expectQuery(
|
||||
"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'",
|
||||
nil,
|
||||
[]string{"count"},
|
||||
[][]driver.Value{
|
||||
{0},
|
||||
},
|
||||
),
|
||||
expectExec(createEventsStmt, sql.ErrNoRows),
|
||||
expectRollback(nil),
|
||||
),
|
||||
@@ -46,6 +89,14 @@ func Test_verifyEvents(t *testing.T) {
|
||||
args: args{
|
||||
db: prepareDB(t,
|
||||
expectBegin(nil),
|
||||
expectQuery(
|
||||
"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events%'",
|
||||
nil,
|
||||
[]string{"count"},
|
||||
[][]driver.Value{
|
||||
{0},
|
||||
},
|
||||
),
|
||||
expectExec(createEventsStmt, nil),
|
||||
expectCommit(nil),
|
||||
),
|
||||
|
@@ -124,7 +124,7 @@ func openFile(fileName string) (io.Reader, error) {
|
||||
}
|
||||
|
||||
func keyStorage(config database.Config, masterKey string) (crypto.KeyStorage, error) {
|
||||
db, err := database.Connect(config, false)
|
||||
db, err := database.Connect(config, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ CREATE TABLE adminapi.locks (
|
||||
CREATE TABLE adminapi.current_sequences (
|
||||
view_name TEXT,
|
||||
current_sequence BIGINT,
|
||||
event_timestamp TIMESTAMPTZ,
|
||||
event_date TIMESTAMPTZ,
|
||||
last_successful_spooler_run TIMESTAMPTZ,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
|
@@ -12,7 +12,7 @@ CREATE TABLE auth.locks (
|
||||
CREATE TABLE auth.current_sequences (
|
||||
view_name TEXT,
|
||||
current_sequence BIGINT,
|
||||
event_timestamp TIMESTAMPTZ,
|
||||
event_date TIMESTAMPTZ,
|
||||
last_successful_spooler_run TIMESTAMPTZ,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
|
@@ -1,23 +0,0 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 04/cockroach/index.sql
|
||||
//go:embed 04/postgres/index.sql
|
||||
stmts04 embed.FS
|
||||
)
|
||||
|
||||
func New04(db *database.DB) *EventstoreIndexesNew {
|
||||
return &EventstoreIndexesNew{
|
||||
dbClient: db,
|
||||
name: "04_eventstore_indexes",
|
||||
step: "04",
|
||||
fileName: "index.sql",
|
||||
stmts: stmts04,
|
||||
}
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
CREATE INDEX IF NOT EXISTS write_model ON eventstore.events (instance_id, aggregate_type, aggregate_id, event_type, resource_owner)
|
||||
STORING (id, aggregate_version, previous_aggregate_sequence, creation_date, event_data, editor_user, editor_service, previous_aggregate_type_sequence);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS active_instances ON eventstore.events (creation_date desc, instance_id) USING HASH;
|
@@ -1,3 +0,0 @@
|
||||
CREATE INDEX IF NOT EXISTS write_model ON eventstore.events (instance_id, aggregate_type, aggregate_id, event_type, resource_owner);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS active_instances ON eventstore.events (creation_date, instance_id);
|
@@ -1,23 +0,0 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 09/cockroach/index.sql
|
||||
//go:embed 09/postgres/index.sql
|
||||
stmts09 embed.FS
|
||||
)
|
||||
|
||||
func New09(db *database.DB) *EventstoreIndexesNew {
|
||||
return &EventstoreIndexesNew{
|
||||
dbClient: db,
|
||||
name: "09_optimise_indexes",
|
||||
step: "09",
|
||||
fileName: "index.sql",
|
||||
stmts: stmts09,
|
||||
}
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
-- replace agg_type_agg_id
|
||||
BEGIN;
|
||||
DROP INDEX IF EXISTS eventstore.events@agg_type_agg_id;
|
||||
COMMIT;
|
||||
|
||||
BEGIN;
|
||||
CREATE INDEX agg_type_agg_id ON eventstore.events (
|
||||
instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
) STORING (
|
||||
event_type
|
||||
, aggregate_version
|
||||
, previous_aggregate_sequence
|
||||
, previous_aggregate_type_sequence
|
||||
, creation_date
|
||||
, event_data
|
||||
, editor_user
|
||||
, editor_service
|
||||
, resource_owner
|
||||
);
|
||||
COMMIT;
|
||||
|
||||
-- replace agg_type
|
||||
BEGIN;
|
||||
DROP INDEX IF EXISTS eventstore.events@agg_type;
|
||||
COMMIT;
|
||||
|
||||
BEGIN;
|
||||
CREATE INDEX agg_type ON eventstore.events (
|
||||
instance_id
|
||||
, aggregate_type
|
||||
, event_sequence
|
||||
) STORING (
|
||||
event_type
|
||||
, aggregate_id
|
||||
, aggregate_version
|
||||
, previous_aggregate_sequence
|
||||
, previous_aggregate_type_sequence
|
||||
, creation_date
|
||||
, event_data
|
||||
, editor_user
|
||||
, editor_service
|
||||
, resource_owner
|
||||
);
|
||||
COMMIT;
|
||||
|
||||
-- drop unused index
|
||||
BEGIN;
|
||||
DROP INDEX IF EXISTS eventstore.events@agg_type_seq;
|
||||
COMMIT;
|
@@ -1,30 +0,0 @@
|
||||
-- replace agg_type_agg_id
|
||||
BEGIN;
|
||||
DROP INDEX IF EXISTS eventstore.agg_type_agg_id;
|
||||
COMMIT;
|
||||
|
||||
BEGIN;
|
||||
CREATE INDEX agg_type_agg_id ON eventstore.events (
|
||||
instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
);
|
||||
COMMIT;
|
||||
|
||||
-- replace agg_type
|
||||
BEGIN;
|
||||
DROP INDEX IF EXISTS eventstore.agg_type;
|
||||
COMMIT;
|
||||
|
||||
BEGIN;
|
||||
CREATE INDEX agg_type ON eventstore.events (
|
||||
instance_id
|
||||
, aggregate_type
|
||||
, event_sequence
|
||||
);
|
||||
COMMIT;
|
||||
|
||||
-- drop unused index
|
||||
BEGIN;
|
||||
DROP INDEX IF EXISTS eventstore.agg_type_seq;
|
||||
COMMIT;
|
@@ -3,7 +3,7 @@ package setup
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"embed"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/cockroach-go/v2/crdb"
|
||||
@@ -17,8 +17,9 @@ var (
|
||||
correctCreationDate10CreateTable string
|
||||
//go:embed 10/10_fill_table.sql
|
||||
correctCreationDate10FillTable string
|
||||
//go:embed 10/10_update.sql
|
||||
correctCreationDate10Update string
|
||||
//go:embed 10/cockroach/10_update.sql
|
||||
//go:embed 10/postgres/10_update.sql
|
||||
correctCreationDate10Update embed.FS
|
||||
//go:embed 10/10_count_wrong_events.sql
|
||||
correctCreationDate10CountWrongEvents string
|
||||
//go:embed 10/10_empty_table.sql
|
||||
@@ -34,7 +35,8 @@ func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, mig.FailAfter)
|
||||
defer cancel()
|
||||
|
||||
for {
|
||||
for i := 0; ; i++ {
|
||||
logging.WithFields("mig", mig.String(), "iteration", i).Debug("start iteration")
|
||||
var affected int64
|
||||
err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error {
|
||||
if mig.dbClient.Type() == "cockroach" {
|
||||
@@ -46,6 +48,7 @@ func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logging.WithFields("mig", mig.String(), "iteration", i).Debug("temp table created")
|
||||
|
||||
_, err = tx.ExecContext(ctx, correctCreationDate10Truncate)
|
||||
if err != nil {
|
||||
@@ -55,19 +58,25 @@ func (mig *CorrectCreationDate) Execute(ctx context.Context) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logging.WithFields("mig", mig.String(), "iteration", i).Debug("temp table filled")
|
||||
|
||||
res := tx.QueryRowContext(ctx, correctCreationDate10CountWrongEvents)
|
||||
if err := res.Scan(&affected); err != nil || affected == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, correctCreationDate10Update)
|
||||
updateStmt, err := readStmt(correctCreationDate10Update, "10", mig.dbClient.Type(), "10_update.sql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logging.WithFields("count", affected).Info("creation dates changed")
|
||||
_, err = tx.ExecContext(ctx, updateStmt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logging.WithFields("mig", mig.String(), "iteration", i, "count", affected).Debug("creation dates updated")
|
||||
return nil
|
||||
})
|
||||
logging.WithFields("mig", mig.String(), "iteration", i).Debug("end iteration")
|
||||
if affected == 0 || err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
CREATE temporary TABLE IF NOT EXISTS wrong_events (
|
||||
CREATE TEMPORARY TABLE IF NOT EXISTS wrong_events (
|
||||
instance_id TEXT
|
||||
, event_sequence BIGINT
|
||||
, current_cd TIMESTAMPTZ
|
||||
|
@@ -10,6 +10,8 @@ INSERT INTO wrong_events (
|
||||
) AS next_cd
|
||||
FROM
|
||||
eventstore.events
|
||||
WHERE
|
||||
"position" IS NULL
|
||||
) sub WHERE
|
||||
current_cd < next_cd
|
||||
ORDER BY
|
||||
|
@@ -1 +0,0 @@
|
||||
UPDATE eventstore.events e SET creation_date = we.next_cd FROM wrong_events we WHERE e.event_sequence = we.event_sequence and e.instance_id = we.instance_id;
|
1
cmd/setup/10/cockroach/10_update.sql
Normal file
1
cmd/setup/10/cockroach/10_update.sql
Normal file
@@ -0,0 +1 @@
|
||||
UPDATE eventstore.events e SET (creation_date, "position") = (we.next_cd, we.next_cd::DECIMAL) FROM wrong_events we WHERE e.event_sequence = we.event_sequence AND e.instance_id = we.instance_id;
|
10
cmd/setup/10/postgres/10_update.sql
Normal file
10
cmd/setup/10/postgres/10_update.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
UPDATE
|
||||
eventstore.events e
|
||||
SET
|
||||
creation_date = we.next_cd
|
||||
, "position" = (EXTRACT(EPOCH FROM we.next_cd))
|
||||
FROM
|
||||
wrong_events we
|
||||
WHERE
|
||||
e.event_sequence = we.event_sequence
|
||||
AND e.instance_id = we.instance_id;
|
@@ -1,92 +0,0 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
|
||||
"github.com/cockroachdb/cockroach-go/v2/crdb"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 11/11_add_column.sql
|
||||
addEventCreatedAt string
|
||||
//go:embed 11/11_update_events.sql
|
||||
setCreatedAt string
|
||||
//go:embed 11/11_set_column.sql
|
||||
setCreatedAtDetails string
|
||||
//go:embed 11/postgres/create_index.sql
|
||||
//go:embed 11/cockroach/create_index.sql
|
||||
createdAtIndexCreateStmt embed.FS
|
||||
//go:embed 11/postgres/drop_index.sql
|
||||
//go:embed 11/cockroach/drop_index.sql
|
||||
createdAtIndexDropStmt embed.FS
|
||||
)
|
||||
|
||||
type AddEventCreatedAt struct {
|
||||
BulkAmount int
|
||||
step10 *CorrectCreationDate
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddEventCreatedAt) Execute(ctx context.Context) error {
|
||||
// execute step 10 again because events created after the first execution of step 10
|
||||
// could still have the wrong ordering of sequences and creation date
|
||||
if err := mig.step10.Execute(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := mig.dbClient.ExecContext(ctx, addEventCreatedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
createIndex, err := readStmt(createdAtIndexCreateStmt, "11", mig.dbClient.Type(), "create_index.sql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = mig.dbClient.ExecContext(ctx, createIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; ; i++ {
|
||||
var affected int64
|
||||
err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error {
|
||||
res, err := tx.Exec(setCreatedAt, mig.BulkAmount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
affected, _ = res.RowsAffected()
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logging.WithFields("step", "11", "iteration", i, "affected", affected).Info("set created_at iteration done")
|
||||
if affected < int64(mig.BulkAmount) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
logging.Info("set details")
|
||||
_, err = mig.dbClient.ExecContext(ctx, setCreatedAtDetails)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dropIndex, err := readStmt(createdAtIndexDropStmt, "11", mig.dbClient.Type(), "drop_index.sql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = mig.dbClient.ExecContext(ctx, dropIndex)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddEventCreatedAt) String() string {
|
||||
return "11_event_created_at"
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
BEGIN;
|
||||
-- create table with empty created_at
|
||||
ALTER TABLE eventstore.events ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ DEFAULT NULL;
|
||||
-- set column rules
|
||||
ALTER TABLE eventstore.events ALTER COLUMN created_at SET DEFAULT clock_timestamp();
|
||||
COMMIT;
|
@@ -1,3 +0,0 @@
|
||||
BEGIN;
|
||||
ALTER TABLE eventstore.events ALTER COLUMN created_at SET NOT NULL;
|
||||
COMMIT;
|
@@ -1,21 +0,0 @@
|
||||
UPDATE eventstore.events SET
|
||||
created_at = creation_date
|
||||
FROM (
|
||||
SELECT
|
||||
e.event_sequence as seq
|
||||
, e.instance_id as i_id
|
||||
, e.creation_date as cd
|
||||
FROM
|
||||
eventstore.events e
|
||||
WHERE
|
||||
created_at IS NULL
|
||||
ORDER BY
|
||||
event_sequence ASC
|
||||
, instance_id
|
||||
LIMIT $1
|
||||
) AS e
|
||||
WHERE
|
||||
e.seq = eventstore.events.event_sequence
|
||||
AND e.i_id = eventstore.events.instance_id
|
||||
AND e.cd = eventstore.events.creation_date
|
||||
;
|
@@ -1,8 +0,0 @@
|
||||
CREATE INDEX IF NOT EXISTS ca_fill_idx ON eventstore.events (
|
||||
event_sequence DESC
|
||||
, instance_id
|
||||
) STORING (
|
||||
id
|
||||
, creation_date
|
||||
, created_at
|
||||
) WHERE created_at IS NULL;
|
@@ -1 +0,0 @@
|
||||
DROP INDEX IF EXISTS eventstore.events@ca_fill_idx;
|
@@ -1,4 +0,0 @@
|
||||
CREATE INDEX IF NOT EXISTS ca_fill_idx ON eventstore.events (
|
||||
event_sequence DESC
|
||||
, instance_id
|
||||
) WHERE created_at IS NULL;
|
@@ -1 +0,0 @@
|
||||
DROP INDEX IF EXISTS eventstore.ca_fill_idx;
|
72
cmd/setup/14.go
Normal file
72
cmd/setup/14.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgconn"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 14/cockroach/*.sql
|
||||
//go:embed 14/postgres/*.sql
|
||||
newEventsTable embed.FS
|
||||
)
|
||||
|
||||
type NewEventsTable struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *NewEventsTable) Execute(ctx context.Context) error {
|
||||
migrations, err := newEventsTable.ReadDir("14/" + mig.dbClient.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if events already exists events2 is created during a setup job
|
||||
var count int
|
||||
err = mig.dbClient.QueryRow(
|
||||
func(row *sql.Row) error {
|
||||
if err = row.Scan(&count); err != nil {
|
||||
return err
|
||||
}
|
||||
return row.Err()
|
||||
},
|
||||
"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events2'",
|
||||
)
|
||||
if err != nil || count == 1 {
|
||||
return err
|
||||
}
|
||||
for _, migration := range migrations {
|
||||
stmt, err := readStmt(newEventsTable, "14", mig.dbClient.Type(), migration.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stmt = strings.ReplaceAll(stmt, "{{.username}}", mig.dbClient.Username())
|
||||
|
||||
logging.WithFields("migration", mig.String(), "file", migration.Name()).Debug("execute statement")
|
||||
|
||||
_, err = mig.dbClient.ExecContext(ctx, stmt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *NewEventsTable) String() string {
|
||||
return "14_events_push"
|
||||
}
|
||||
|
||||
func (mig *NewEventsTable) ContinueOnErr(err error) bool {
|
||||
pgErr := new(pgconn.PgError)
|
||||
if errors.As(err, &pgErr) {
|
||||
return pgErr.Code == "42P01"
|
||||
}
|
||||
return false
|
||||
}
|
1
cmd/setup/14/cockroach/01_disable_inserts.sql
Normal file
1
cmd/setup/14/cockroach/01_disable_inserts.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE eventstore.events RENAME TO events_old;
|
33
cmd/setup/14/cockroach/02_create_and_fill_events2.sql
Normal file
33
cmd/setup/14/cockroach/02_create_and_fill_events2.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
CREATE TABLE eventstore.events2 (
|
||||
instance_id,
|
||||
aggregate_type,
|
||||
aggregate_id,
|
||||
|
||||
event_type,
|
||||
"sequence",
|
||||
revision,
|
||||
created_at,
|
||||
payload,
|
||||
creator,
|
||||
"owner",
|
||||
|
||||
"position",
|
||||
in_tx_order,
|
||||
|
||||
PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence")
|
||||
) AS SELECT
|
||||
instance_id,
|
||||
aggregate_type,
|
||||
aggregate_id,
|
||||
|
||||
event_type,
|
||||
event_sequence,
|
||||
substr(aggregate_version, 2)::SMALLINT,
|
||||
creation_date,
|
||||
event_data,
|
||||
editor_user,
|
||||
resource_owner,
|
||||
|
||||
creation_date::DECIMAL,
|
||||
event_sequence
|
||||
FROM eventstore.events_old;
|
7
cmd/setup/14/cockroach/03_constraints.sql
Normal file
7
cmd/setup/14/cockroach/03_constraints.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE eventstore.events2 ALTER COLUMN event_type SET NOT NULL;
|
||||
ALTER TABLE eventstore.events2 ALTER COLUMN revision SET NOT NULL;
|
||||
ALTER TABLE eventstore.events2 ALTER COLUMN created_at SET NOT NULL;
|
||||
ALTER TABLE eventstore.events2 ALTER COLUMN creator SET NOT NULL;
|
||||
ALTER TABLE eventstore.events2 ALTER COLUMN "owner" SET NOT NULL;
|
||||
ALTER TABLE eventstore.events2 ALTER COLUMN "position" SET NOT NULL;
|
||||
ALTER TABLE eventstore.events2 ALTER COLUMN in_tx_order SET NOT NULL;
|
3
cmd/setup/14/cockroach/04_indexes.sql
Normal file
3
cmd/setup/14/cockroach/04_indexes.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
CREATE INDEX IF NOT EXISTS es_active_instances ON eventstore.events2 (created_at DESC) STORING ("position");
|
||||
CREATE INDEX IF NOT EXISTS es_wm ON eventstore.events2 (aggregate_id, instance_id, aggregate_type, event_type);
|
||||
CREATE INDEX IF NOT EXISTS es_projection ON eventstore.events2 (instance_id, aggregate_type, event_type, "position");
|
1
cmd/setup/14/postgres/01_disable_inserts.sql
Normal file
1
cmd/setup/14/postgres/01_disable_inserts.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE eventstore.events RENAME TO events_old;
|
31
cmd/setup/14/postgres/02_create_and_fill_events2.sql
Normal file
31
cmd/setup/14/postgres/02_create_and_fill_events2.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
CREATE TABLE eventstore.events2 (
|
||||
instance_id,
|
||||
aggregate_type,
|
||||
aggregate_id,
|
||||
|
||||
event_type,
|
||||
"sequence",
|
||||
revision,
|
||||
created_at,
|
||||
payload,
|
||||
creator,
|
||||
"owner",
|
||||
|
||||
"position",
|
||||
in_tx_order
|
||||
) AS SELECT
|
||||
instance_id,
|
||||
aggregate_type,
|
||||
aggregate_id,
|
||||
|
||||
event_type,
|
||||
event_sequence,
|
||||
substr(aggregate_version, 2)::SMALLINT,
|
||||
creation_date,
|
||||
event_data,
|
||||
editor_user,
|
||||
resource_owner,
|
||||
|
||||
EXTRACT(EPOCH FROM creation_date),
|
||||
event_sequence
|
||||
FROM eventstore.events_old;
|
4
cmd/setup/14/postgres/03_events2_pk.sql
Normal file
4
cmd/setup/14/postgres/03_events2_pk.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
BEGIN;
|
||||
ALTER TABLE eventstore.events2 DROP CONSTRAINT IF EXISTS events2_pkey;
|
||||
ALTER TABLE eventstore.events2 ADD PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence");
|
||||
COMMIT;
|
7
cmd/setup/14/postgres/04_constraints.sql
Normal file
7
cmd/setup/14/postgres/04_constraints.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE eventstore.events2 ALTER COLUMN event_type SET NOT NULL,
|
||||
ALTER COLUMN revision SET NOT NULL,
|
||||
ALTER COLUMN created_at SET NOT NULL,
|
||||
ALTER COLUMN creator SET NOT NULL,
|
||||
ALTER COLUMN "owner" SET NOT NULL,
|
||||
ALTER COLUMN "position" SET NOT NULL,
|
||||
ALTER COLUMN in_tx_order SET NOT NULL;
|
3
cmd/setup/14/postgres/05_indexes.sql
Normal file
3
cmd/setup/14/postgres/05_indexes.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
CREATE INDEX IF NOT EXISTS es_active_instances ON eventstore.events2 (created_at DESC, instance_id);
|
||||
CREATE INDEX IF NOT EXISTS es_wm ON eventstore.events2 (aggregate_id, instance_id, aggregate_type, event_type);
|
||||
CREATE INDEX IF NOT EXISTS es_projection ON eventstore.events2 (instance_id, aggregate_type, event_type, "position");
|
45
cmd/setup/15.go
Normal file
45
cmd/setup/15.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 15/cockroach/*.sql
|
||||
//go:embed 15/postgres/*.sql
|
||||
currentProjectionState embed.FS
|
||||
)
|
||||
|
||||
type CurrentProjectionState struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *CurrentProjectionState) Execute(ctx context.Context) error {
|
||||
migrations, err := currentProjectionState.ReadDir("15/" + mig.dbClient.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, migration := range migrations {
|
||||
stmt, err := readStmt(currentProjectionState, "15", mig.dbClient.Type(), migration.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logging.WithFields("file", migration.Name(), "migration", mig.String()).Info("execute statement")
|
||||
|
||||
_, err = mig.dbClient.ExecContext(ctx, stmt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *CurrentProjectionState) String() string {
|
||||
return "15_current_projection_state"
|
||||
}
|
16
cmd/setup/15/cockroach/01_new_failed_events.sql
Normal file
16
cmd/setup/15/cockroach/01_new_failed_events.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE IF NOT EXISTS projections.failed_events2 (
|
||||
projection_name TEXT NOT NULL
|
||||
, instance_id TEXT NOT NULL
|
||||
|
||||
, aggregate_type TEXT NOT NULL
|
||||
, aggregate_id TEXT NOT NULL
|
||||
, event_creation_date TIMESTAMPTZ NOT NULL
|
||||
, failed_sequence INT8 NOT NULL
|
||||
|
||||
, failure_count INT2 NULL DEFAULT 0
|
||||
, error TEXT
|
||||
, last_failed TIMESTAMPTZ
|
||||
|
||||
, PRIMARY KEY (projection_name, instance_id, aggregate_type, aggregate_id, failed_sequence)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS fe2_instance_id_idx on projections.failed_events2 (instance_id);
|
26
cmd/setup/15/cockroach/02_fe_from_projections.sql
Normal file
26
cmd/setup/15/cockroach/02_fe_from_projections.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO projections.failed_events2 (
|
||||
projection_name
|
||||
, instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
, event_creation_date
|
||||
, failed_sequence
|
||||
, failure_count
|
||||
, error
|
||||
, last_failed
|
||||
) SELECT
|
||||
fe.projection_name
|
||||
, fe.instance_id
|
||||
, e.aggregate_type
|
||||
, e.aggregate_id
|
||||
, e.created_at
|
||||
, e.sequence
|
||||
, fe.failure_count
|
||||
, fe.error
|
||||
, fe.last_failed
|
||||
FROM
|
||||
projections.failed_events fe
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = fe.instance_id
|
||||
AND e.sequence = fe.failed_sequence
|
||||
ON CONFLICT DO NOTHING;
|
26
cmd/setup/15/cockroach/03_fe_from_adminapi.sql
Normal file
26
cmd/setup/15/cockroach/03_fe_from_adminapi.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO projections.failed_events2 (
|
||||
projection_name
|
||||
, instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
, event_creation_date
|
||||
, failed_sequence
|
||||
, failure_count
|
||||
, error
|
||||
, last_failed
|
||||
) SELECT
|
||||
fe.view_name
|
||||
, fe.instance_id
|
||||
, e.aggregate_type
|
||||
, e.aggregate_id
|
||||
, e.created_at
|
||||
, e.sequence
|
||||
, fe.failure_count
|
||||
, fe.err_msg
|
||||
, fe.last_failed
|
||||
FROM
|
||||
adminapi.failed_events fe
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = fe.instance_id
|
||||
AND e.sequence = fe.failed_sequence
|
||||
ON CONFLICT DO NOTHING;
|
26
cmd/setup/15/cockroach/04_fe_from_auth.sql
Normal file
26
cmd/setup/15/cockroach/04_fe_from_auth.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO projections.failed_events2 (
|
||||
projection_name
|
||||
, instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
, event_creation_date
|
||||
, failed_sequence
|
||||
, failure_count
|
||||
, error
|
||||
, last_failed
|
||||
) SELECT
|
||||
fe.view_name
|
||||
, fe.instance_id
|
||||
, e.aggregate_type
|
||||
, e.aggregate_id
|
||||
, e.created_at
|
||||
, e.sequence
|
||||
, fe.failure_count
|
||||
, fe.err_msg
|
||||
, fe.last_failed
|
||||
FROM
|
||||
auth.failed_events fe
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = fe.instance_id
|
||||
AND e.sequence = fe.failed_sequence
|
||||
ON CONFLICT DO NOTHING;
|
15
cmd/setup/15/cockroach/05_current_states.sql
Normal file
15
cmd/setup/15/cockroach/05_current_states.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE IF NOT EXISTS projections.current_states (
|
||||
projection_name TEXT NOT NULL
|
||||
, instance_id TEXT NOT NULL
|
||||
|
||||
, last_updated TIMESTAMPTZ
|
||||
|
||||
, aggregate_id TEXT
|
||||
, aggregate_type TEXT
|
||||
, "sequence" INT8
|
||||
, event_date TIMESTAMPTZ
|
||||
, "position" DECIMAL
|
||||
|
||||
, PRIMARY KEY (projection_name, instance_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS cs_instance_id_idx ON projections.current_states (instance_id);
|
29
cmd/setup/15/cockroach/06_cs_from_projections.sql
Normal file
29
cmd/setup/15/cockroach/06_cs_from_projections.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
INSERT INTO projections.current_states (
|
||||
projection_name
|
||||
, instance_id
|
||||
, event_date
|
||||
, "position"
|
||||
, last_updated
|
||||
) (SELECT
|
||||
cs.projection_name
|
||||
, cs.instance_id
|
||||
, e.created_at
|
||||
, e.position
|
||||
, cs.timestamp
|
||||
FROM
|
||||
projections.current_sequences cs
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = cs.instance_id
|
||||
AND e.aggregate_type = cs.aggregate_type
|
||||
AND e.sequence = cs.current_sequence
|
||||
AND cs.current_sequence = (
|
||||
SELECT
|
||||
MAX(cs2.current_sequence)
|
||||
FROM
|
||||
projections.current_sequences cs2
|
||||
WHERE
|
||||
cs.projection_name = cs2.projection_name
|
||||
AND cs.instance_id = cs2.instance_id
|
||||
)
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
28
cmd/setup/15/cockroach/07_cs_from_adminapi.sql
Normal file
28
cmd/setup/15/cockroach/07_cs_from_adminapi.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
INSERT INTO projections.current_states (
|
||||
projection_name
|
||||
, instance_id
|
||||
, event_date
|
||||
, "position"
|
||||
, last_updated
|
||||
) (SELECT
|
||||
cs.view_name
|
||||
, cs.instance_id
|
||||
, e.created_at
|
||||
, e.position
|
||||
, cs.last_successful_spooler_run
|
||||
FROM
|
||||
adminapi.current_sequences cs
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = cs.instance_id
|
||||
AND e.sequence = cs.current_sequence
|
||||
AND cs.current_sequence = (
|
||||
SELECT
|
||||
MAX(cs2.current_sequence)
|
||||
FROM
|
||||
adminapi.current_sequences cs2
|
||||
WHERE
|
||||
cs.view_name = cs2.view_name
|
||||
AND cs.instance_id = cs2.instance_id
|
||||
)
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
28
cmd/setup/15/cockroach/08_cs_from_auth.sql
Normal file
28
cmd/setup/15/cockroach/08_cs_from_auth.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
INSERT INTO projections.current_states (
|
||||
projection_name
|
||||
, instance_id
|
||||
, event_date
|
||||
, "position"
|
||||
, last_updated
|
||||
) (SELECT
|
||||
cs.view_name
|
||||
, cs.instance_id
|
||||
, e.created_at
|
||||
, e.position
|
||||
, cs.last_successful_spooler_run
|
||||
FROM
|
||||
auth.current_sequences cs
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = cs.instance_id
|
||||
AND e.sequence = cs.current_sequence
|
||||
AND cs.current_sequence = (
|
||||
SELECT
|
||||
MAX(cs2.current_sequence)
|
||||
FROM
|
||||
auth.current_sequences cs2
|
||||
WHERE
|
||||
cs.view_name = cs2.view_name
|
||||
AND cs.instance_id = cs2.instance_id
|
||||
)
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
16
cmd/setup/15/postgres/01_new_failed_events.sql
Normal file
16
cmd/setup/15/postgres/01_new_failed_events.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE IF NOT EXISTS projections.failed_events2 (
|
||||
projection_name TEXT NOT NULL
|
||||
, instance_id TEXT NOT NULL
|
||||
|
||||
, aggregate_type TEXT NOT NULL
|
||||
, aggregate_id TEXT NOT NULL
|
||||
, event_creation_date TIMESTAMPTZ NOT NULL
|
||||
, failed_sequence INT8 NOT NULL
|
||||
|
||||
, failure_count INT2 NULL DEFAULT 0
|
||||
, error TEXT
|
||||
, last_failed TIMESTAMPTZ
|
||||
|
||||
, PRIMARY KEY (projection_name, instance_id, aggregate_type, aggregate_id, failed_sequence)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS fe2_instance_id_idx on projections.failed_events2 (instance_id);
|
26
cmd/setup/15/postgres/02_fe_from_projections.sql
Normal file
26
cmd/setup/15/postgres/02_fe_from_projections.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO projections.failed_events2 (
|
||||
projection_name
|
||||
, instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
, event_creation_date
|
||||
, failed_sequence
|
||||
, failure_count
|
||||
, error
|
||||
, last_failed
|
||||
) SELECT
|
||||
fe.projection_name
|
||||
, fe.instance_id
|
||||
, e.aggregate_type
|
||||
, e.aggregate_id
|
||||
, e.created_at
|
||||
, e.sequence
|
||||
, fe.failure_count
|
||||
, fe.error
|
||||
, fe.last_failed
|
||||
FROM
|
||||
projections.failed_events fe
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = fe.instance_id
|
||||
AND e.sequence = fe.failed_sequence
|
||||
ON CONFLICT DO NOTHING;
|
26
cmd/setup/15/postgres/03_fe_from_adminapi.sql
Normal file
26
cmd/setup/15/postgres/03_fe_from_adminapi.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO projections.failed_events2 (
|
||||
projection_name
|
||||
, instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
, event_creation_date
|
||||
, failed_sequence
|
||||
, failure_count
|
||||
, error
|
||||
, last_failed
|
||||
) SELECT
|
||||
fe.view_name
|
||||
, fe.instance_id
|
||||
, e.aggregate_type
|
||||
, e.aggregate_id
|
||||
, e.created_at
|
||||
, e.sequence
|
||||
, fe.failure_count
|
||||
, fe.err_msg
|
||||
, fe.last_failed
|
||||
FROM
|
||||
adminapi.failed_events fe
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = fe.instance_id
|
||||
AND e.sequence = fe.failed_sequence
|
||||
ON CONFLICT DO NOTHING;
|
26
cmd/setup/15/postgres/04_fe_from_auth.sql
Normal file
26
cmd/setup/15/postgres/04_fe_from_auth.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO projections.failed_events2 (
|
||||
projection_name
|
||||
, instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
, event_creation_date
|
||||
, failed_sequence
|
||||
, failure_count
|
||||
, error
|
||||
, last_failed
|
||||
) SELECT
|
||||
fe.view_name
|
||||
, fe.instance_id
|
||||
, e.aggregate_type
|
||||
, e.aggregate_id
|
||||
, e.created_at
|
||||
, e.sequence
|
||||
, fe.failure_count
|
||||
, fe.err_msg
|
||||
, fe.last_failed
|
||||
FROM
|
||||
auth.failed_events fe
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = fe.instance_id
|
||||
AND e.sequence = fe.failed_sequence
|
||||
ON CONFLICT DO NOTHING;
|
15
cmd/setup/15/postgres/05_current_states.sql
Normal file
15
cmd/setup/15/postgres/05_current_states.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE IF NOT EXISTS projections.current_states (
|
||||
projection_name TEXT NOT NULL
|
||||
, instance_id TEXT NOT NULL
|
||||
|
||||
, last_updated TIMESTAMPTZ
|
||||
|
||||
, aggregate_id TEXT
|
||||
, aggregate_type TEXT
|
||||
, "sequence" INT8
|
||||
, event_date TIMESTAMPTZ
|
||||
, "position" DECIMAL
|
||||
|
||||
, PRIMARY KEY (projection_name, instance_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS cs_instance_id_idx ON projections.current_states (instance_id);
|
28
cmd/setup/15/postgres/06_cs_from_projections.sql
Normal file
28
cmd/setup/15/postgres/06_cs_from_projections.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
INSERT INTO projections.current_states (
|
||||
projection_name
|
||||
, instance_id
|
||||
, event_date
|
||||
, "position"
|
||||
, last_updated
|
||||
) SELECT
|
||||
cs.projection_name
|
||||
, cs.instance_id
|
||||
, e.created_at
|
||||
, e.position
|
||||
, cs.timestamp
|
||||
FROM
|
||||
projections.current_sequences cs
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = cs.instance_id
|
||||
AND e.aggregate_type = cs.aggregate_type
|
||||
AND e.sequence = cs.current_sequence
|
||||
AND cs.current_sequence = (
|
||||
SELECT
|
||||
MAX(cs2.current_sequence)
|
||||
FROM
|
||||
projections.current_sequences cs2
|
||||
WHERE
|
||||
cs.projection_name = cs2.projection_name
|
||||
AND cs.instance_id = cs2.instance_id
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
27
cmd/setup/15/postgres/07_cs_from_adminapi.sql
Normal file
27
cmd/setup/15/postgres/07_cs_from_adminapi.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
INSERT INTO projections.current_states (
|
||||
projection_name
|
||||
, instance_id
|
||||
, event_date
|
||||
, "position"
|
||||
, last_updated
|
||||
) SELECT
|
||||
cs.view_name
|
||||
, cs.instance_id
|
||||
, e.created_at
|
||||
, e.position
|
||||
, cs.last_successful_spooler_run
|
||||
FROM
|
||||
adminapi.current_sequences cs
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = cs.instance_id
|
||||
AND e.sequence = cs.current_sequence
|
||||
AND cs.current_sequence = (
|
||||
SELECT
|
||||
MAX(cs2.current_sequence)
|
||||
FROM
|
||||
adminapi.current_sequences cs2
|
||||
WHERE
|
||||
cs.view_name = cs2.view_name
|
||||
AND cs.instance_id = cs2.instance_id
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
27
cmd/setup/15/postgres/08_cs_from_auth.sql
Normal file
27
cmd/setup/15/postgres/08_cs_from_auth.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
INSERT INTO projections.current_states (
|
||||
projection_name
|
||||
, instance_id
|
||||
, event_date
|
||||
, "position"
|
||||
, last_updated
|
||||
) SELECT
|
||||
cs.view_name
|
||||
, cs.instance_id
|
||||
, e.created_at
|
||||
, e.position
|
||||
, cs.last_successful_spooler_run
|
||||
FROM
|
||||
auth.current_sequences cs
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = cs.instance_id
|
||||
AND e.sequence = cs.current_sequence
|
||||
AND cs.current_sequence = (
|
||||
SELECT
|
||||
MAX(cs2.current_sequence)
|
||||
FROM
|
||||
auth.current_sequences cs2
|
||||
WHERE
|
||||
cs.view_name = cs2.view_name
|
||||
AND cs.instance_id = cs2.instance_id
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
@@ -9,6 +9,8 @@ import (
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
old_es "github.com/zitadel/zitadel/internal/eventstore/repository/sql"
|
||||
new_es "github.com/zitadel/zitadel/internal/eventstore/v3"
|
||||
"github.com/zitadel/zitadel/internal/migration"
|
||||
)
|
||||
|
||||
@@ -29,11 +31,14 @@ func Cleanup(config *Config) {
|
||||
|
||||
logging.Info("cleanup started")
|
||||
|
||||
dbClient, err := database.Connect(config.Database, false)
|
||||
zitadelDBClient, err := database.Connect(config.Database, false, false)
|
||||
logging.OnError(err).Fatal("unable to connect to database")
|
||||
esPusherDBClient, err := database.Connect(config.Database, false, true)
|
||||
logging.OnError(err).Fatal("unable to connect to database")
|
||||
|
||||
es, err := eventstore.Start(&eventstore.Config{Client: dbClient})
|
||||
logging.OnError(err).Fatal("unable to start eventstore")
|
||||
config.Eventstore.Pusher = new_es.NewEventstore(esPusherDBClient)
|
||||
config.Eventstore.Querier = old_es.NewCRDB(zitadelDBClient)
|
||||
es := eventstore.NewEventstore(config.Eventstore)
|
||||
migration.RegisterMappers(es)
|
||||
|
||||
step, err := migration.LatestStep(ctx, es)
|
||||
|
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
)
|
||||
@@ -31,6 +32,7 @@ type Config struct {
|
||||
DefaultInstance command.InstanceSetup
|
||||
Machine *id.Config
|
||||
Projections projection.Config
|
||||
Eventstore *eventstore.Config
|
||||
}
|
||||
|
||||
func MustNewConfig(v *viper.Viper) *Config {
|
||||
@@ -60,16 +62,15 @@ type Steps struct {
|
||||
s1ProjectionTable *ProjectionTable
|
||||
s2AssetsTable *AssetTable
|
||||
FirstInstance *FirstInstance
|
||||
s4EventstoreIndexes *EventstoreIndexesNew
|
||||
s5LastFailed *LastFailed
|
||||
s6OwnerRemoveColumns *OwnerRemoveColumns
|
||||
s7LogstoreTables *LogstoreTables
|
||||
s8AuthTokens *AuthTokenIndexes
|
||||
s9EventstoreIndexes2 *EventstoreIndexesNew
|
||||
CorrectCreationDate *CorrectCreationDate
|
||||
AddEventCreatedAt *AddEventCreatedAt
|
||||
s12AddOTPColumns *AddOTPColumns
|
||||
s13FixQuotaProjection *FixQuotaConstraints
|
||||
s14NewEventsTable *NewEventsTable
|
||||
s15CurrentStates *CurrentProjectionState
|
||||
}
|
||||
|
||||
type encryptionKeyConfig struct {
|
||||
|
@@ -1,29 +0,0 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
type EventstoreIndexesNew struct {
|
||||
dbClient *database.DB
|
||||
name string
|
||||
step string
|
||||
fileName string
|
||||
stmts embed.FS
|
||||
}
|
||||
|
||||
func (mig *EventstoreIndexesNew) Execute(ctx context.Context) error {
|
||||
stmt, err := readStmt(mig.stmts, mig.step, mig.dbClient.Type(), mig.fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = mig.dbClient.ExecContext(ctx, stmt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *EventstoreIndexesNew) String() string {
|
||||
return mig.name
|
||||
}
|
@@ -14,6 +14,8 @@ import (
|
||||
"github.com/zitadel/zitadel/cmd/tls"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
old_es "github.com/zitadel/zitadel/internal/eventstore/repository/sql"
|
||||
new_es "github.com/zitadel/zitadel/internal/eventstore/v3"
|
||||
"github.com/zitadel/zitadel/internal/migration"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
)
|
||||
@@ -62,22 +64,26 @@ func Setup(config *Config, steps *Steps, masterKey string) {
|
||||
ctx := context.Background()
|
||||
logging.Info("setup started")
|
||||
|
||||
dbClient, err := database.Connect(config.Database, false)
|
||||
zitadelDBClient, err := database.Connect(config.Database, false, false)
|
||||
logging.OnError(err).Fatal("unable to connect to database")
|
||||
esPusherDBClient, err := database.Connect(config.Database, false, true)
|
||||
logging.OnError(err).Fatal("unable to connect to database")
|
||||
|
||||
eventstoreClient, err := eventstore.Start(&eventstore.Config{Client: dbClient})
|
||||
config.Eventstore.Querier = old_es.NewCRDB(zitadelDBClient)
|
||||
config.Eventstore.Pusher = new_es.NewEventstore(esPusherDBClient)
|
||||
eventstoreClient := eventstore.NewEventstore(config.Eventstore)
|
||||
logging.OnError(err).Fatal("unable to start eventstore")
|
||||
migration.RegisterMappers(eventstoreClient)
|
||||
|
||||
steps.s1ProjectionTable = &ProjectionTable{dbClient: dbClient.DB}
|
||||
steps.s2AssetsTable = &AssetTable{dbClient: dbClient.DB}
|
||||
steps.s1ProjectionTable = &ProjectionTable{dbClient: zitadelDBClient.DB}
|
||||
steps.s2AssetsTable = &AssetTable{dbClient: zitadelDBClient.DB}
|
||||
|
||||
steps.FirstInstance.instanceSetup = config.DefaultInstance
|
||||
steps.FirstInstance.userEncryptionKey = config.EncryptionKeys.User
|
||||
steps.FirstInstance.smtpEncryptionKey = config.EncryptionKeys.SMTP
|
||||
steps.FirstInstance.oidcEncryptionKey = config.EncryptionKeys.OIDC
|
||||
steps.FirstInstance.masterKey = masterKey
|
||||
steps.FirstInstance.db = dbClient
|
||||
steps.FirstInstance.db = zitadelDBClient
|
||||
steps.FirstInstance.es = eventstoreClient
|
||||
steps.FirstInstance.defaults = config.SystemDefaults
|
||||
steps.FirstInstance.zitadelRoles = config.InternalAuthZ.RolePermissionMappings
|
||||
@@ -85,19 +91,17 @@ func Setup(config *Config, steps *Steps, masterKey string) {
|
||||
steps.FirstInstance.externalSecure = config.ExternalSecure
|
||||
steps.FirstInstance.externalPort = config.ExternalPort
|
||||
|
||||
steps.s4EventstoreIndexes = New04(dbClient)
|
||||
steps.s5LastFailed = &LastFailed{dbClient: dbClient.DB}
|
||||
steps.s6OwnerRemoveColumns = &OwnerRemoveColumns{dbClient: dbClient.DB}
|
||||
steps.s7LogstoreTables = &LogstoreTables{dbClient: dbClient.DB, username: config.Database.Username(), dbType: config.Database.Type()}
|
||||
steps.s8AuthTokens = &AuthTokenIndexes{dbClient: dbClient}
|
||||
steps.s9EventstoreIndexes2 = New09(dbClient)
|
||||
steps.CorrectCreationDate.dbClient = dbClient
|
||||
steps.AddEventCreatedAt.dbClient = dbClient
|
||||
steps.AddEventCreatedAt.step10 = steps.CorrectCreationDate
|
||||
steps.s12AddOTPColumns = &AddOTPColumns{dbClient: dbClient}
|
||||
steps.s13FixQuotaProjection = &FixQuotaConstraints{dbClient: dbClient}
|
||||
steps.s5LastFailed = &LastFailed{dbClient: zitadelDBClient.DB}
|
||||
steps.s6OwnerRemoveColumns = &OwnerRemoveColumns{dbClient: zitadelDBClient.DB}
|
||||
steps.s7LogstoreTables = &LogstoreTables{dbClient: zitadelDBClient.DB, username: config.Database.Username(), dbType: config.Database.Type()}
|
||||
steps.s8AuthTokens = &AuthTokenIndexes{dbClient: zitadelDBClient}
|
||||
steps.CorrectCreationDate.dbClient = esPusherDBClient
|
||||
steps.s12AddOTPColumns = &AddOTPColumns{dbClient: zitadelDBClient}
|
||||
steps.s13FixQuotaProjection = &FixQuotaConstraints{dbClient: zitadelDBClient}
|
||||
steps.s14NewEventsTable = &NewEventsTable{dbClient: esPusherDBClient}
|
||||
steps.s15CurrentStates = &CurrentProjectionState{dbClient: zitadelDBClient}
|
||||
|
||||
err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil)
|
||||
err = projection.Create(ctx, zitadelDBClient, eventstoreClient, config.Projections, nil, nil, nil)
|
||||
logging.OnError(err).Fatal("unable to start projections")
|
||||
|
||||
repeatableSteps := []migration.RepeatableMigration{
|
||||
@@ -114,32 +118,28 @@ 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.OnError(err).Fatal("unable to migrate step 1")
|
||||
logging.WithFields("name", steps.s1ProjectionTable.String()).OnError(err).Fatal("migration failed")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s2AssetsTable)
|
||||
logging.OnError(err).Fatal("unable to migrate step 2")
|
||||
logging.WithFields("name", steps.s2AssetsTable.String()).OnError(err).Fatal("migration failed")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.FirstInstance)
|
||||
logging.OnError(err).Fatal("unable to migrate step 3")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s4EventstoreIndexes)
|
||||
logging.OnError(err).Fatal("unable to migrate step 4")
|
||||
logging.WithFields("name", steps.FirstInstance.String()).OnError(err).Fatal("migration failed")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s5LastFailed)
|
||||
logging.OnError(err).Fatal("unable to migrate step 5")
|
||||
logging.WithFields("name", steps.s5LastFailed.String()).OnError(err).Fatal("migration failed")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s6OwnerRemoveColumns)
|
||||
logging.OnError(err).Fatal("unable to migrate step 6")
|
||||
logging.WithFields("name", steps.s6OwnerRemoveColumns.String()).OnError(err).Fatal("migration failed")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s7LogstoreTables)
|
||||
logging.OnError(err).Fatal("unable to migrate step 7")
|
||||
logging.WithFields("name", steps.s7LogstoreTables.String()).OnError(err).Fatal("migration failed")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s8AuthTokens)
|
||||
logging.OnError(err).Fatal("unable to migrate step 8")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s9EventstoreIndexes2)
|
||||
logging.OnError(err).Fatal("unable to migrate step 9")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.CorrectCreationDate)
|
||||
logging.OnError(err).Fatal("unable to migrate step 10")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.AddEventCreatedAt)
|
||||
logging.OnError(err).Fatal("unable to migrate step 11")
|
||||
logging.WithFields("name", steps.s8AuthTokens.String()).OnError(err).Fatal("migration failed")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s12AddOTPColumns)
|
||||
logging.OnError(err).Fatal("unable to migrate step 12")
|
||||
logging.WithFields("name", steps.s12AddOTPColumns.String()).OnError(err).Fatal("migration failed")
|
||||
err = migration.Migrate(ctx, eventstoreClient, steps.s13FixQuotaProjection)
|
||||
logging.OnError(err).Fatal("unable to migrate step 13")
|
||||
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")
|
||||
|
||||
for _, repeatableStep := range repeatableSteps {
|
||||
err = migration.Migrate(ctx, eventstoreClient, repeatableStep)
|
||||
|
@@ -60,6 +60,8 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
old_es "github.com/zitadel/zitadel/internal/eventstore/repository/sql"
|
||||
new_es "github.com/zitadel/zitadel/internal/eventstore/v3"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/logstore"
|
||||
"github.com/zitadel/zitadel/internal/logstore/emitters/access"
|
||||
@@ -121,12 +123,16 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
dbClient, err := database.Connect(config.Database, false)
|
||||
zitadelDBClient, err := database.Connect(config.Database, false, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start client for projection: %w", err)
|
||||
}
|
||||
esPusherDBClient, err := database.Connect(config.Database, false, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start client for projection: %w", err)
|
||||
}
|
||||
|
||||
keyStorage, err := cryptoDB.NewKeyStorage(dbClient, masterKey)
|
||||
keyStorage, err := cryptoDB.NewKeyStorage(zitadelDBClient, masterKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start key storage: %w", err)
|
||||
}
|
||||
@@ -135,18 +141,16 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
||||
return err
|
||||
}
|
||||
|
||||
config.Eventstore.Client = dbClient
|
||||
eventstoreClient, err := eventstore.Start(config.Eventstore)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start eventstore for queries: %w", err)
|
||||
}
|
||||
config.Eventstore.Pusher = new_es.NewEventstore(esPusherDBClient)
|
||||
config.Eventstore.Querier = old_es.NewCRDB(zitadelDBClient)
|
||||
eventstoreClient := eventstore.NewEventstore(config.Eventstore)
|
||||
|
||||
sessionTokenVerifier := internal_authz.SessionTokenVerifier(keys.OIDC)
|
||||
|
||||
queries, err := query.StartQueries(
|
||||
ctx,
|
||||
eventstoreClient,
|
||||
dbClient,
|
||||
zitadelDBClient,
|
||||
config.Projections,
|
||||
config.SystemDefaults,
|
||||
keys.IDPConfig,
|
||||
@@ -160,12 +164,13 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
||||
return internal_authz.CheckPermission(ctx, &authz_es.UserMembershipRepo{Queries: q}, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
|
||||
}
|
||||
},
|
||||
config.SystemAPIUsers,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start queries: %w", err)
|
||||
}
|
||||
|
||||
authZRepo, err := authz.Start(queries, dbClient, keys.OIDC, config.ExternalSecure, config.Eventstore.AllowOrderByCreationDate)
|
||||
authZRepo, err := authz.Start(queries, eventstoreClient, zitadelDBClient, keys.OIDC, config.ExternalSecure)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting authz repo: %w", err)
|
||||
}
|
||||
@@ -173,7 +178,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
||||
return internal_authz.CheckPermission(ctx, authZRepo, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
|
||||
}
|
||||
|
||||
storage, err := config.AssetStorage.NewStorage(dbClient.DB)
|
||||
storage, err := config.AssetStorage.NewStorage(zitadelDBClient.DB)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start asset storage client: %w", err)
|
||||
}
|
||||
@@ -215,7 +220,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
actionsExecutionDBEmitter, err := logstore.NewEmitter[*record.ExecutionLog](ctx, clock, config.Quotas.Execution, execution.NewDatabaseLogStorage(dbClient, commands, queries))
|
||||
actionsExecutionDBEmitter, err := logstore.NewEmitter[*record.ExecutionLog](ctx, clock, config.Quotas.Execution, execution.NewDatabaseLogStorage(zitadelDBClient, commands, queries))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -254,7 +259,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
||||
commands,
|
||||
queries,
|
||||
eventstoreClient,
|
||||
dbClient,
|
||||
zitadelDBClient,
|
||||
config,
|
||||
storage,
|
||||
authZRepo,
|
||||
@@ -271,7 +276,7 @@ func startZitadel(config *Config, masterKey string, server chan<- *Server) error
|
||||
if server != nil {
|
||||
server <- &Server{
|
||||
Config: config,
|
||||
DB: dbClient,
|
||||
DB: zitadelDBClient,
|
||||
KeyStorage: keyStorage,
|
||||
Keys: keys,
|
||||
Eventstore: eventstoreClient,
|
||||
@@ -338,18 +343,25 @@ func startAPIs(
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating api %w", err)
|
||||
}
|
||||
authRepo, err := auth_es.Start(ctx, config.Auth, config.SystemDefaults, commands, queries, dbClient, eventstore, keys.OIDC, keys.User, config.Eventstore.AllowOrderByCreationDate)
|
||||
|
||||
config.Auth.Spooler.Client = dbClient
|
||||
config.Auth.Spooler.Eventstore = eventstore
|
||||
authRepo, err := auth_es.Start(ctx, config.Auth, config.SystemDefaults, commands, queries, dbClient, eventstore, keys.OIDC, keys.User)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting auth repo: %w", err)
|
||||
}
|
||||
adminRepo, err := admin_es.Start(ctx, config.Admin, store, dbClient, eventstore, config.Eventstore.AllowOrderByCreationDate)
|
||||
|
||||
config.Admin.Spooler.Client = dbClient
|
||||
config.Admin.Spooler.Eventstore = eventstore
|
||||
err = admin_es.Start(ctx, config.Admin, store, dbClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting admin repo: %w", err)
|
||||
}
|
||||
if err := apis.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.Database.DatabaseName(), config.DefaultInstance, config.ExternalDomain)); err != nil {
|
||||
|
||||
if err := apis.RegisterServer(ctx, system.CreateServer(commands, queries, config.Database.DatabaseName(), config.DefaultInstance, config.ExternalDomain)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := apis.RegisterServer(ctx, admin.CreateServer(config.Database.DatabaseName(), commands, queries, config.SystemDefaults, adminRepo, config.ExternalSecure, keys.User, config.AuditLogRetention)); err != nil {
|
||||
if err := apis.RegisterServer(ctx, admin.CreateServer(config.Database.DatabaseName(), commands, queries, config.SystemDefaults, config.ExternalSecure, keys.User, config.AuditLogRetention)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := apis.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, keys.User, config.ExternalSecure, config.AuditLogRetention)); err != nil {
|
||||
|
Reference in New Issue
Block a user