Changing a table's storage layout (engine or sorting key) in ClickHouse requires a full table rewrite. Doing it in-place can block or slow concurrent reads and writes due to heavy merges and metadata changes, creating real risk for production workloads. Blue/Green avoids this by creating a new versioned table and migrating data live via a materialized view, so traffic continues uninterrupted.
When to use it:
How Moose does it:
version, setting the new order_by_fieldsengine (Table modeling).Setting config.version on an changes only the underlying table name (suffixes dots with underscores). Your code still refers to the logical table you exported.
OlapTableAssume the original events table orders by id only. We want to update the
sorting key to optimize reads by ordering on id, createdAt.
from typing import Annotatedfrom pydantic import BaseModelfrom moose_lib import OlapTable, Key, OlapConfig class EventV0(BaseModel): id: str name: str created_at: str # datetime in your format events = OlapTable[EventV0]("events", config=OlapConfig(version="0.0", order_by_fields=["id"]))Create a new table with the same logical name, but set version: "0.1" and update the ordering to id, createdAt. Moose will create events_0_1 in ClickHouse.
class EventV1(BaseModel): id: Key[str] name: str created_at: str events_v1 = OlapTable[EventV1]("events", config=OlapConfig(version="0.1", order_by_fields=["id", "created_at"]))Create a materialized view that:
events_v0)events_v1)Pass the versioned OlapTable instance as targetTable. If you only pass a tableName, Moose will create an unversioned target.
from moose_lib import MaterializedView, MaterializedViewOptionsfrom app.tables.events import eventsfrom app.tables.events_v01 import events_v1, EventV1 migrate_events_to_v01 = MaterializedView[EventV1]( MaterializedViewOptions( materialized_view_name="mv_events_to_0_1", select_statement=( f"SELECT * FROM {events.name}" ), select_tables=[events], ), target_table=events_v1,)What happens when you export this view:
INSERT INTO ... SELECT ...)events automatically flow into events_0_1eventsV1).0.1, 1.0, 1.1. Moose will render events_1_1 as the physical name.selectStatement.