ClickHouse / ClickHouse/ClickHouse.EntityFrameworkCore

Migrations: split each migration into single-DDL steps (ClickHouse has no transactions)

Abierto
#44 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
C#
Estrellas
23
Forks
7
Merge medio
14 d 3 h
PR fusionados (30 d)
1

Descripción

**Background**

EF Core assumes a migration is atomic: the whole Up() runs inside a transaction, and the row in __EFMigrationsHistory is written only if every operation succeeds. If anything fails, the transaction rolls back and the database is left exactly as it was, so the migration can simply be re-run.

ClickHouse has no transactions for DDL. There is no multi-statement atomicity and no rollback. Each CREATE / ALTER / DROP commits the moment it executes.

The problem

A normal EF migration bundles many DDL operations into one Up():

```protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable("events", /* … */); // DDL_1
migrationBuilder.CreateTable("events_by_day", /* … */); // DDL_2
migrationBuilder.CreateClickHouseMaterializedView( // DDL_3 (depends on DDL_1 + DDL_2)
viewName: "events_mv", targetTable: "events_by_day",
selectQuery: "SELECT day, count() FROM events GROUP BY day");
}
```

EF runs these as a single migration and writes the history row only after DDL_3 succeeds. Now suppose DDL_3 fails (a bad SELECT, an unsupported engine, a transient server error). Because there is no transaction:
- DDL_1 and DDL_2 are already committed: events and events_by_day exist on the server.
- The migration is not recorded as applied (no history row).

The migration is now wedged. Re-running it re-executes DDL_1: CREATE TABLE events → Code: 57. Table already exists. It can never complete, and the database is stuck in a half-applied state that has to be cleaned up by hand.

This is not an edge case, any migration with more than one DDL statement is exposed to it, and the larger the migration the worse the partial-failure blast radius.

Proposed approach: one DDL statement per migration step

Instead of one migration containing N operations, split a single dotnet ef migrations add into N single-operation step migrations, each with its own __EFMigrationsHistory row:
```
20260601120000_Init_001 CREATE TABLE `events` …
20260601120000_Init_002 CREATE TABLE `events_by_day` …
20260601120000_Init_003 CREATE MATERIALIZED VIEW `events_mv` …
```

Now a partial failure is resumable:

- If _003 fails, _001 and _002 are each recorded as applied.
- Fix the model and re-run database update → it skips _001/_002 (already in history) and resumes at _003.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Línea de trabajo

The issue identifies migration Up(), MigrationBuilder, and __EFMigrationsHistory as the relevant entry points. Start by tracing how the provider executes multiple DDL operations and records migration history. Done means the proposed single-DDL steps preserve ordering and allow a later failed step to resume without re-executing completed steps.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
csharp, sql
Área
backend, databases
Tipo de issue
Nueva funcionalidad
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Tranquilo
Claridad
Bastante claro
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.