Repository navigation
fix(migrations): pre-create clickhouse tracking tables by cluster topology - #88666
posthog[bot] wants to merge 2 commits into
Conversation
…ology Key the ON CLUSTER pre-create of the migration tracking tables on the real migrations cluster topology, read from system.clusters, instead of the MULTINODE_CLICKHOUSE flag. The flag defaults to False, so a multi-node posthog_migrations cluster fell through to infi's auto-create, which issues CREATE TABLE without ON CLUSTER and hangs the migration step. Also pass an explicit, generous timeout to the migration Database so slow ON CLUSTER statements do not fail at the 60 second default. Generated-By: PostHog Desktop Task-Id: b0a3b493-63c5-46a5-a660-09226dbff717
🦔 PostHog Review reviewed this pull requestFound 0 must fix, 0 should fix, 1 consider. Published 1 finding (view the review). |
|
This changes ClickHouse migration bootstrap logic (cluster-topology detection driving
Gate mechanics and policy version
|
|
ReviewHog Alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
…house The comment claimed an undefined migrations cluster "needs no pre-create" and that infi's auto-create "lands on the one node and the SELECT reads it back". That is inaccurate: infi always renders `ON CLUSTER <cluster>` when the Database is built with a cluster, so an undefined cluster (0 rows in system.clusters) fails loudly with CLUSTER_DOESNT_EXIST rather than being handled. Correct the comment to describe the real behavior — the single-node case is handled by auto-create, the undefined case fails early, which is acceptable for an invalid configuration. Generated-By: PostHog Desktop Task-Id: 3f283726-6660-4e2b-87fc-6f99687bb91d
|
This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the |
|
This PR was closed due to lack of activity. Feel free to reopen if it's still relevant. |
| def _create_migration_tracking_tables_if_not_exist(self, database: str, cluster: str, multi_node: bool): | ||
| # infi.clickhouse_orm's auto-create path issues `CREATE TABLE` without | ||
| # `ON CLUSTER`, so the underlying ReplicatedMergeTree only lands on the | ||
| # migrations host. With a real multi-node `posthog_migrations` cluster, | ||
| # the Distributed tracking table fans out to every shard and trips | ||
| # UNKNOWN_TABLE on satellites that never received the local replica. | ||
| # Pre-create both tables on the cluster so the very first SELECT in | ||
| # infi's migrate() succeeds and the auto-create branch never runs. |
There was a problem hiding this comment.
Correct the auto-create explanation
Why we think it's a valid issue
- Checked: The
infi.clickhouse_ormauto-create path in the installed venv, the two tracking models it creates,on_cluster(), theDatabaseconstructor, and the value ofCLICKHOUSE_MIGRATIONS_CLUSTER. - Found: The auto-create path calls
create_table(MigrationHistoryReplicated)andcreate_table(MigrationHistoryDistributed)(database.py:364-378). Both models subclass plainModel(migrations.py:289,migrations.py:309), so both useModel.create_table_sql, which renderson_cluster(db)into the statement (models.py:355).on_cluster()returnsON CLUSTER '<cluster>'wheneverdb.cluster is not None(utils.py:158-162). - Found:
db.clusteris always set on this path.Database.__init__stores it (database.py:109),migrate_clickhouse.py:71passescluster=CLICKHOUSE_MIGRATIONS_CLUSTER, and that setting defaults to the non-empty string"posthog_migrations"(posthog/settings/data_stores.py:342). It is neverNone. So infi's auto-create does emitON CLUSTER, and the comment atposthog/management/commands/migrate_clickhouse.py:167-168states the opposite. - Found: The same file already contradicts itself. Line 143 says "infi still builds the tracking tables with
ON CLUSTER <cluster>" — the correct behavior, added by commit feb766a, which fixed this exact misconception in the neighboring comment. Lines 167-171 still carry the old, wrong version, including theUNKNOWN_TABLE-on-satellites failure chain that cannot happen once the DDL runsON CLUSTER. - Impact: The only stated justification for
_create_migration_tracking_tables_if_not_existis false, and it disagrees with another comment 25 lines above it. A future maintainer reading the two comments cannot tell which one describes infi. That maintainer may delete the helper as unnecessary, or re-derive the guard from the wrong failure mode, in tooling that runs DDL against production clusters. The real reasons — the helper creates the tables through the native client before infi's HTTP request path reads the Distributed table, and it removes the failed-SELECT-then-create retry — are absent. - Impact: No behavior changes, so this stays at the reviewer's lowest priority. It is not a wording preference, however: the claim is objectively wrong and self-contradictory inside the added code, so it does not fall under the style exclusion.
Issue description
The earlier review thread confirmed that infi adds ON CLUSTER when db.cluster is set. This comment still says that auto-create omits it. Both tracking models inherit Model, whose create_table_sql calls on_cluster(db). The comment therefore gives the wrong reason for this pre-create path.
Suggested fix
Explain that this helper creates the cluster tables before infi reads the Distributed table. It also moves creation to the native client. Do not state that infi omits ON CLUSTER.
Prompt to fix with AI (copy-paste)
## Context
@posthog/management/commands/migrate_clickhouse.py#L166-173
<issue_description>
The earlier review thread confirmed that infi adds `ON CLUSTER` when `db.cluster` is set. This comment still says that auto-create omits it. Both tracking models inherit `Model`, whose `create_table_sql` calls `on_cluster(db)`. The comment therefore gives the wrong reason for this pre-create path.
</issue_description>
<issue_validation>
- **Checked:** The `infi.clickhouse_orm` auto-create path in the installed venv, the two tracking models it creates, `on_cluster()`, the `Database` constructor, and the value of `CLICKHOUSE_MIGRATIONS_CLUSTER`.
- **Found:** The auto-create path calls `create_table(MigrationHistoryReplicated)` and `create_table(MigrationHistoryDistributed)` (`database.py:364-378`). Both models subclass plain `Model` (`migrations.py:289`, `migrations.py:309`), so both use `Model.create_table_sql`, which renders `on_cluster(db)` into the statement (`models.py:355`). `on_cluster()` returns `ON CLUSTER '<cluster>'` whenever `db.cluster is not None` (`utils.py:158-162`).
- **Found:** `db.cluster` is always set on this path. `Database.__init__` stores it (`database.py:109`), `migrate_clickhouse.py:71` passes `cluster=CLICKHOUSE_MIGRATIONS_CLUSTER`, and that setting defaults to the non-empty string `"posthog_migrations"` (`posthog/settings/data_stores.py:342`). It is never `None`. So infi's auto-create does emit `ON CLUSTER`, and the comment at `posthog/management/commands/migrate_clickhouse.py:167-168` states the opposite.
- **Found:** The same file already contradicts itself. Line 143 says "infi still builds the tracking tables with `ON CLUSTER <cluster>`" — the correct behavior, added by commit feb766abb, which fixed this exact misconception in the neighboring comment. Lines 167-171 still carry the old, wrong version, including the `UNKNOWN_TABLE`-on-satellites failure chain that cannot happen once the DDL runs `ON CLUSTER`.
- **Impact:** The only stated justification for `_create_migration_tracking_tables_if_not_exist` is false, and it disagrees with another comment 25 lines above it. A future maintainer reading the two comments cannot tell which one describes infi. That maintainer may delete the helper as unnecessary, or re-derive the guard from the wrong failure mode, in tooling that runs DDL against production clusters. The real reasons — the helper creates the tables through the native client before infi's HTTP request path reads the Distributed table, and it removes the failed-SELECT-then-create retry — are absent.
- **Impact:** No behavior changes, so this stays at the reviewer's lowest priority. It is not a wording preference, however: the claim is objectively wrong and self-contradictory inside the added code, so it does not fall under the style exclusion.
</issue_validation>
## Task
Investigate the issue and solve it
<potential_solution>
Explain that this helper creates the cluster tables before infi reads the Distributed table. It also moves creation to the native client. Do not state that infi omits `ON CLUSTER`.
</potential_solution>
Problem
posthog_migrationscluster has more than one node. This hits our own migration and bootstrap tooling across dev, CI, and self-hosted installs, not end users. It has failed continuously since late February, still arriving today, across ClickHouse 23.9 through 25.6 (many separate installs, not one host retrying).migrate_clickhouseasks infi for applied migrations withreplicated=True, so infi selects from the Distributed tracking table. A helper pre-creates that tableON CLUSTERso infi's auto-create never runs, but it was gated on theMULTINODE_CLICKHOUSEflag, which defaults to False everywhere except one CI workflow.CREATE TABLEwith noON CLUSTERand hangs past the 60 second default timeout theDatabase(...)call never overrode. A variant fails withCLUSTER_DOESNT_EXISTwhere the config has no such cluster.Changes
system.clusters, instead ofMULTINODE_CLICKHOUSE. The tracking-table pre-create now runs when the migrations cluster has more than one node, and stays off for a single-node cluster or a cluster the config does not define (which also avoids theCLUSTER_DOESNT_EXISTvariant).timeoutwhen constructing the migrationDatabase, so a slowON CLUSTERstatement no longer fails at infi's 60 second default.bin/migrate. That PR retries the step; this one removes the deterministic hang the retries were re-hitting.How did you test this code?
I am an agent. I did not manually run this against a multi-node cluster. I added
posthog/management/commands/test/test_migrate_clickhouse.pywith automated cases:ON CLUSTERpre-create follows the cluster node count, not the flag: it runs for a multi-node cluster and stays off for a single-node one, both withMULTINODE_CLICKHOUSE=False. This guards the exact regression fixed here — re-gating the pre-create on the flag would fail these cases.Databaseis built with the generous timeout, guarding against a return to the 60 second default.No existing test covered this command, so the tests are new rather than extended.
Automatic notifications
Docs update
No user-facing docs affected. This is internal migration tooling.
🤖 Agent context
Autonomy: Fully autonomous
/writing-tests(gated and shaped the new test),/writing-simplified-technical-english.system.clustersrow count so single-node and undefined-cluster installs skip theON CLUSTERpath, which also removes theCLUSTER_DOESNT_EXISTvariant. Chose to stack on the retry PR rather than duplicate itsbin/migratechanges.Created with PostHog Desktop from this inbox report.