Batalla de MERGE. Crónica con conclusiones y moralidad.

Unas pocas semanas antes del importante festival de compromiso , el último antes de la versión feature freeze de PostgreSQL 11 , los boletines informativos de los piratas informáticos , comprimiendo el conjunto de chips en el paquete de la izquierda, vieron el thriller MERGE . El director de suspenso y CEO de 2ndQuadrant, Simon Riggs , trató con impresionante perseverancia e ingenio para introducir un parche que implemente la sintaxis del comando MERGE en la versión. Riggs ha sido comediante desde 2009, y con el estatus de comediante puede aprobar los parches usted mismo. Se le opusieron no menos respetados comités y veteranos de PostgreSQL. Las pasiones estaban hirviendo clara e implícitamente, ni siquiera llegó a insultar directamente, un hecho sorprendente para los clientes habituales en muchos foros nacionales. Sin embargo, aún quedaba algo de tensión cuando se resolvió la cuestión, y no hay nada de qué discutir.

Pero las pasiones son pasiones (se discutirán más adelante), y me gustaría resolver desapasionadamente la esencia de este problema completamente descabellado.


MERGE afuera


Si se está simplificando por completo, la cuestión es esta: tenemos 2 tablas con los mismos campos y datos diferentes. Asumir nombre y edad. Necesitamos combinarlos en uno. Pero sería necesario decidir qué hacer con esas personalidades que están en ambas tablas. Lo más probable es que queramos todo en la tabla final y actualizar la información para que coincida con las personas. Está claro que incluso en este contexto, esta es una tarea muy común. Se puede resolver sin MERGE , haciendo una solicitud compleja, puede usar disparadores, etc. Pero es inconveniente. Sin embargo, la versión no canónica de MERGE, que se llama UPSERT (UPdate + inSERT), resuelve este problema.

El operador MERGE está en el estándar SQL-2003 y ya está en todo su esplendor en SQL-2008. Se implementa en Oracle, DB2 y en MS SQL, lo que significa que la falta de MERGE molestará a aquellos que están considerando pasar de estos DBMS a PostgreSQL. El deseo anhelante de Simon Riggs lo más rápido posible, ya en PostgreSQL 11, fue alimentado por los deseos de los clientes de 2ndQuadrant, y no por la ambición o la disputa.

De hecho, MERGE tiene capacidades ricas, los datos no tienen que ser tomados de tablas, especialmente de estructuras similares.

La sintaxis del comando es la siguiente:

  MERGE INTO tablename USING table_reference ON (condition) WHEN MATCHED THEN UPDATE SET column1 = value1 [, column2 = value2 ...] WHEN NOT MATCHED THEN INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...]); 

Sin embargo, puedes hacer esto:

 MERGE [hint] INTO [schema .] {table | view} [table_alias] USING { subquery | [schema .] { table | view}} [table_alias] ON ( condition ) [ merge_update_clause ] [ merge_insert_clause ] [ error_logging_clause ] ; 

Esta sintaxis se implementa en Oracle. En palabras, MERGE realiza acciones que modifican los registros en la tabla de destino target_table_name usando data_source en un solo comando SQL, que puede, según las condiciones, hacer INSERT, UPDATE o DELETE con respecto a los registros en target_table_name. En este caso, target_table_name puede ser una vista, y data_source puede ser un conjunto de tablas o vistas, el resultado de una subconsulta .

Primero, la MERGE realiza una left outer join en el data_source con target_table_name , lo que sugiere 0 o más registros de cambios candidatos; WHEN cláusulas se calculan en el orden especificado; tan pronto como se cumpla la condición, se realiza la acción correspondiente. Palabras clave WHEN [NOT] MATCH THEN no es muy común en SQL , por lo que le recordamos que esta es una construcción de control como if-else en otros idiomas. MERGE actúa de la misma manera que UPDATE, INSERT o DELETE con respecto a target_table_name , solo la sintaxis de todo el comando es diferente.

Una cláusula con ON debe establecer una conexión en todas las columnas de la clave primaria o, si se especifican otras columnas, se debe usar algún índice único para que las condiciones [NOT] MATCHED determinen inmediatamente las acciones para el registro candidato para excluir la interacción con otras transacciones.

