clickhouse-cloud-multi-table-rename

Par divinevideo · divine-mobile

Corrige les échecs de migration ClickHouse Cloud causés par des instructions RENAME multi-tables. À utiliser quand : (1) la migration échoue avec « Database X is Shared, it does not support renaming of multiple tables in single query », (2) golang-migrate ou d'autres outils de migration affichent une version de base de données en état « dirty » après une migration de type table-swap sur ClickHouse Cloud, (3) la migration de schéma fonctionne sur ClickHouse auto-hébergé mais échoue sur ClickHouse Cloud. ClickHouse Cloud utilise le moteur SharedMergeTree, qui comporte des restrictions absentes du MergeTree standard.

npx skills add https://github.com/divinevideo/divine-mobile --skill clickhouse-cloud-multi-table-rename

Limitation de RENAME Multi-Table sur ClickHouse Cloud

Problème

ClickHouse Cloud (moteur SharedMergeTree) ne supporte pas le renommage de plusieurs tables dans une seule instruction RENAME TABLE, ce qui est un pattern courant pour les échanges atomiques de tables lors des migrations de schéma. ClickHouse auto-hébergé supporte cela, donc les migrations qui fonctionnent localement ou sur des instances auto-hébergées échoueront sur ClickHouse Cloud.

Contexte / Conditions de déclenchement

  • Message d'erreur : "Database X is Shared, it does not support renaming of multiple tables in single query"
  • Code d'erreur : 48
  • Utilisation de golang-migrate (ou similaire) avec ClickHouse Cloud
  • Le SQL de migration contient un pattern comme :
    RENAME TABLE db.original TO db.original_old,
                 db.new_version TO db.original;
  • La migration fonctionne en staging (ClickHouse auto-hébergé) mais échoue en production (ClickHouse Cloud)

Solution

Prévention : Écrire des migrations compatibles avec ClickHouse Cloud

Au lieu d'un RENAME multi-table :

-- MAUVAIS : Cela échoue sur ClickHouse Cloud
RENAME TABLE nostr.my_table TO nostr.my_table_old,
             nostr.my_table_v2 TO nostr.my_table;

Utilisez des instructions RENAME séparées :

-- BON : Diviser en opérations individuelles
RENAME TABLE nostr.my_table TO nostr.my_table_old;
RENAME TABLE nostr.my_table_v2 TO nostr.my_table;

Note : Cela perd l'atomicité, mais ClickHouse Cloud ne supporte de toute façon pas la version atomique.

Récupération : Corriger une migration défaillante qui a déjà échoué

  1. Vérifier l'état actuel — identifier quelles tables existent et dans quel état elles sont :

    SHOW TABLES LIKE '%my_table%';
    DESCRIBE TABLE nostr.my_table;       -- Vérifier si elle a l'ancien ou nouveau schéma
    DESCRIBE TABLE nostr.my_table_v2;    -- Vérifier si la nouvelle table a été créée
  2. Compléter la migration manuellement avec des renommages séparés :

    -- Si l'original et la v2 existent tous les deux (RENAME n'a jamais été exécuté) :
    RENAME TABLE nostr.my_table TO nostr.my_table_old;
    RENAME TABLE nostr.my_table_v2 TO nostr.my_table;
    DROP TABLE IF EXISTS nostr.my_table_old;
    -- Recréer les vues qui ont été supprimées
  3. Forcer la version de migration pour la marquer comme complétée :

    # Utiliser golang-migrate
    migrate -path=/migrations -database "clickhouse://..." force VERSION
  4. Si vous utilisez des jobs K8s, recréer le job avec les arguments force VERSION :

    containers:
      - name: migrate
        image: my-migrate-image:tag
        args: ["force", "65"]  # Le numéro de migration qui a été appliqué manuellement

Vérification

Après la migration manuelle, vérifier :

-- Vérifier que la table a le nouveau schéma
DESCRIBE TABLE nostr.my_table;

-- Vérifier que la version de migration est propre (pas sale)
SELECT version, dirty FROM schema_migrations ORDER BY version DESC LIMIT 5;

-- Vérifier que les tables anciennes/temporaires sont nettoyées
SHOW TABLES LIKE '%my_table%';

Exemple

La migration 65 pour funnelcake devait changer view_traffic_sources.source de Enum8 à String. La migration :

  1. A supprimé une vue dépendante
  2. A créé view_traffic_sources_v2 avec le nouveau schéma
  3. A copié les données
  4. A tenté RENAME TABLE original TO old, v2 TO original — ÉCHEC sur ClickHouse Cloud

Récupération :

# Via l'API HTTP depuis un pod curl dans le cluster :
curl -s "$CH_URL/?database=nostr&user=$USER&password=$PASS" \
  --data-binary 'RENAME TABLE nostr.view_traffic_sources TO nostr.view_traffic_sources_old'
curl -s "$CH_URL/?database=nostr&user=$USER&password=$PASS" \
  --data-binary 'RENAME TABLE nostr.view_traffic_sources_v2 TO nostr.view_traffic_sources'
curl -s "$CH_URL/?database=nostr&user=$USER&password=$PASS" \
  --data-binary 'DROP TABLE IF EXISTS nostr.view_traffic_sources_old'
# Recréer la vue de synthèse...
# Puis forcer la version de migration à 65

Notes

  • SharedMergeTree est le moteur par défaut sur ClickHouse Cloud — vous ne pouvez pas passer à un MergeTree standard
  • D'autres limitations de SharedMergeTree existent (par ex., certaines opérations ALTER se comportent différemment)
  • Lors de l'écriture de migrations pour des environnements à la fois auto-hébergés et cloud, utilisez toujours des instructions RENAME séparées
  • Le driver ClickHouse de golang-migrate utilise x-multi-statement=true qui divise les instructions sur ;, mais le RENAME avec virgules reste une seule instruction
  • Si une migration échouée a laissé une table _v2 derrière elle, vous devez DROP TABLE IF EXISTS avant de relancer la migration, sinon CREATE TABLE IF NOT EXISTS ignorera silencieusement la création et l'INSERT dupliquera les données dans la table v2 existante
  • Utilisez SET alter_sync = 2; SET mutations_sync = 2; dans les migrations pour assurer l'exécution synchrone sur ClickHouse Cloud

Références

  • Différences SharedMergeTree de ClickHouse Cloud : le moteur SharedMergeTree a des restrictions sur les opérations qui nécessitent une coordination inter-shards
  • Driver ClickHouse de golang-migrate : github.com/golang-migrate/migrate avec driver clickhouse

Skills similaires