Skip to content

Commit db4f06f

Browse files
fix(policy): run migrations on db only once for all policy services (#1040)
Closes #742 1. Runs migrations just once and IFF at least one service requires a DB connection 2. Resolves pass by value error where struct pointer `*db.Client` outside for loop was not reassigned within `startService` function due to copy of value. Returns a `*db.Client` instead of resolving with `**db.Client` parameter for better readability. Service starting logs after these changes: ```shell time=2024-06-26T16:54:22.597-07:00 level=INFO msg="starting services" time=2024-06-26T16:54:22.597-07:00 level=INFO msg="skipping migrations" namespace=wellknown service=wellknownconfiguration.WellKnownService runMigrations=true reason="service does not require a database" time=2024-06-26T16:54:22.597-07:00 level=INFO msg="started service" namespace=wellknown service=wellknownconfiguration.WellKnownService time=2024-06-26T16:54:22.597-07:00 level=INFO msg="skipping migrations" namespace=entityresolution service=entityresolution.EntityResolutionService runMigrations=true reason="service does not require a database" time=2024-06-26T16:54:22.597-07:00 level=INFO msg="started service" namespace=entityresolution service=entityresolution.EntityResolutionService time=2024-06-26T16:54:22.597-07:00 level=INFO msg="creating database client" namespace=policy time=2024-06-26T16:54:22.611-07:00 level=INFO msg="running database migrations" time=2024-06-26T16:54:22.612-07:00 level=INFO msg="running migration up" schema=opentdf_policy database=opentdf time=2024-06-26T16:54:22.617-07:00 level=INFO msg="migration db info " "current version"=20240618000000 time=2024-06-26T16:54:22.619-07:00 level=INFO msg="migration up complete" "post-op version"=20240618000000 time=2024-06-26T16:54:22.619-07:00 level=INFO msg="database migrations complete" applied=0 time=2024-06-26T16:54:22.619-07:00 level=INFO msg="started service" namespace=policy service=policy.attributes.AttributesService time=2024-06-26T16:54:22.619-07:00 level=INFO msg="skipping migrations" namespace=policy service=policy.namespaces.NamespaceService runMigrations=true reason="required migrations already ran" time=2024-06-26T16:54:22.619-07:00 level=INFO msg="started service" namespace=policy service=policy.namespaces.NamespaceService time=2024-06-26T16:54:22.619-07:00 level=INFO msg="skipping migrations" namespace=policy service=policy.resourcemapping.ResourceMappingService runMigrations=true reason="required migrations already ran" time=2024-06-26T16:54:22.619-07:00 level=INFO msg="started service" namespace=policy service=policy.resourcemapping.ResourceMappingService time=2024-06-26T16:54:22.619-07:00 level=INFO msg="skipping migrations" namespace=policy service=policy.subjectmapping.SubjectMappingService runMigrations=true reason="required migrations already ran" time=2024-06-26T16:54:22.619-07:00 level=INFO msg="started service" namespace=policy service=policy.subjectmapping.SubjectMappingService time=2024-06-26T16:54:22.619-07:00 level=INFO msg="skipping migrations" namespace=policy service=policy.kasregistry.KeyAccessServerRegistryService runMigrations=true reason="required migrations already ran" time=2024-06-26T16:54:22.619-07:00 level=INFO msg="started service" namespace=policy service=policy.kasregistry.KeyAccessServerRegistryService time=2024-06-26T16:54:22.619-07:00 level=INFO msg="skipping migrations" namespace=health service=grpc.health.v1.Health runMigrations=true reason="service does not require a database" time=2024-06-26T16:54:22.619-07:00 level=INFO msg="started service" namespace=health service=grpc.health.v1.Health time=2024-06-26T16:54:22.619-07:00 level=INFO msg="skipping migrations" namespace=authorization service=authorization.AuthorizationService runMigrations=true reason="service does not require a database" time=2024-06-26T16:54:22.619-07:00 level=DEBUG msg="authorization service client config" config="{ClientID:tdf-authorization-svc ClientSecret:secret TokenURL:http://localhost:8888/auth/realms/opentdf/protocol/openid-connect/token Scopes:[] EndpointParams:map[] AuthStyle:0 authStyleCache:{v:{v:<nil>}}}" time=2024-06-26T16:54:22.619-07:00 level=DEBUG msg="authorization service token source created" token_source="&{new:0x14000412450 mu:{state:0 sema:0} t:<nil> expiryDelta:100}" time=2024-06-26T16:54:22.619-07:00 level=INFO msg="started service" namespace=authorization service=authorization.AuthorizationService time=2024-06-26T16:54:22.619-07:00 level=INFO msg="skipping migrations" namespace=kas service=kas.AccessService runMigrations=true reason="service does not require a database" time=2024-06-26T16:54:22.619-07:00 level=INFO msg="started service" namespace=kas service=kas.AccessService time=2024-06-26T16:54:22.619-07:00 level=INFO msg="starting opentdf" time=2024-06-26T16:54:22.619-07:00 level=INFO msg="services running" time=2024-06-26T16:54:22.619-07:00 level=INFO msg="service running" namespace=wellknown service=wellknownconfiguration.WellKnownService database=false time=2024-06-26T16:54:22.619-07:00 level=INFO msg="service running" namespace=entityresolution service=entityresolution.EntityResolutionService database=false time=2024-06-26T16:54:22.619-07:00 level=INFO msg="service running" namespace=policy service=policy.attributes.AttributesService database=true time=2024-06-26T16:54:22.619-07:00 level=INFO msg="service running" namespace=policy service=policy.namespaces.NamespaceService database=true time=2024-06-26T16:54:22.620-07:00 level=INFO msg="service running" namespace=policy service=policy.resourcemapping.ResourceMappingService database=true time=2024-06-26T16:54:22.620-07:00 level=INFO msg="service running" namespace=policy service=policy.subjectmapping.SubjectMappingService database=true time=2024-06-26T16:54:22.620-07:00 level=INFO msg="service running" namespace=policy service=policy.kasregistry.KeyAccessServerRegistryService database=true time=2024-06-26T16:54:22.620-07:00 level=INFO msg="service running" namespace=health service=grpc.health.v1.Health database=false time=2024-06-26T16:54:22.620-07:00 level=INFO msg="service running" namespace=authorization service=authorization.AuthorizationService database=false time=2024-06-26T16:54:22.620-07:00 level=INFO msg="service running" namespace=kas service=kas.AccessService database=false time=2024-06-26T16:54:22.620-07:00 level=INFO msg="starting http server" address=:8080 time=2024-06-26T16:54:22.620-07:00 level=INFO msg="starting in process grpc server" ``` --------- Co-authored-by: Ryan Schumacher <[email protected]>
1 parent 93d8f70 commit db4f06f

File tree

2 files changed

+47
-23
lines changed

2 files changed

+47
-23
lines changed

service/pkg/db/db_migration.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ func migrationInit(ctx context.Context, c *Client, migrations *embed.FS) (*goose
5555
// RunMigrations runs the migrations for the schema
5656
// Schema will be created if it doesn't exist
5757
func (c *Client) RunMigrations(ctx context.Context, migrations *embed.FS) (int, error) {
58+
if migrations == nil {
59+
return 0, fmt.Errorf("migrations FS is required to run migrations")
60+
}
5861
slog.Info("running migration up", slog.String("schema", c.config.Schema), slog.String("database", c.config.Database))
5962

6063
// Create schema if it doesn't exist

service/pkg/server/services.go

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -67,61 +67,82 @@ func startServices(ctx context.Context, cfg config.Config, otdf *server.OpenTDFS
6767
continue
6868
}
6969

70-
// Use a single database client per namespace
70+
// Use a single database client per namespace and run migrations once per namespace
7171
var d *db.Client
72+
runMigrations := cfg.DB.RunMigrations
7273

7374
for _, r := range registers {
74-
s, err := startService(ctx, cfg, r, otdf, eng, client, d, logger)
75+
s, db, err := startService(ctx, &cfg, r, otdf, eng, client, d, &runMigrations, logger)
7576
if err != nil {
7677
return closeServices, services, err
7778
}
79+
d = db
7880
services = append(services, s)
7981
}
8082
}
8183

8284
return closeServices, services, nil
8385
}
8486

85-
func startService(ctx context.Context, cfg config.Config, s serviceregistry.Service, otdf *server.OpenTDFServer, eng *opa.Engine, client *sdk.SDK, d *db.Client, logger *logger.Logger) (serviceregistry.Service, error) {
86-
// Create the database client if required
87+
func startService(
88+
ctx context.Context,
89+
cfg *config.Config,
90+
s serviceregistry.Service,
91+
otdf *server.OpenTDFServer,
92+
eng *opa.Engine,
93+
client *sdk.SDK,
94+
d *db.Client,
95+
runMigrations *bool,
96+
logger *logger.Logger,
97+
) (serviceregistry.Service, *db.Client, error) {
98+
// Create the database client only if required
8799
if s.DB.Required && d == nil {
88100
var err error
89101

90-
// Conditionally set the db client if the service requires it
91-
// Currently, we are dynamically registering namespaces and don't offer the ability to apply
92-
// config at the NS layer. This poses a problem where services under a NS want to share a
93-
// database connection.
94-
// TODO: this should be reassessed with how we handle registering a single namespace
95102
logger.Info("creating database client", slog.String("namespace", s.Namespace))
96-
// Make sure we only create a single db client per namespace
97103
d, err = db.New(ctx, cfg.DB,
98104
db.WithService(s.Namespace),
99105
db.WithMigrations(s.DB.Migrations),
100106
)
101107
if err != nil {
102-
return s, fmt.Errorf("issue creating database client for %s: %w", s.Namespace, err)
108+
return s, d, fmt.Errorf("issue creating database client for %s: %w", s.Namespace, err)
103109
}
104110
}
105111

106-
// Run migrations if required
107-
if cfg.DB.RunMigrations && d != nil {
108-
if s.DB.Migrations == nil {
109-
return s, fmt.Errorf("migrations FS is required when runMigrations is enabled")
110-
}
111-
112+
// Run migrations IFF a service requires it and they're configured to run but haven't run yet
113+
shouldRun := s.DB.Required && *runMigrations
114+
if shouldRun {
112115
logger.Info("running database migrations")
113116
appliedMigrations, err := d.RunMigrations(ctx, s.DB.Migrations)
114117
if err != nil {
115-
return s, fmt.Errorf("issue running database migrations: %w", err)
118+
return s, d, fmt.Errorf("issue running database migrations: %w", err)
116119
}
117120
logger.Info("database migrations complete",
118121
slog.Int("applied", appliedMigrations),
119122
)
120-
} else {
123+
// Only run migrations once
124+
*runMigrations = false
125+
}
126+
127+
if !shouldRun {
128+
requiredAlreadyRan := s.DB.Required && cfg.DB.RunMigrations && !*runMigrations
129+
noDBRequired := !s.DB.Required
130+
migrationsDisabled := !cfg.DB.RunMigrations
131+
132+
reason := "undetermined"
133+
if requiredAlreadyRan { //nolint:gocritic // This is more readable than a switch
134+
reason = "required migrations already ran"
135+
} else if noDBRequired {
136+
reason = "service does not require a database"
137+
} else if migrationsDisabled {
138+
reason = "migrations are disabled"
139+
}
140+
121141
logger.Info("skipping migrations",
122142
slog.String("namespace", s.Namespace),
123-
slog.String("reason", "runMigrations is false"),
124-
slog.Bool("runMigrations", false),
143+
slog.String("service", s.ServiceDesc.ServiceName),
144+
slog.Bool("configured runMigrations", cfg.DB.RunMigrations),
145+
slog.String("reason", reason),
125146
)
126147
}
127148

@@ -146,7 +167,7 @@ func startService(ctx context.Context, cfg config.Config, s serviceregistry.Serv
146167
// Register the service with the gRPC gateway
147168
if err := handler(ctx, otdf.Mux, impl); err != nil {
148169
logger.Error("failed to start service", slog.String("namespace", s.Namespace), slog.String("error", err.Error()))
149-
return s, err
170+
return s, d, err
150171
}
151172

152173
logger.Info("started service", slog.String("namespace", s.Namespace), slog.String("service", s.ServiceDesc.ServiceName))
@@ -158,5 +179,5 @@ func startService(ctx context.Context, cfg config.Config, s serviceregistry.Serv
158179
d.Close()
159180
}
160181
}
161-
return s, nil
182+
return s, d, nil
162183
}

0 commit comments

Comments
 (0)