Repository navigation
La evolución se puede repartir, y deja de retener 43 GB mientras corre - #999
Merged
Merged
Conversation
Tres cosas que salieron de medir por que esta operacion usa un solo nucleo mientras hay doce ociosos. La respuesta es que no puede usar mas: Postgres no planifica un camino paralelo sobre una relacion temporal. En el fuente de la 18.6, `set_rel_consider_parallel` vuelve en cuanto ve `RELPERSISTENCE_TEMP`, asi que los caminos parciales no se descartan por coste, no se generan; y `plan_create_index_workers` se niega igual para los dos indices. Medido sobre dos ciclos completos: el backend ocupa el 96% de UN nucleo con el 3,9% de sus muestras fuera de CPU. Ningun valor de `max_parallel_workers_per_gather` cambia eso. La via para gastar mas de un nucleo es correr varios rangos, no acelerar uno. 1. EL REPARTO NECESITA SU SEMILLA, Y LA SEMILLA NO SE DESCRIBE. `composicion` es una fila por release y `estratos`/`detalle` son una fila por transicion, asi que un rango no puede servir para las dos cosas: o describe su primera release y pierde la transicion que entra en ella, o la conserva empezando una release antes y entonces describe dos veces una release de la serie. `seed_from_previous` es la tercera opcion, y la operacion resuelve el predecesor ella misma sobre las releases que EXISTEN: antes de la 194 esta la 192, porque la 193 es el mismo fichero, y antes de la 211 esta la 205, porque la 206 a la 210 no se publicaron. Quien llamara restando uno se equivocaria en los dos sitios, y en silencio. Es la clase de defecto que ya tenemos anotada para la 192 y la 193: una serie contando un intervalo dos veces. 2. UNA TRANSACCION PARA TODA LA SERIE RETIENE TODO. Los ficheros de una temporal borrada no se desenlazan hasta el COMMIT: 577 MB por release medidos, 43 GB proyectados sobre las 74, y un xmin clavado que detiene el vacuum de la base entera durante las horas que dure la corrida. Se comitea por release. Las temporales sobreviven porque son de sesion y por defecto preservan filas. Eso regala la garantia de leer toda la serie en un solo snapshot, asi que la garantia deja de ser gratis y pasa a comprobarse: `_comprobar_serie` compara la serie como se leyo contra la serie como esta al terminar, y si se movio falla en vez de publicar dos artefactos que describen corpus distintos. 3. `temp_buffers` ERA EL NUMERO MAS DESATENDIDO DEL LOTE. Las temporales se leen por esos buffers y nunca por `shared_buffers`, y estaban en los 8 MB de fabrica contra un conjunto vivo de ~1 GB de relaciones temporales por ciclo. Ahora es un campo del payload, con `None` dejando el servidor como esta, y se fija antes de la primera temporal de la sesion porque despues el servidor ignora el cambio sin decir nada. El bucle sale de `execute` a `_recorrer` y los tres TSV a `_artefactos`, que es lo que el presupuesto de complejidad pedia; y la semilla y el rango viajan juntos en `_Plan` en lugar de como dos locales que cada llamante tenia que recordar emparejar.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #999 +/- ##
===========================================
+ Coverage 87.85% 88.01% +0.16%
===========================================
Files 306 330 +24
Lines 23414 25454 +2040
===========================================
+ Hits 20570 22403 +1833
- Misses 2844 3051 +207
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Por qué
Pregunta de Francisco mientras
analyze_annotation_evolutioncorría sobre las 74releases de la campaña limpia: "Esto no podría hacerse utilizando más CPU?". La
respuesta medida es que no, no dentro de una corrida, y el motivo tiene
consecuencias de diseño.
Postgres no paraleliza nada de esto, y no por configuración. En el fuente de
la 18.6 exacta,
set_rel_consider_parallel(allpaths.c:623) vuelve en cuanto veRELPERSISTENCE_TEMP:Los caminos parciales no se descartan por coste: no se generan.
plan_create_index_workers(planner.c:7272) se niega igual para los dosCREATE INDEX. Y la documentación lista "Scans of temporary tables" como siempreparallel restricted. El control pareado lo cierra: la misma consulta de
_estratos, palabra por palabra, saca 5 subplanes paralelos sobre tablaspermanentes y cero sobre temporales, con la persistencia como única variable.
Medido sobre dos ciclos completos de release: el backend ocupa 96% de UN
núcleo con 3,9% de muestras fuera de CPU, y una ráfaga de 12.000 muestras a 2 ms
da R=99,98%. Mientras tanto, 12 de los 16 núcleos ociosos. La vía para gastar
más de un núcleo es correr varios rangos a la vez.
Y repartir por rangos no era exacto
composiciones una fila por release;estratosydetalleson una fila portransición. Un rango no puede servir para las dos cosas, y la línea 361 emitía
composicionincondicionalmente, antes del guardiaif ant is not None::+3·(k−1)filas.Con 4 shards, 9 filas de más.
k−1transiciones completas, y resultan serprecisamente
(192,194), la que cruza la 193 borrada, y(205,211), la quecruza el hueco 206-210.
Es la clase de defecto que el proyecto ya tiene anotada para la 192 y la 193: una
serie contando un intervalo dos veces.
seed_from_previoussepara las dos cosas. La operación resuelve el predecesorella misma, sobre las releases que existen, que es el punto: antes de la 194
está la 192 y antes de la 211 está la 205. Quien llamara restando uno se
equivocaría en los dos sitios y en silencio.
Dos cosas más que aparecieron midiendo
Una transacción para toda la serie retiene todo. Los ficheros de una temporal
borrada no se desenlazan hasta el COMMIT: 577 MB por release medidos, 43 GB
proyectados sobre las 74, y un xmin clavado que detiene el vacuum de la base
entera durante horas. Ahora se comitea por release; las temporales sobreviven
porque son de sesión y preservan filas.
Eso regala la garantía de leer la serie en un solo snapshot, así que la garantía
deja de ser gratis y pasa a comprobarse.
_comprobar_seriecompara la serie comose leyó contra la serie al terminar, y si se movió falla en lugar de publicar
dos artefactos que describen corpus distintos.
temp_buffersera el número más desatendido del lote. Las temporales se leenpor esos buffers y nunca por
shared_buffers, y estaban en los 8 MB de fábricacontra ~1 GB de conjunto vivo por ciclo. De ahí que el 37% de las muestras marque
DataFileReadmientras el proceso está al 99,5% de un núcleo: sonpreadservidospor la caché de páginas, cobrados como
stime. Ahora es un campo del payload,Nonedeja el servidor como está, y se fija antes de la primera temporal porquedespués el servidor ignora el cambio sin decir nada.
Qué cubren los tests
Los existentes leen el SQL que un método construye. Los nuevos preguntan lo
contrario: qué sentencias decide emitir el bucle, y para qué releases. Ahí
estaba el defecto, y no se ve desde ningún método suelto.
_SesionDeSerieresponde por la forma de la consulta y
_AlmacenQueLeeguarda lo escrito, así queel test lee el TSV de verdad.
El que importa es
test_the_shards_concatenate_into_the_whole_series: cuatroshards deben escribir exactamente las filas que escribe una corrida. Revirtiendo
el guardia a mano, falla con
Left contains 9 more items, que es el+3·(k−1)predicho.
Los dos bordes están fijados por separado, porque son los dos sitios donde la
aritmética sobre el número falla: la semilla de la 194 es la 192 y la de la 211 es
la 205.
Lo que esto NO hace
No cambia ningún número de una corrida sin
seed_from_previous: los artefactos deun rango completo son byte a byte los mismos. No convierte las temporales en
UNLOGGED, que es la otra vía para habilitar paralelismo y que cuesta lapropiedad de que una ejecución interrumpida no deja residuo. Y no reparte nada por
su cuenta: deja el reparto expresable y exacto, y quién lo encola es otra
decisión.
La corrida en curso, 74 releases con la operación anterior, no se tocó para esto.