Files
zeekay 9a2c38391e debrand: purge ClickHouse -> datastore + hanzo-ds; datastore:// DSN; no replace
- imports: ClickHouse/ch-go, clickhouse-go/v2, srikanthccv/ClickHouse-go-mock
  -> github.com/hanzo-ds/{native,go,mock} (require, clean semver, NO replace)
- identifiers/aliases/type-names/config-keys/type-IDs: clickhouse -> datastore
  (receiver dir clickhousesystemtablesreceiver -> datastoresystemtablesreceiver)
- DSN scheme clickhouse:// -> datastore:// (driver treats non-http/https as Native)
- removed the go.opencensus.io version-pin replace (require-pinned instead)
- kept: real external refs (github.com/ClickHouse, clickhouse.com docs) and the
  hanzo-ds/mock test API (NewClickHouseNative) pending the mock's own rename
Full ./... build passes. 0 replace directives, 0 signoz.
2026-07-12 19:08:48 -07:00

218 lines
5.8 KiB
Go

package migrate
import (
"context"
"fmt"
"time"
"github.com/hanzo-ds/go"
"github.com/hanzoai/otel-collector/cmd/o11yotelcollector/config"
schemamigrator "github.com/hanzoai/otel-collector/cmd/o11yschemamigrator/schema_migrator"
"github.com/hanzoai/otel-collector/constants"
"github.com/cenkalti/backoff/v4"
"github.com/spf13/cobra"
"go.uber.org/zap"
)
type asyncUp struct {
conn datastore.Conn
cluster string
migrationManager *schemamigrator.MigrationManager
timeout time.Duration
logger *zap.Logger
}
func registerAsyncUp(parentCmd *cobra.Command, logger *zap.Logger) {
syncUpCommand := &cobra.Command{
Use: "up",
Short: "Runs 'up' async migrations for the store. Up async migrations are used to apply new async migrations to the store.",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
up, err := newAsyncUp(config.Datastore.DSN, config.Datastore.Cluster, config.Datastore.Replication, config.MigrateSyncUp.Timeout, logger)
if err != nil {
return err
}
err = up.Run(cmd.Context())
if err != nil {
return err
}
return nil
},
}
config.MigrateAsyncUp.RegisterFlags(syncUpCommand)
parentCmd.AddCommand(syncUpCommand)
}
func newAsyncUp(dsn string, cluster string, replication bool, timeout time.Duration, logger *zap.Logger) (*asyncUp, error) {
opts, err := datastore.ParseDSN(dsn)
if err != nil {
return nil, err
}
conn, err := datastore.Open(opts)
if err != nil {
return nil, err
}
migrationManager, err := schemamigrator.NewMigrationManager(
schemamigrator.WithClusterName(cluster),
schemamigrator.WithReplicationEnabled(replication),
schemamigrator.WithConn(conn),
schemamigrator.WithConnOptions(*opts),
schemamigrator.WithLogger(logger),
)
return &asyncUp{
conn: conn,
cluster: cluster,
migrationManager: migrationManager,
timeout: timeout,
logger: logger.With(zap.String("type", "async"), zap.String("subcommand", "up")),
}, nil
}
func (cmd *asyncUp) Run(ctx context.Context) error {
backoff := backoff.NewExponentialBackOff()
backoff.MaxElapsedTime = cmd.timeout
for {
err := cmd.Up(ctx)
if err == nil {
break
}
migrateErr := Unwrapb(err)
// exit early for non-retryable errors.
if !migrateErr.IsRetryable() {
return fmt.Errorf("failed to run migrations: %w", err)
}
nextBackOff := backoff.NextBackOff()
cmd.logger.Info("Retryable error occurred while running migrations", zap.Error(err), zap.Duration("retry_after", nextBackOff))
if nextBackOff == backoff.Stop {
return fmt.Errorf("timed out waiting for migration to complete within the configured timeout of %s", cmd.timeout)
}
time.Sleep(nextBackOff)
}
return nil
}
func (cmd *asyncUp) Up(ctx context.Context) error {
err := cmd.runSquashedMigrations(ctx)
if err != nil {
return err
}
cmd.logger.Info("Running async migrations")
err = cmd.run(ctx, schemamigrator.TracesMigrations, schemamigrator.O11yTracesDB)
if err != nil {
return err
}
logsMigrations := schemamigrator.LogsMigrations
if constants.EnableLogsMigrationsV2 {
logsMigrations = schemamigrator.LogsMigrationsV2
}
err = cmd.run(ctx, logsMigrations, schemamigrator.O11yLogsDB)
if err != nil {
return err
}
err = cmd.run(ctx, schemamigrator.MetricsMigrations, schemamigrator.O11yMetricsDB)
if err != nil {
return err
}
err = cmd.run(ctx, schemamigrator.MetadataMigrations, schemamigrator.O11yMetadataDB)
if err != nil {
return err
}
err = cmd.run(ctx, schemamigrator.AnalyticsMigrations, schemamigrator.O11yAnalyticsDB)
if err != nil {
return err
}
err = cmd.run(ctx, schemamigrator.MeterMigrations, schemamigrator.O11yMeterDB)
if err != nil {
return err
}
return nil
}
func (cmd *asyncUp) runSquashedMigrations(ctx context.Context) error {
squashedMigrations := map[string][]schemamigrator.SchemaMigrationRecord{
schemamigrator.O11yLogsDB: schemamigrator.CustomRetentionLogsMigrations,
schemamigrator.O11yMetricsDB: schemamigrator.SquashedMetricsMigrations,
schemamigrator.O11yTracesDB: schemamigrator.SquashedTracesMigrations,
}
for database, migrations := range squashedMigrations {
cmd.logger.Info("checking if should run squashed migrations", zap.String("database", database))
should, err := cmd.migrationManager.ShouldRunSquashedV2(ctx, database)
if err != nil {
return NewRetryableError(err)
}
if !should {
cmd.logger.Info("skipping squashed migrations", zap.String("database", database))
return nil
}
cmd.logger.Info("running squashed migrations", zap.String("database", database))
err = cmd.run(ctx, migrations, database)
if err != nil {
return err
}
}
return nil
}
func (cmd *asyncUp) run(ctx context.Context, migrations []schemamigrator.SchemaMigrationRecord, db string) error {
for _, migration := range migrations {
if !cmd.migrationManager.IsAsync(migration) {
continue
}
ok, err := cmd.migrationManager.CheckMigrationStatus(ctx, db, migration.MigrationID, schemamigrator.FinishedStatus)
if err != nil {
return NewRetryableError(err)
}
if ok {
continue
}
for _, item := range migration.UpItems {
if err := cmd.migrationManager.RunOperationWithoutUpdate(ctx, item, migration.MigrationID, db); err != nil {
cmd.logger.Error("Error occurred while running operation", zap.Error(err))
// if any one of the operations fails, mark the migration as failed
if err := cmd.migrationManager.InsertMigrationEntry(ctx, db, migration.MigrationID, schemamigrator.FailedStatus); err != nil {
return err
}
return err
}
}
// if all the operations succeed, mark the migration as finished
if err := cmd.migrationManager.InsertMigrationEntry(ctx, db, migration.MigrationID, schemamigrator.FinishedStatus); err != nil {
return err
}
}
return nil
}