Comando determinista MERGE : no puede actualizar el mismo registro varias veces en el mismo comando MERGE.
Un ejemplo:

 MERGE CustomerAccount CA USING RecentTransactions T ON T.CustomerId = CA.CustomerId WHEN MATCHED THEN UPDATE SET Balance = Balance + TransactionValue WHEN NOT MATCHED THEN INSERT (CustomerId, Balance) VALUES (T.CustomerId, T.TransactionValue); 

o con una subconsulta:

 MERGE INTO bonuses D USING (SELECT employee_id, salary, department_id FROM employees WHERE department_id = 80) S ON (D.employee_id = S.employee_id) WHEN MATCHED THEN UPDATE SET D.bonus = D.bonus + S.salary*.01 DELETE WHERE (S.salary > 8000) WHEN NOT MATCHED THEN INSERT (D.employee_id, D.bonus) VALUES (S.employee_id, S.salary*.01) WHERE (S.salary <= 8000); 

En IBM DB2, la sintaxis también funcionará. Como dicen , "bajo el capó" esto se hará de manera similar a la construcción UPDATE FROM .
Desde 2008, MS SQL también tiene MERGE .

Pero incluso detrás de una sintaxis estándar única, comienza el problema de elegir entre un número considerable de mecanismos y métodos de implementación. El equipo debe trabajar en diferentes niveles de aislamiento de transacciones, con diferentes algoritmos de bloqueo, centrándose en un modo de operación altamente competitivo o no tan competitivo. Y, como puede suponer, para implementar esta lógica complicada, necesita tocar muchos componentes DBMS.

UPSERT, pseudo-MERGE


Está claro que los desarrolladores de DBMS estaban buscando soluciones de compromiso, negándose a reproducir literalmente la sintaxis estándar. La ventaja de este enfoque es la libertad. Puede usar mecanismos orgánicos para un DBMS en particular, puede optimizar la implementación para las tareas que considere más relevantes para sus usuarios.

Por ejemplo, en MySQL hay un comando REPLACE que funciona como INSERT , pero si las filas nuevas y antiguas tienen los mismos valores en el índice PRIMARY KEY o UNIQUE , la fila anterior se elimina antes de insertar la nueva. Pero también hay INSERT ... ON DUPLICATE KEY UPDATE donde INSERT y UPDATE ocurren (en lugar de DELETE en REPLACE ). Esto es UPSERT . Y hay INSERT IGNORE , que simplemente no realiza la inserción, sin arrojar un error (pero advertencia) bajo ciertas restricciones en la tabla de destino.

Crónicas de PG MERGE


En la comunidad PostgreSQL, la conversación sobre MERGE comenzó en 2005 cuando Jaime Casanova preguntó si alguien en la comunidad había comenzado a desarrollar MERGE . Peter Eisentraut sugirió discutir si valdría la pena desarrollar algún tipo de opción MERGE para PostgreSQL: una implementación similar en MySQL, o sería mejor dirigir sus esfuerzos hacia una versión Oracle del tipo MERGE liviana en funcionalidad. Sin embargo, ¿vale la pena hacer esfuerzos en esta dirección?

