Skip to content

Commit 94038b3

Browse files
chores: split reconcilerBase on more specialized helpers (#162)
1 parent 614f84e commit 94038b3

26 files changed

Lines changed: 1023 additions & 983 deletions

api/v1alpha1/clickhousecluster_types.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,6 @@ type ClickHouseCluster struct {
220220

221221
Spec ClickHouseClusterSpec `json:"spec,omitempty"`
222222
Status ClickHouseClusterStatus `json:"status,omitempty"`
223-
224-
specificName string `json:"-"`
225223
}
226224

227225
// ClickHouseReplicaID identifies a ClickHouse replica within the cluster.
@@ -291,22 +289,18 @@ func (v *ClickHouseCluster) GetStatus() *ClickHouseClusterStatus {
291289
return &v.Status
292290
}
293291

294-
// Conditions returns pointer to the conditions slice.
295-
func (v *ClickHouseCluster) Conditions() *[]metav1.Condition {
296-
if v.Status.Conditions == nil {
297-
v.Status.Conditions = []metav1.Condition{}
292+
// GetConditions returns pointer to the conditions slice.
293+
func (v *ClickHouseClusterStatus) GetConditions() *[]metav1.Condition {
294+
if v.Conditions == nil {
295+
v.Conditions = []metav1.Condition{}
298296
}
299297

300-
return &v.Status.Conditions
298+
return &v.Conditions
301299
}
302300

303301
// SpecificName returns cluster name with resource suffix. Used to generate resource names that may be used in DNS.
304302
func (v *ClickHouseCluster) SpecificName() string {
305-
if v.specificName == "" {
306-
v.specificName = normalizeName(v.Name) + "-clickhouse"
307-
}
308-
309-
return v.specificName
303+
return normalizeName(v.Name) + "-clickhouse"
310304
}
311305

312306
// Shards returns requested number of shards in the ClickHouseCluster.

api/v1alpha1/conditions.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package v1alpha1
22

33
// ConditionType represents the type of condition.
4-
type ConditionType string
4+
type ConditionType = string
55

66
// ConditionReason represents the reason for a condition's Status.
7-
type ConditionReason string
7+
type ConditionReason = string
88

99
// Common condition types and reasons for all resources.
1010
const (

api/v1alpha1/keepercluster_types.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,6 @@ type KeeperCluster struct {
184184

185185
Spec KeeperClusterSpec `json:"spec,omitempty"`
186186
Status KeeperClusterStatus `json:"status,omitempty"`
187-
188-
specificName string `json:"-"`
189187
}
190188

191189
// KeeperReplicaID represents ClickHouse Keeper replica ID. Used for naming resources and RAFT configuration.
@@ -226,22 +224,18 @@ func (v *KeeperCluster) GetStatus() *KeeperClusterStatus {
226224
return &v.Status
227225
}
228226

229-
// Conditions returns pointer to the conditions slice.
230-
func (v *KeeperCluster) Conditions() *[]metav1.Condition {
231-
if v.Status.Conditions == nil {
232-
v.Status.Conditions = []metav1.Condition{}
227+
// GetConditions returns pointer to the conditions slice.
228+
func (v *KeeperClusterStatus) GetConditions() *[]metav1.Condition {
229+
if v.Conditions == nil {
230+
v.Conditions = []metav1.Condition{}
233231
}
234232

235-
return &v.Status.Conditions
233+
return &v.Conditions
236234
}
237235

238236
// SpecificName returns cluster name with resource suffix. Used to generate resource names.
239237
func (v *KeeperCluster) SpecificName() string {
240-
if v.specificName == "" {
241-
v.specificName = normalizeName(v.Name) + "-keeper"
242-
}
243-
244-
return v.specificName
238+
return normalizeName(v.Name) + "-keeper"
245239
}
246240

247241
// Replicas returns requested number of replicas in the cluster.

internal/controller/clickhouse/commands.go

Lines changed: 110 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,52 @@ func (cmd *commander) Version(ctx context.Context, id v1.ClickHouseReplicaID) (s
106106
return version, nil
107107
}
108108

109+
func (cmd *commander) SyncDatabases(ctx context.Context, log controllerutil.Logger, replicas []v1.ClickHouseReplicaID) bool {
110+
result := true
111+
112+
replicaDatabases := controllerutil.ExecuteParallel(replicas, func(id v1.ClickHouseReplicaID) (v1.ClickHouseReplicaID, map[string]databaseDescriptor, error) {
113+
databases, err := cmd.Databases(ctx, id)
114+
return id, databases, err
115+
})
116+
117+
databases := map[string]databaseDescriptor{}
118+
for id, dbs := range replicaDatabases {
119+
if dbs.Err != nil {
120+
log.Warn("failed to get databases from replica", "replica_id", id, "error", dbs.Err)
121+
122+
result = false
123+
continue
124+
}
125+
126+
databases = controllerutil.MergeMaps(databases, dbs.Result)
127+
}
128+
129+
results := controllerutil.ExecuteParallel(replicas, func(id v1.ClickHouseReplicaID) (v1.ClickHouseReplicaID, struct{}, error) {
130+
if len(databases) == len(replicaDatabases[id].Result) {
131+
return id, struct{}{}, nil
132+
}
133+
134+
dbsToSync := map[string]databaseDescriptor{}
135+
for name, desc := range databases {
136+
if _, ok := replicaDatabases[id].Result[name]; !ok {
137+
dbsToSync[name] = desc
138+
}
139+
}
140+
141+
return id, struct{}{}, cmd.CreateDatabases(ctx, log, id, dbsToSync)
142+
})
143+
144+
for id, res := range results {
145+
if res.Err != nil {
146+
log.Info("failed to create databases", "replica_id", id, "error", res.Err)
147+
148+
result = false
149+
}
150+
}
151+
152+
return result
153+
}
154+
109155
func (cmd *commander) Databases(ctx context.Context, id v1.ClickHouseReplicaID) (map[string]databaseDescriptor, error) {
110156
conn, err := cmd.getConn(id)
111157
if err != nil {
@@ -169,55 +215,23 @@ func (cmd *commander) CreateDatabases(ctx context.Context, log controllerutil.Lo
169215
return nil
170216
}
171217

172-
// EnsureDefaultDatabaseEngine ensures that the default database engine is set to the Selected one.
173-
func (cmd *commander) EnsureDefaultDatabaseEngine(ctx context.Context, log controllerutil.Logger, id v1.ClickHouseReplicaID) error {
174-
log = log.With("replica_id", id)
175-
176-
conn, err := cmd.getConn(id)
177-
if err != nil {
178-
return fmt.Errorf("failed to get connection for replica %s: %w", id, err)
179-
}
180-
181-
var engine string
182-
183-
rows := conn.QueryRow(ctx, "SELECT engine FROM system.databases WHERE name='default' ")
184-
if err = rows.Scan(&engine); err != nil {
185-
if !errors.Is(err, sql.ErrNoRows) {
186-
return fmt.Errorf("failed to scan default database engine for replica %s: %w", id, err)
187-
}
188-
189-
log.Debug("no default database found")
190-
} else {
191-
if engine == "Replicated" {
192-
log.Debug("default database already has the Replicated engine")
193-
return nil
194-
}
195-
196-
var count uint64
197-
if err = conn.QueryRow(ctx, "SELECT COUNT() FROM system.tables WHERE database='default'").Scan(&count); err != nil {
198-
log.Error(err, "error checking if database 'default' has tables")
199-
return fmt.Errorf("check tables in %s: %w", id, err)
200-
}
201-
202-
if count > 0 {
203-
log.Warn("database `default` has tables, but its engine is not Replicated, data loss is possible")
204-
}
218+
// EnsureDefaultDatabaseEngine ensures that the default database engine is set to the Replicated.
219+
func (cmd *commander) EnsureDefaultDatabaseEngine(ctx context.Context, log controllerutil.Logger, replicas []v1.ClickHouseReplicaID) bool {
220+
res := controllerutil.ExecuteParallel(replicas, func(id v1.ClickHouseReplicaID) (v1.ClickHouseReplicaID, struct{}, error) {
221+
err := cmd.ensureReplicaDefaultDatabaseEngine(ctx, log, id)
222+
return id, struct{}{}, err
223+
})
205224

206-
log.Debug("dropping default database")
225+
result := true
226+
for id, repl := range res {
227+
if repl.Err != nil {
228+
log.Info("failed to recreate default database as Replicated", "replica_id", id, "error", repl.Err)
207229

208-
if err := conn.Exec(ctx, "DROP DATABASE default SYNC"); err != nil {
209-
return fmt.Errorf("failed to drop default database on replica %s: %w", id, err)
230+
result = false
210231
}
211232
}
212233

213-
log.Debug("creating replicated default database")
214-
215-
defaultDatabaseUUID := uuid.NewSHA1(uuid.Nil, []byte(cmd.cluster.SpecificName())).String()
216-
if err = conn.Exec(ctx, createDefaultDatabaseQuery, defaultDatabaseUUID); err != nil {
217-
return fmt.Errorf("create default replicated database %s: %w", id, err)
218-
}
219-
220-
return nil
234+
return result
221235
}
222236

223237
func (cmd *commander) SyncShard(ctx context.Context, log controllerutil.Logger, shardID int32) error {
@@ -322,7 +336,7 @@ func (cmd *commander) SyncReplica(ctx context.Context, log controllerutil.Logger
322336
func (cmd *commander) CleanupDatabaseReplicas(
323337
ctx context.Context,
324338
log controllerutil.Logger,
325-
notInSync map[v1.ClickHouseReplicaID]struct{},
339+
running map[v1.ClickHouseReplicaID]struct{},
326340
) error {
327341
id, conn, err := cmd.getAnyConn(ctx)
328342
if err != nil {
@@ -357,8 +371,8 @@ func (cmd *commander) CleanupDatabaseReplicas(
357371
continue
358372
}
359373

360-
if _, ok := notInSync[toDrop]; ok {
361-
log.Debug("skipping stale database replica cleanup that is not in sync", "database", database, "replica_id", toDrop)
374+
if _, ok := running[toDrop]; ok {
375+
log.Debug("skipping stale database replica cleanup that is still running", "database", database, "replica_id", toDrop)
362376
continue
363377
}
364378

@@ -442,3 +456,52 @@ func (cmd *commander) getAnyConn(ctx context.Context) (v1.ClickHouseReplicaID, c
442456

443457
return v1.ClickHouseReplicaID{}, nil, errors.New("no available connections")
444458
}
459+
460+
func (cmd *commander) ensureReplicaDefaultDatabaseEngine(ctx context.Context, log controllerutil.Logger, id v1.ClickHouseReplicaID) error {
461+
log = log.With("replica_id", id)
462+
463+
conn, err := cmd.getConn(id)
464+
if err != nil {
465+
return fmt.Errorf("failed to get connection for replica %s: %w", id, err)
466+
}
467+
468+
var engine string
469+
470+
rows := conn.QueryRow(ctx, "SELECT engine FROM system.databases WHERE name='default' ")
471+
if err = rows.Scan(&engine); err != nil {
472+
if !errors.Is(err, sql.ErrNoRows) {
473+
return fmt.Errorf("failed to scan default database engine for replica %s: %w", id, err)
474+
}
475+
476+
log.Debug("no default database found")
477+
} else {
478+
if engine == "Replicated" {
479+
return nil
480+
}
481+
482+
var count uint64
483+
if err = conn.QueryRow(ctx, "SELECT COUNT() FROM system.tables WHERE database='default'").Scan(&count); err != nil {
484+
log.Error(err, "error checking if database 'default' has tables")
485+
return fmt.Errorf("check tables in %s: %w", id, err)
486+
}
487+
488+
if count > 0 {
489+
log.Warn("database `default` has tables, but its engine is not Replicated, data loss is possible")
490+
}
491+
492+
log.Debug("dropping default database")
493+
494+
if err := conn.Exec(ctx, "DROP DATABASE default SYNC"); err != nil {
495+
return fmt.Errorf("failed to drop default database on replica %s: %w", id, err)
496+
}
497+
}
498+
499+
log.Debug("creating replicated default database")
500+
501+
defaultDatabaseUUID := uuid.NewSHA1(uuid.Nil, []byte(cmd.cluster.SpecificName())).String()
502+
if err = conn.Exec(ctx, createDefaultDatabaseQuery, defaultDatabaseUUID); err != nil {
503+
return fmt.Errorf("create default replicated database %s: %w", id, err)
504+
}
505+
506+
return nil
507+
}

internal/controller/clickhouse/commands_test.go

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"net"
7+
"slices"
78
"strconv"
89
"strings"
910
"time"
@@ -228,24 +229,19 @@ var _ = Describe("commander", Ordered, Label("integration"), func() {
228229
}
229230
})
230231

231-
Describe("EnsureDefaultDatabaseEngine", func() {
232-
for i := range testReplicas {
233-
It(fmt.Sprintf("converts default database from Atomic to Replicated on %d", i), func(ctx context.Context) {
234-
id := v1.ClickHouseReplicaID{ShardID: 0, Index: i}
235-
236-
By("running EnsureDefaultDatabaseEngine")
232+
It("converts default database from Atomic to Replicated", func(ctx context.Context) {
233+
By("running EnsureDefaultDatabaseEngine")
237234

238-
err := cmd.EnsureDefaultDatabaseEngine(ctx, cmd.log, id)
239-
Expect(err).NotTo(HaveOccurred())
235+
Expect(cmd.EnsureDefaultDatabaseEngine(ctx, cmd.log, slices.Collect(cmd.cluster.ReplicaIDs()))).To(BeTrue())
240236

241-
By("verifying default database is now Replicated")
237+
By("verifying default database is now Replicated")
242238

243-
dbs, err := cmd.Databases(ctx, id)
244-
Expect(err).NotTo(HaveOccurred())
245-
Expect(dbs).To(HaveKey("default"))
246-
Expect(dbs["default"].IsReplicated).To(BeTrue())
247-
Expect(dbs["default"].UUID).To(Equal(uuid.NewSHA1(uuid.Nil, []byte(cmd.cluster.SpecificName())).String()))
248-
})
239+
for id := range cmd.cluster.ReplicaIDs() {
240+
dbs, err := cmd.Databases(ctx, id)
241+
Expect(err).NotTo(HaveOccurred())
242+
Expect(dbs).To(HaveKey("default"))
243+
Expect(dbs["default"].IsReplicated).To(BeTrue())
244+
Expect(dbs["default"].UUID).To(Equal(uuid.NewSHA1(uuid.Nil, []byte(cmd.cluster.SpecificName())).String()))
249245
}
250246
})
251247

internal/controller/clickhouse/config_test.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,20 @@ import (
1212

1313
var _ = Describe("ConfigGenerator", func() {
1414
ctx := clickhouseReconciler{
15-
reconcilerBase: reconcilerBase{
16-
Cluster: &v1.ClickHouseCluster{
17-
ObjectMeta: metav1.ObjectMeta{
18-
Name: "test-cluster",
19-
Namespace: "test-namespace",
20-
},
21-
Spec: v1.ClickHouseClusterSpec{
22-
Replicas: new(int32(3)),
23-
Shards: new(int32(2)),
24-
Settings: v1.ClickHouseSettings{
25-
ExtraConfig: runtime.RawExtension{
26-
Raw: []byte(`{"test": "value"}`),
27-
},
28-
ExtraUsersConfig: runtime.RawExtension{
29-
Raw: []byte(`{}`),
30-
},
15+
Cluster: &v1.ClickHouseCluster{
16+
ObjectMeta: metav1.ObjectMeta{
17+
Name: "test-cluster",
18+
Namespace: "test-namespace",
19+
},
20+
Spec: v1.ClickHouseClusterSpec{
21+
Replicas: new(int32(3)),
22+
Shards: new(int32(2)),
23+
Settings: v1.ClickHouseSettings{
24+
ExtraConfig: runtime.RawExtension{
25+
Raw: []byte(`{"test": "value"}`),
26+
},
27+
ExtraUsersConfig: runtime.RawExtension{
28+
Raw: []byte(`{}`),
3129
},
3230
},
3331
},

0 commit comments

Comments
 (0)