diff --git a/README.md b/README.md index 533c257ea..d0ac06734 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Examples: GOOSE_DRIVER=mysql GOOSE_DBSTRING="user:password@/dbname" goose status GOOSE_DRIVER=redshift GOOSE_DBSTRING="postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" goose status GOOSE_DRIVER=clickhouse GOOSE_DBSTRING="clickhouse://user:password@qwerty.clickhouse.cloud:9440/dbname?secure=true&skip_verify=false" goose status + GOOSE_DRIVER=clickhouse-replicated GOOSE_CLICKHOUSE_CLUSTER_NAME=example GOOSE_DBSTRING="clickhouse://user:password@qwerty.clickhouse.cloud:9440/dbname?secure=true&skip_verify=false" goose status Options: diff --git a/cmd/goose/main.go b/cmd/goose/main.go index 8a8f86f92..b83b040b8 100644 --- a/cmd/goose/main.go +++ b/cmd/goose/main.go @@ -269,9 +269,10 @@ Examples: goose tidb "user:password@/dbname?parseTime=true" status goose mssql "sqlserver://user:password@dbname:1433?database=master" status goose clickhouse "tcp://127.0.0.1:9000" status + goose clickhouse-replicated "tcp://127.0.0.1:9000" status goose vertica "vertica://user:password@localhost:5433/dbname?connection_load_balance=1" status goose ydb "grpcs://localhost:2135/local?go_query_mode=scripting&go_fake_tx=scripting&go_query_bind=declare,numeric" status - goose turso "libsql://dbname.turso.io?authToken=token" status + goose turso "libsql://dbname.turso.io?authToken=token" status GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose status GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose create init sql @@ -279,7 +280,8 @@ Examples: GOOSE_DRIVER=mysql GOOSE_DBSTRING="user:password@/dbname" goose status GOOSE_DRIVER=redshift GOOSE_DBSTRING="postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" goose status GOOSE_DRIVER=turso GOOSE_DBSTRING="libsql://dbname.turso.io?authToken=token" goose status - GOOSE_DRIVER=clickhouse GOOSE_DBSTRING="clickhouse://user:password@qwerty.clickhouse.cloud:9440/dbname?secure=true&skip_verify=false" goose status + GOOSE_DRIVER=clickhouse GOOSE_DBSTRING="clickhouse://user:password@qwerty.clickhouse.cloud:9440/dbname?secure=true&skip_verify=false" goose status + GOOSE_DRIVER=clickhouse-replicated GOOSE_CLICKHOUSE_CLUSTER_NAME=example GOOSE_DBSTRING="clickhouse://user:password@qwerty.clickhouse.cloud:9440/dbname?secure=true&skip_verify=false" goose status Options: ` @@ -302,23 +304,23 @@ Commands: ) var sqlMigrationTemplate = template.Must(template.New("goose.sql-migration").Parse(`-- Thank you for giving goose a try! --- +-- -- This file was automatically created running goose init. If you're familiar with goose -- feel free to remove/rename this file, write some SQL and goose up. Briefly, --- +-- -- Documentation can be found here: https://pressly.github.io/goose -- -- A single goose .sql file holds both Up and Down migrations. --- +-- -- All goose .sql files are expected to have a -- +goose Up annotation. -- The -- +goose Down annotation is optional, but recommended, and must come after the Up annotation. --- --- The -- +goose NO TRANSACTION annotation may be added to the top of the file to run statements +-- +-- The -- +goose NO TRANSACTION annotation may be added to the top of the file to run statements -- outside a transaction. Both Up and Down migrations within this file will be run without a transaction. --- --- More complex statements that have semicolons within them must be annotated with +-- +-- More complex statements that have semicolons within them must be annotated with -- the -- +goose StatementBegin and -- +goose StatementEnd annotations to be properly recognized. --- +-- -- Use GitHub issues for reporting bugs and requesting features, enjoy! -- +goose Up diff --git a/database/dialect.go b/database/dialect.go index ba2da5cab..570062a01 100644 --- a/database/dialect.go +++ b/database/dialect.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" + "github.com/pressly/goose/v3/internal/cfg" "github.com/pressly/goose/v3/internal/dialect/dialectquery" ) @@ -13,17 +14,18 @@ import ( type Dialect string const ( - DialectClickHouse Dialect = "clickhouse" - DialectMSSQL Dialect = "mssql" - DialectMySQL Dialect = "mysql" - DialectPostgres Dialect = "postgres" - DialectRedshift Dialect = "redshift" - DialectSQLite3 Dialect = "sqlite3" - DialectTiDB Dialect = "tidb" - DialectTurso Dialect = "turso" - DialectVertica Dialect = "vertica" - DialectYdB Dialect = "ydb" - DialectStarrocks Dialect = "starrocks" + DialectClickHouse Dialect = "clickhouse" + DialectClickHouseReplicated Dialect = "clickhouse-replicated" + DialectMSSQL Dialect = "mssql" + DialectMySQL Dialect = "mysql" + DialectPostgres Dialect = "postgres" + DialectRedshift Dialect = "redshift" + DialectSQLite3 Dialect = "sqlite3" + DialectTiDB Dialect = "tidb" + DialectTurso Dialect = "turso" + DialectVertica Dialect = "vertica" + DialectYdB Dialect = "ydb" + DialectStarrocks Dialect = "starrocks" ) // NewStore returns a new [Store] implementation for the given dialect. @@ -36,16 +38,19 @@ func NewStore(dialect Dialect, tablename string) (Store, error) { } lookup := map[Dialect]dialectquery.Querier{ DialectClickHouse: &dialectquery.Clickhouse{}, - DialectMSSQL: &dialectquery.Sqlserver{}, - DialectMySQL: &dialectquery.Mysql{}, - DialectPostgres: &dialectquery.Postgres{}, - DialectRedshift: &dialectquery.Redshift{}, - DialectSQLite3: &dialectquery.Sqlite3{}, - DialectTiDB: &dialectquery.Tidb{}, - DialectVertica: &dialectquery.Vertica{}, - DialectYdB: &dialectquery.Ydb{}, - DialectTurso: &dialectquery.Turso{}, - DialectStarrocks: &dialectquery.Starrocks{}, + DialectClickHouseReplicated: &dialectquery.ClickhouseReplicated{ + ClusterName: cfg.GOOSECLICKHOUSECLUSTERNAME, + }, + DialectMSSQL: &dialectquery.Sqlserver{}, + DialectMySQL: &dialectquery.Mysql{}, + DialectPostgres: &dialectquery.Postgres{}, + DialectRedshift: &dialectquery.Redshift{}, + DialectSQLite3: &dialectquery.Sqlite3{}, + DialectTiDB: &dialectquery.Tidb{}, + DialectVertica: &dialectquery.Vertica{}, + DialectYdB: &dialectquery.Ydb{}, + DialectTurso: &dialectquery.Turso{}, + DialectStarrocks: &dialectquery.Starrocks{}, } querier, ok := lookup[dialect] if !ok { diff --git a/internal/cfg/cfg.go b/internal/cfg/cfg.go index aa9707633..a52700ebd 100644 --- a/internal/cfg/cfg.go +++ b/internal/cfg/cfg.go @@ -3,9 +3,10 @@ package cfg import "os" var ( - GOOSEDRIVER = envOr("GOOSE_DRIVER", "") - GOOSEDBSTRING = envOr("GOOSE_DBSTRING", "") - GOOSEMIGRATIONDIR = envOr("GOOSE_MIGRATION_DIR", DefaultMigrationDir) + GOOSECLICKHOUSECLUSTERNAME = envOr("GOOSE_CLICKHOUSE_CLUSTER_NAME", "{cluster}") + GOOSEDRIVER = envOr("GOOSE_DRIVER", "") + GOOSEDBSTRING = envOr("GOOSE_DBSTRING", "") + GOOSEMIGRATIONDIR = envOr("GOOSE_MIGRATION_DIR", DefaultMigrationDir) // https://no-color.org/ GOOSENOCOLOR = envOr("NO_COLOR", "false") ) @@ -22,6 +23,7 @@ type EnvVar struct { func List() []EnvVar { return []EnvVar{ + {Name: "GOOSE_CLICKHOUSER_CLUSTER_NAME", Value: GOOSECLICKHOUSECLUSTERNAME}, {Name: "GOOSE_DRIVER", Value: GOOSEDRIVER}, {Name: "GOOSE_DBSTRING", Value: GOOSEDBSTRING}, {Name: "GOOSE_MIGRATION_DIR", Value: GOOSEMIGRATIONDIR}, diff --git a/internal/dialect/dialectquery/clickhouse.go b/internal/dialect/dialectquery/clickhouse.go index 8c046fb56..fc914c2a7 100644 --- a/internal/dialect/dialectquery/clickhouse.go +++ b/internal/dialect/dialectquery/clickhouse.go @@ -43,12 +43,14 @@ func (c *Clickhouse) GetLatestVersion(tableName string) string { return fmt.Sprintf(q, tableName) } -type ClickhouseReplicated struct{} +type ClickhouseReplicated struct { + ClusterName string +} var _ Querier = (*ClickhouseReplicated)(nil) func (c *ClickhouseReplicated) CreateTable(tableName string) string { - q := `CREATE TABLE IF NOT EXISTS %s ON CLUSTER '{cluster}' ( + q := `CREATE TABLE IF NOT EXISTS %s ON CLUSTER '%s' ( version_id Int64, is_applied UInt8, date Date default now(), @@ -56,7 +58,7 @@ func (c *ClickhouseReplicated) CreateTable(tableName string) string { ) ENGINE = ReplicatedMergeTree() ORDER BY (date)` - return fmt.Sprintf(q, tableName) + return fmt.Sprintf(q, tableName, c.ClusterName) } func (c *ClickhouseReplicated) InsertVersion(tableName string) string { diff --git a/internal/dialect/store.go b/internal/dialect/store.go index cac7b244d..766a6d9fd 100644 --- a/internal/dialect/store.go +++ b/internal/dialect/store.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/pressly/goose/v3/internal/cfg" "github.com/pressly/goose/v3/internal/dialect/dialectquery" ) @@ -64,7 +65,9 @@ func NewStore(d Dialect) (Store, error) { case Clickhouse: querier = &dialectquery.Clickhouse{} case ClickhouseReplicated: - querier = &dialectquery.ClickhouseReplicated{} + querier = &dialectquery.ClickhouseReplicated{ + ClusterName: cfg.GOOSECLICKHOUSECLUSTERNAME, + } case Vertica: querier = &dialectquery.Vertica{} case Ydb: diff --git a/internal/testing/integration/database_test.go b/internal/testing/integration/database_test.go index 649020255..0ea75761d 100644 --- a/internal/testing/integration/database_test.go +++ b/internal/testing/integration/database_test.go @@ -88,6 +88,27 @@ func TestClickhouseRemote(t *testing.T) { require.Equal(t, 265, count) } +func TestClickhouseReplicated(t *testing.T) { + t.Parallel() + + db0, db1, cleanup, err := testdb.NewClickHouseReplicated(testdb.WithDebug(false)) + require.NoError(t, err) + t.Cleanup(cleanup) + + testDatabase(t, database.DialectClickHouseReplicated, db0, "testdata/migrations/clickhouse-replicated") + + rows, err := db1.Query(`SELECT count(*) FROM clickstream`) + require.NoError(t, err) + var result int + for rows.Next() { + err = rows.Scan(&result) + require.NoError(t, err) + } + require.Equal(t, result, 3) + require.NoError(t, rows.Close()) + require.NoError(t, rows.Err()) +} + func TestMySQL(t *testing.T) { t.Parallel() diff --git a/internal/testing/integration/testdata/migrations/clickhouse-replicated/00001_a.sql b/internal/testing/integration/testdata/migrations/clickhouse-replicated/00001_a.sql new file mode 100644 index 000000000..2c2cbfccf --- /dev/null +++ b/internal/testing/integration/testdata/migrations/clickhouse-replicated/00001_a.sql @@ -0,0 +1,55 @@ +-- +goose Up +CREATE TABLE IF NOT EXISTS trips ON CLUSTER '{cluster}' +( + `trip_id` UInt32, + `vendor_id` Enum8('1' = 1, '2' = 2, '3' = 3, '4' = 4, 'CMT' = 5, 'VTS' = 6, 'DDS' = 7, 'B02512' = 10, 'B02598' = 11, 'B02617' = 12, 'B02682' = 13, 'B02764' = 14, '' = 15), + `pickup_date` Date, + `pickup_datetime` DateTime, + `dropoff_date` Date, + `dropoff_datetime` DateTime, + `store_and_fwd_flag` UInt8, + `rate_code_id` UInt8, + `pickup_longitude` Float64, + `pickup_latitude` Float64, + `dropoff_longitude` Float64, + `dropoff_latitude` Float64, + `passenger_count` UInt8, + `trip_distance` Float64, + `fare_amount` Float32, + `extra` Float32, + `mta_tax` Float32, + `tip_amount` Float32, + `tolls_amount` Float32, + `ehail_fee` Float32, + `improvement_surcharge` Float32, + `total_amount` Float32, + `payment_type` Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4), + `trip_type` UInt8, + `pickup` FixedString(25), + `dropoff` FixedString(25), + `cab_type` Enum8('yellow' = 1, 'green' = 2, 'uber' = 3), + `pickup_nyct2010_gid` Int8, + `pickup_ctlabel` Float32, + `pickup_borocode` Int8, + `pickup_ct2010` String, + `pickup_boroct2010` FixedString(7), + `pickup_cdeligibil` String, + `pickup_ntacode` FixedString(4), + `pickup_ntaname` String, + `pickup_puma` UInt16, + `dropoff_nyct2010_gid` UInt8, + `dropoff_ctlabel` Float32, + `dropoff_borocode` UInt8, + `dropoff_ct2010` String, + `dropoff_boroct2010` FixedString(7), + `dropoff_cdeligibil` String, + `dropoff_ntacode` FixedString(4), + `dropoff_ntaname` String, + `dropoff_puma` UInt16 +) +ENGINE = ReplicatedMergeTree() +PARTITION BY toYYYYMM(pickup_date) +ORDER BY pickup_datetime; + +-- +goose Down +DROP TABLE IF EXISTS trips ON CLUSTER '{cluster}' SYNC; diff --git a/internal/testing/integration/testdata/migrations/clickhouse-replicated/00002_b.sql b/internal/testing/integration/testdata/migrations/clickhouse-replicated/00002_b.sql new file mode 100644 index 000000000..82a3f7692 --- /dev/null +++ b/internal/testing/integration/testdata/migrations/clickhouse-replicated/00002_b.sql @@ -0,0 +1,13 @@ +-- +goose Up +CREATE TABLE IF NOT EXISTS clickstream ON CLUSTER '{cluster}' ( + customer_id String, + time_stamp Date, + click_event_type String, + country_code FixedString(2), + source_id UInt64 +) +ENGINE = ReplicatedMergeTree() +ORDER BY (time_stamp); + +-- +goose Down +DROP TABLE IF EXISTS clickstream ON CLUSTER '{cluster}' SYNC; diff --git a/internal/testing/integration/testdata/migrations/clickhouse-replicated/00003_c.sql b/internal/testing/integration/testdata/migrations/clickhouse-replicated/00003_c.sql new file mode 100644 index 000000000..ecf714b58 --- /dev/null +++ b/internal/testing/integration/testdata/migrations/clickhouse-replicated/00003_c.sql @@ -0,0 +1,8 @@ +-- +goose Up +INSERT INTO clickstream VALUES ('customer1', '2021-10-02', 'add_to_cart', 'US', 568239 ); + +INSERT INTO clickstream (customer_id, time_stamp, click_event_type) VALUES ('customer2', '2021-10-30', 'remove_from_cart' ); + +INSERT INTO clickstream (* EXCEPT(country_code)) VALUES ('customer3', '2021-11-07', 'checkout', 307493 ); + +-- +goose Down diff --git a/internal/testing/testdb/clickhouse-replicated.go b/internal/testing/testdb/clickhouse-replicated.go new file mode 100644 index 000000000..ba0e764f6 --- /dev/null +++ b/internal/testing/testdb/clickhouse-replicated.go @@ -0,0 +1,133 @@ +package testdb + +import ( + "context" + "database/sql" + "fmt" + "log" + "path/filepath" + "strings" + "time" + + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" + "github.com/sethvargo/go-retry" +) + +const clickhouseReplicatedNetworkName = "goose-clickhouse-replicated-tests" + +func newClickHouseReplicated(opts ...OptionsFunc) (*sql.DB, *sql.DB, func(), error) { + option := &options{} + for _, f := range opts { + f(option) + } + + pool, err := dockertest.NewPool("") + if err != nil { + return nil, nil, nil, err + } + + net, err := pool.CreateNetwork(clickhouseReplicatedNetworkName) + if err != nil { + if !strings.Contains(err.Error(), "already exists") { + return nil, nil, nil, err + } + + nets, err := pool.NetworksByName(clickhouseReplicatedNetworkName) + if err != nil { + return nil, nil, nil, err + } + if len(nets) != 1 { + return nil, nil, nil, fmt.Errorf("found more than one network with name %s", clickhouseReplicatedNetworkName) + } + + net = &nets[0] + } + + zk, err := startZookeeper(pool, net) + if err != nil { + return nil, nil, nil, err + } + + path, err := filepath.Abs("./../../../") + if err != nil { + return nil, nil, nil, err + } + + db0, cleanup0, err := NewClickHouse( + WithName("clickhouse0"), + WithEnv([]string{"REPLICA_NAME=clickhouse0"}), + WithNetwork(net), + WithMounts([]string{ + path + "/testdata/clickhouse-replicated:/etc/clickhouse-server/conf.d", + })) + if err != nil { + return nil, nil, nil, err + } + + db1, cleanup1, err := NewClickHouse( + WithName("clickhouse1"), + WithEnv([]string{"REPLICA_NAME=clickhouse1"}), + WithNetwork(net), + WithMounts([]string{ + path + "/testdata/clickhouse-replicated:/etc/clickhouse-server/conf.d", + })) + if err != nil { + return nil, nil, nil, err + } + + cleanup := func() { + if option.debug { + return + } + + cleanup0() + cleanup1() + if err := pool.Purge(zk); err != nil { + log.Printf("failed to purge resource: %v", err) + } + if err := pool.RemoveNetwork(net); err != nil { + log.Printf("failed to purge network %s: %v", net.Network.Name, err) + } + } + + return db0, db1, cleanup, nil +} + +func startZookeeper(pool *dockertest.Pool, net *dockertest.Network) (*dockertest.Resource, error) { + runOptions := &dockertest.RunOptions{ + Name: "zookeeper", + Repository: "zookeeper", + Tag: "3.7.2", + Labels: map[string]string{"goose_test": "1"}, + PortBindings: make(map[docker.Port][]docker.PortBinding), + NetworkID: net.Network.ID, + } + zk, err := pool.RunWithOptions( + runOptions, + func(config *docker.HostConfig) { + // Set AutoRemove to true so that stopped container goes away by itself. + config.AutoRemove = true + config.RestartPolicy = docker.RestartPolicy{Name: "no"} + }, + ) + if err != nil { + return nil, err + } + + backoff := retry.WithMaxDuration(1*time.Minute, retry.NewConstant(2*time.Second)) + if err := retry.Do(context.Background(), backoff, func(ctx context.Context) error { + exitCode, err := zk.Exec([]string{"zkCli.sh", "ls", "/"}, dockertest.ExecOptions{}) + if err != nil { + return retry.RetryableError(err) + } + if exitCode != 0 { + return retry.RetryableError(fmt.Errorf("zk cmd returns %d", exitCode)) + } + return nil + }); err != nil { + return nil, fmt.Errorf("could not connect to docker database: %v", err) + } + + return zk, nil +} diff --git a/internal/testing/testdb/clickhouse.go b/internal/testing/testdb/clickhouse.go index ae5b50013..39016dbed 100644 --- a/internal/testing/testdb/clickhouse.go +++ b/internal/testing/testdb/clickhouse.go @@ -34,6 +34,7 @@ func newClickHouse(opts ...OptionsFunc) (*sql.DB, func(), error) { if err != nil { return nil, nil, err } + runOptions := &dockertest.RunOptions{ Repository: CLICKHOUSE_IMAGE, Tag: CLICKHOUSE_VERSION, @@ -46,6 +47,18 @@ func newClickHouse(opts ...OptionsFunc) (*sql.DB, func(), error) { Labels: map[string]string{"goose_test": "1"}, PortBindings: make(map[docker.Port][]docker.PortBinding), } + if option.network != nil { + runOptions.NetworkID = option.network.Network.ID + } + if len(option.name) > 0 { + runOptions.Name = option.name + } + if len(option.mounts) > 0 { + runOptions.Mounts = option.mounts + } + if len(option.env) > 0 { + runOptions.Env = append(runOptions.Env, option.env...) + } // Port 8123 is used for HTTP, but we're using the TCP protocol endpoint (port 9000). // Ref: https://clickhouse.com/docs/en/interfaces/http/ // Ref: https://clickhouse.com/docs/en/interfaces/tcp/ @@ -62,6 +75,7 @@ func newClickHouse(opts ...OptionsFunc) (*sql.DB, func(), error) { config.RestartPolicy = docker.RestartPolicy{Name: "no"} }, ) + if err != nil { return nil, nil, err } @@ -100,6 +114,9 @@ func clickHouseOpenDB(address string, tlsConfig *tls.Config, debug bool) *sql.DB TLS: tlsConfig, Settings: clickhouse.Settings{ "max_execution_time": 60, + // Next settings allows to execute queries on clickhouse replicated cluster with same manner as single node cluster + "insert_quorum": 2, + "select_sequential_consistency": 1, }, DialTimeout: 5 * time.Second, Compression: &clickhouse.Compression{ @@ -107,8 +124,8 @@ func clickHouseOpenDB(address string, tlsConfig *tls.Config, debug bool) *sql.DB }, Debug: debug, }) - db.SetMaxIdleConns(5) - db.SetMaxOpenConns(10) + db.SetMaxIdleConns(1) + db.SetMaxOpenConns(1) db.SetConnMaxLifetime(time.Hour) return db } diff --git a/internal/testing/testdb/options.go b/internal/testing/testdb/options.go index bd8681208..376a140cf 100644 --- a/internal/testing/testdb/options.go +++ b/internal/testing/testdb/options.go @@ -1,6 +1,12 @@ package testdb +import "github.com/ory/dockertest/v3" + type options struct { + env []string + mounts []string + network *dockertest.Network + name string bindPort int debug bool } @@ -14,3 +20,19 @@ func WithBindPort(n int) OptionsFunc { func WithDebug(b bool) OptionsFunc { return func(o *options) { o.debug = b } } + +func WithMounts(m []string) OptionsFunc { + return func(o *options) { o.mounts = m } +} + +func WithName(n string) OptionsFunc { + return func(o *options) { o.name = n } +} + +func WithEnv(e []string) OptionsFunc { + return func(o *options) { o.env = e } +} + +func WithNetwork(n *dockertest.Network) OptionsFunc { + return func(o *options) { o.network = n } +} diff --git a/internal/testing/testdb/testdb.go b/internal/testing/testdb/testdb.go index 72a2b19d5..80acdefb9 100644 --- a/internal/testing/testdb/testdb.go +++ b/internal/testing/testdb/testdb.go @@ -7,6 +7,11 @@ func NewClickHouse(options ...OptionsFunc) (db *sql.DB, cleanup func(), err erro return newClickHouse(options...) } +// NewClickHouseReplicated starts Zookeeper and two ClickHouse docker containers. Returns db connections for each db and a docker cleanup function. +func NewClickHouseReplicated(options ...OptionsFunc) (db0 *sql.DB, db1 *sql.DB, cleanup func(), err error) { + return newClickHouseReplicated(options...) +} + // NewPostgres starts a PostgreSQL docker container. Returns db connection and a docker cleanup function. func NewPostgres(options ...OptionsFunc) (db *sql.DB, cleanup func(), err error) { return newPostgres(options...) diff --git a/testdata/clickhouse-replicated/macros.xml b/testdata/clickhouse-replicated/macros.xml new file mode 100644 index 000000000..972fc083a --- /dev/null +++ b/testdata/clickhouse-replicated/macros.xml @@ -0,0 +1,7 @@ + + + goose + 0 + + + diff --git a/testdata/clickhouse-replicated/remote_servers.xml b/testdata/clickhouse-replicated/remote_servers.xml new file mode 100644 index 000000000..d1763eb87 --- /dev/null +++ b/testdata/clickhouse-replicated/remote_servers.xml @@ -0,0 +1,17 @@ + + + + + true + + clickhouse0 + 9000 + + + clickhouse1 + 9000 + + + + + diff --git a/testdata/clickhouse-replicated/zookeeper.xml b/testdata/clickhouse-replicated/zookeeper.xml new file mode 100644 index 000000000..ec539cb14 --- /dev/null +++ b/testdata/clickhouse-replicated/zookeeper.xml @@ -0,0 +1,11 @@ + + /clickhouse/{cluster}/tables/{shard}/{database}/{table} + {replica} + + + + zookeeper + 2181 + + +