En medio de una breve discusión, el protagonista de esta narración Simon Riggs aparece con las palabras:
MERGE es útil tanto para sistemas OLTP como para DW (Data Warehouse - data warehouse, es decir, aplicaciones analíticas donde las consultas complejas, pero el entorno y los datos no demasiado competitivos rara vez se actualizan, y si se actualizan, por lo general generalmente en grandes bloques. <...> Podemos implementar MERGE como una variante de COPY FROM, será muy bueno.

Todos están de acuerdo: sí, genial. Más precisamente, casi todo: Stephen Frost : Creo que no soy el único que dice que necesito un estándar MERGE completo y compatible.

Bruce Momjian tiene una propuesta diferente y más pragmática: me parece que necesitamos implementar en MERGE algunas opciones que podamos implementar, y en el resto daremos un error (y en los casos en que será necesario bloquear toda la tabla). Y después de recibir los comentarios de los usuarios y pensaremos qué hacer a continuación.

Pero hasta ahora no pasa nada.

El hielo se ha roto


En 2008, Simon Riggs nuevamente instó a lidiar con MERGE, cuál de las formas de elegir (para entonces, ya está apareciendo una nueva versión de MERGE en el estándar SQL-2008, que todavía está en borrador). Pinta en detalle la implementación actual de Oracle, IBM y MS SQL y la sintaxis alternativa de MySQL y Teradata. Y un poco más tarde ya menciona el comienzo del trabajo en 2ndQuadrant en esta dirección.

Peter Eisentraut escribe en su blog : Por supuesto, Riggs es uno de los especialistas más calificados, puede liderar el trabajo en la implementación de MERGE.

Pero aquí llega el primer giro inesperado: un estudiante está involucrado en el problema, un participante en el desarrollo del programa GSoC , es decir, Google Summer of Code. Se llama Boxuan Bxzhai . No presumo transcribir el apellido. Pronto escribe que el trabajo está casi terminado.

Pero casi no cuenta. Greg Smith de 2ndQuadrant (es decir, aliado de Simon Riggs) escribe:
Entonces, tenemos un parche en el código de los cuales media docena de problemas serios no resueltos. Estoy en silencio sobre los mezquinos. Los problemas son demasiado profundos para finalizar el código para el commitfest. Mientras tanto, no se ha sabido nada de Boxuan durante mucho tiempo. Podríamos ayudarlo, pero ¿dónde está él? ¿Quién está en el saber?

Una discusión sobre las rutas de implementación se reanuda en 2014 , pero nuevamente no sucede nada: no hay código.

Finalmente, ya en 2017, Simon Riggs escribe:
Estoy trabajando en el código para comprometer MERGE a PostgreSQL versión 11 . Utilizamos los mismos mecanismos que subyacen al INSERT ON CONFLICT , que ya funciona, para que no se necesiten cambios en la infraestructura, básicamente solo implementando la sintaxis además de lo que está disponible. Pero escribo mi código desde cero, no uso desarrollos anteriores.

Estamos hablando de Peter Geoghegan ( VMware ) implementado para ese momento ya en la sintaxis alternativa INSERT .. ON CONFLICT UPDATE 9.5 INSERT .. ON CONFLICT UPDATE , diferente del estándar SQL, pero aún relacionado con MERGE y REPLACE en MySQL.

Al principio, el trabajo de Simon se encontró con exclamaciones de ¡Buen trabajo! Sin embargo, Robert Haas , aunque de apoyo, advierte sobre posibles anomalías de serialización. Por ejemplo, para lidiar con INSERT .. ON CONFLICT UPDATE , sin MERGE en su base, de alguna manera es más tranquilo.

UPSERT autor de PostgreSQL UPSERT , UPSERT mismo:
No mezclaría el MERGE ON CONFLICT DO UPDATE y MERGE . <...> Para cargar grandes cantidades de datos ( bulk load ), por ejemplo, usaría el algoritmo de merge join . <...> En general, las ventajas de MERGE estarían relacionadas con el hecho de que las conexiones normales funcionarían allí de la manera habitual: nested loop, hash, merge . Y en INSERT … ON CONFLICT no hay uniones en absoluto.

Haas: Al igual que Peter, creo que si se hace de esta manera, entonces un bloqueo tan fuerte al ejecutar una solicitud DML ve más o menos. Es poco probable que alguien esté contento de que solo una persona pueda trabajar con MERGE a la vez.

Para aquellos que tienen curiosidad: Geigan analiza las sutilezas y las UPSERT diferencias entre MERGE y MERGE aquí y aquí (almacenamos la correspondencia archivada de PostgreSQL en nuestro sitio web).

Simon se resiste. Apela a la historia reciente. Al igual que sobre el seccionamiento, también dijeron "una nueva sintaxis, nada más". Pero resultó ser algo muy útil. Pero no propongo darme cuenta de inmediato de todo lo que está en MERGE. Haremos lo mismo que con la partición: dividimos el desarrollo en fases.

Y un argumento más, en mi opinión es muy convincente: bueno. Pero vamos a elegir. Sugiero una opción práctica. Pronto vendrán 10 años desde el primer intento serio de desarrollar MERGE . ¿No es hora de comenzar a hacer algo, de obtener una solución útil, en lugar de esperar otros 10 años de la solución perfecta? Asumiendo que existe en absoluto.

Finalmente, el parche llega a la comunidad. Que fecha Imagina por favor. No, no lo adivinaron: Simon lo envía el 30 de diciembre de 2017. Y estipula que este es un parche WIP, es decir, Work in Progress, un parche en el trabajo.

Simon, enero:
El parche se completa sin ningún error especial. 1200 líneas de código más pruebas y documentación. Voy a comprometerlo a este commitfest, y completaremos el RLS (Seguridad de nivel de fila - protección en el nivel de grabación) y el soporte de particionamiento más adelante.

Casta de comités


Aquí tenemos que dar un paso al costado y explicar el papel del comisionado en la comunidad. Las funciones del comisionado, es decir, el que está facultado para aceptar el parche en la próxima versión, han cambiado históricamente. Érase una vez, cuando había pocos desarrolladores, el derecho a comprometerse se distribuía generosamente. Por ejemplo, el famoso (en un campo completamente diferente) Julian Assange recibió el título de comandante, siendo el autor de solo seis parches. Ahora no es fácil convertirse en comisionado, no hay nuevas empresas en la lista de un par de docenas de personas. Boyus Momdjan ( EnterpriseDB ) tiene 13,363 commits, Tom Lane (Tom Lane, Crunchy Data ) 13127, Robert Haas ( EnterpriseDB ) - 2074. Por cierto, el único committer de Rusia es Fedor Sigaev ( Postgres Professional ) con sus 383 commits . Simon Riggs tiene 449. Repito: él, como comisionado, tiene suficiente autoridad para tomar y cometer parches, tanto él como sus empleados. Otra cosa es que apenas vale la pena hacerlo, descuidando francamente las opiniones de otros comités y luminarias líderes. También pueden privar el estado de comisionado, pero al menos revert parche.

Fractura en batalla


Por supuesto, en el parche "desesperado", realizado, en general, apresuradamente, encuentran nuevos errores. Nuevas versiones ruedan en respuesta.

A finales de enero, aparece un nuevo personaje: el desarrollador de 2ndQuadrant Pavan (su nombre es todos por su nombre; completamente Pavan Deolasee). Ahora la comunidad está lidiando con un tándem: Pavan envía nuevas versiones y agradece las críticas, y Simon las rompe con una notable presión de marketing.

Haas: No creo que valga la pena tomar decisiones unilaterales para excluir funciones que funcionan en todas partes. Si aceptamos que algunas características no se incluirán en este parche, esto es una cosa. Y es completamente diferente que en los comentarios en esta ocasión todos expresaron su desacuerdo. Y en realidad no escuchamos las razones por las cuales estas características deberían ser excluidas.

La lógica se presentó de la siguiente manera:

  • a priori, hay problemas serios porque no pueden sino estar en los desarrollos en el estilo de "ataque de caballería".
  • Incluso se puede completar la compatibilidad con características importantes como la nueva partición en las versiones 10-11, CTE (expresiones de tabla comunes = CON consultas) o RLS (seguridad de nivel de fila) incluso después de que se acepte el parche en la versión actual, pero solo si la arquitectura propuesta es adecuada para construir en la parte superior Su funcionalidad deseada.

El segundo Peter Geigan formula esto:
Por lo general, presto atención al soporte de varias funciones, ya que si lo es, fortalece la creencia general de que el diseño se hace como debería . Y si tales problemas son causados ​​por el soporte de expresiones WITH [es decir, CTE ], entonces tengo la idea de que la arquitectura subyacente es tal que causará problemas aquí y allá.

Mientras tanto, se acerca la hora X (la última fiesta del comité) y las nubes sobre MERGE se están acumulando. No es que los padres fundadores buscaran específicamente problemas serios en la arquitectura de los parches hechos por Simon y luego Pavan. No tuve que buscar problemas; ellos se abrieron voluntariamente.

El desenlace se acerca


La trama se está acelerando. A pesar de la actitud fría de otros comités hacia su empresa, el 2 de abril, Simon decide cometer el comando siguiente SQL: parche 2016 , agrega los archivos, Depesz (Hubert Lubachevsky) logra anunciarlo en su blog, pero el mismo día Simon revierte todo porque errores

Al día siguiente, comprométase nuevamente agregando soporte WITH .

En respuesta, las acusaciones son realmente graves. Andres Freund ( EnterpriseDB ) escribe:
La arquitectura de MERGE en el analizador y el ejecutor no me impresionó de manera confiable. Crear uniones ocultas durante el análisis de análisis es una muy mala idea. Esta estructura del ejecutor debe ser completamente cambiada.

Tom Lane:
El diseño del árbol de análisis es débil.



Sobrecarga la función InsertStmt , continúa, no hace INSERT en absoluto, pero al azar tiene los mismos campos que el original. Y no todos, pero algunos. Esto es malo, conduce a la confusión.

Agreguemos observaciones de Fedor Sigayev :
En el analizador, aparecieron nodos INSERT relacionados con MERGE , colgados con un montón de campos adicionales. Si observa el plan de ejecución en ANALIZE , no comprenderá de inmediato si se trata de un INSERT regular o de MERGE : para comprenderlo, debe buscar campos adicionales.


Simon, calmadamente: OK, cambiaremos eso y enviaremos un nuevo archivo mañana .
Haas: Estoy de acuerdo con Peter. La elección de la arquitectura no tiene éxito.

Simon no se rinde. El 6 de abril, en respuesta a las críticas de Tom Lane, comete un nuevo parche modificado en el analizador.

Negociación y rendición


Bruce Momjan 6 de abril :
Quiero señalar que la gente no te pidió que trabajaras duro para arreglar algo urgentemente. Le pidieron que retire el parche. Puedes, por supuesto, trabajar duro, esperando que cambien de opinión, pero, una vez más, no te preguntaron sobre eso.

Simon: Si Tom [Lane] y Andres [Freund] durante los días restantes aún sienten que sus temores no se han disipado, estaré feliz de revertir el parche sin más preámbulos.

Tom Lane: Todavía voto para que el parche se revierta. Incluso si él fuera perfecto ahora, ahora la gente no tiene tiempo para convencerse de esto, a la garganta de otros asuntos urgentes.

Eso es todo.

Simon dijo que sí, y la batalla en MERGE terminado. Se han devuelto todos los parches, el tema se ha movido al siguiente commitfest con el estado "En espera de la finalización del autor". Los participantes en el espectáculo hicieron las paces.


Sin embargo, a juzgar por la correspondencia de las últimas semanas, parece existir cierta tensión.

Moralidad Prometida


  • Afortunadamente, la comunidad PostgreSQL tiene mecanismos naturales y formales para la detección (casi) libre de conflictos de intentos de soluciones inmaduras. Incluso si son golpeados por desarrolladores respetados en el rango de jefe de la compañía, cuya contribución al desarrollo de PostgreSQL es enorme. Y los clientes que carecen de funcionalidad están presionando para invertir.
  • Desafortunadamente, la comunidad a menudo se detiene. Es inercial en la adopción de desarrollos incluso inequívocamente relevantes. A veces se incluye el perfeccionismo irracional. La experiencia de Postgres Professional, donde trabajo, lo confirma. Perforamos un parche grande e importante de índices INCLUDE durante 3 años. Todavía está esperando una serie útil de parches para trabajar con JSON / JSONB . La expresión "entrega tu desarrollo a la comunidad" no significa realmente dar, sino golpear : el invitado es recibido con los brazos abiertos y escoltado a cuarentenas.

PD: Descargo de responsabilidad del autor : solo queríamos mostrar una parte de la vida comunitaria. Todas las coincidencias de nombres son aleatorias :)
PPS: Samurai Natalia Levshina .

Source: https://habr.com/ru/post/es412605/


All Articles