refactor: update benchmark results and configuration for improved performance in go-migrate
This commit is contained in:
75
benchmark-results.md
Normal file
75
benchmark-results.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Benchmark go-migrate — 2,000,000 filas
|
||||
|
||||
**Tabla**: `Cartografia.MANZANA`
|
||||
**Fecha**: 2026-05-29
|
||||
**Entorno**: Docker local (MSSQL 2022 Developer / PostgreSQL 16 + PostGIS)
|
||||
|
||||
---
|
||||
|
||||
## Resultado final — 5 pasadas cada dirección
|
||||
|
||||
| Métrica | MSSQL → PostgreSQL | PostgreSQL → MSSQL |
|
||||
|---|---|---|
|
||||
| **Promedio** | **8.37s** | **16.77s** |
|
||||
| **Mediana** | 8.16s | 16.33s |
|
||||
| **Mínimo** | 7.75s | 16.03s |
|
||||
| **Máximo** | 9.17s | 18.46s |
|
||||
| **Desv. estándar** | 0.56s | 1.01s |
|
||||
| **Throughput promedio** | **~238,892 filas/seg** | **~119,261 filas/seg** |
|
||||
| **Factor** | 1x | **~2x más lento** |
|
||||
|
||||
---
|
||||
|
||||
## Evolución del tuning PG → MSSQL
|
||||
|
||||
| Etapa | Config | Tiempo | Throughput | Δ |
|
||||
|---|---|---|---|---|
|
||||
| Corrida 1 — original | conservadora | 236.8s | ~8,446 /seg | baseline |
|
||||
| Corrida 2 — igualada | mismos parámetros | 21.94s | ~91,148 /seg | +10.8x |
|
||||
| Tuning A | 4ext/8load 50k | 17.37s | ~115,200 /seg | +1.27x |
|
||||
| Tuning C | 16 loaders | 17.26s | ~115,900 /seg | +1.28x |
|
||||
| **Tuning D — óptimo** | **8ext/8load 50k** | **~16.77s** | **~119,261 /seg** | **+1.37x** |
|
||||
| Tablock + 8 loaders | lock exclusivo serial | ~44s | ~45,000 /seg | ❌ regresión |
|
||||
| Tablock + 1 loader | minimal logging | ~47s | ~42,000 /seg | ❌ regresión |
|
||||
|
||||
---
|
||||
|
||||
## Configuración óptima — `config-reverse.yaml`
|
||||
|
||||
```yaml
|
||||
max_parallel_workers: 4
|
||||
defaults:
|
||||
batches_per_partition: 4
|
||||
max_extractors: 8 # ← mayor lever de mejora
|
||||
extractor_batch_size: 25000
|
||||
extractor_queue_size: 32
|
||||
max_transformers: 8
|
||||
transformer_batch_size: 50000
|
||||
transformer_queue_size: 32
|
||||
max_loaders: 8
|
||||
loader_batch_size: 50000 # sweet spot — 75k y 100k peores
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Análisis de la brecha final (~2x)
|
||||
|
||||
La diferencia residual entre ambas direcciones es estructural y está en el protocolo de escritura:
|
||||
|
||||
| Protocolo | Mecanismo | Overhead |
|
||||
|---|---|---|
|
||||
| `pgx.CopyFrom` (→ PG) | PostgreSQL COPY protocol — streaming binario sin SQL | mínimo |
|
||||
| `mssql.CopyIn` (→ MSSQL) | BCP protocol — row-by-row dentro de un bulk statement | mayor por fila |
|
||||
|
||||
`mssql.CopyIn` itera fila a fila via `stmt.ExecContext(row...)` antes del flush final, lo que introduce overhead por fila independientemente del batch size. `pgx.CopyFrom` hace streaming puro.
|
||||
|
||||
---
|
||||
|
||||
## Hallazgos sobre Tablock
|
||||
|
||||
`Tablock: true` en `mssql.BulkOptions` resultó contraproducente en ambos escenarios:
|
||||
|
||||
- **Con 8 loaders paralelos**: cada loader compite por un lock exclusivo de tabla → serialización completa (~44s)
|
||||
- **Con 1 loader + batch enorme**: sin contención de locks, pero overhead de log + gestión de la lock exclusiva superó el beneficio de minimal logging (~47s)
|
||||
|
||||
**Conclusión**: para este patrón de carga (múltiples loaders concurrentes), `Tablock: false` (default) es siempre mejor.
|
||||
Reference in New Issue
Block a user