Gran cantidad de transacciones de almacenamiento

Hola,

de vez en cuando nos enfrentamos a cargas de transacciones enormes en nuestro almacenamiento. No hemos encontrado ningún horario o patrón temporal para este evento, pero ocurre al menos una vez al día. Incluso la duración varía de 10 minutos a varias horas.
Durante estas cargas, toda nuestra instalación se comporta de manera ligeramente extraña; por ejemplo, la lectura de temas no se reconoce, por lo que siguen apareciendo en “Nuevos” y/o “Sin leer”.

Parece que Discourse está moviendo grandes cantidades de archivos. Especialmente las operaciones de READ están aumentando. Ya hemos verificado si el tráfico externo también está aumentando, pero no es así. Solo el tráfico entre Discourse y el almacenamiento se ve afectado.
Detectamos este comportamiento por primera vez después de actualizar de Discourse 2.4.0.beta9 a 2.4.0.beta10, pero no estamos seguros de si ya había ocurrido antes. Ahora estamos ejecutando la versión 2.5.0.beta4.

Nuestra instalación de Discourse se ejecuta en un entorno de Azure con almacenamiento Premium conectado vía SMBv3, que normalmente funciona bastante bien.

¿Alguien puede explicar qué está ocurriendo? Al principio sospechamos del trabajo de sidekiq MigrateUploadScheme, pero si este trabajo fuera responsable de estas transacciones, deberíamos ver estas cargas altas con mucha más frecuencia de la que vemos. Además, no hemos encontrado ningún otro trabajo que pudiera ser responsable.


Debido a los “IOPS de ráfaga”, pueden ver este pico de aproximadamente 800k transacciones/30 min. Una vez que se agotan estos créditos, se reduce a aproximadamente 250k transacciones/30 min. Por favor, no se preocupen por este pico, ya que es simplemente un bono limitado/creditado del nivel de almacenamiento de Azure.
Normalmente tenemos entre 5k y 40k transacciones por 30 minutos.

En este punto no sabemos dónde buscar y cualquier idea o indicación será apreciada.

Saludos cordiales,
Sascha

¿Tiene habilitadas las copias de seguridad automáticas? Verifique la configuración del sitio copias de seguridad automáticas habilitadas y frecuencia de copias de seguridad.

Hola.
No, las copias de seguridad están completamente deshabilitadas. Estamos utilizando la retención de copias de seguridad de la instancia de PSQL en sí y (no automatizadas) instantáneas de almacenamiento.

¿Puedes habilitar estadísticas en PostgreSQL para buscar consultas de larga ejecución o consultas repetidas?

Necesitas habilitar pg_stat_statements y explorar las estadísticas que genera.

Habilitamos las estadísticas hace un tiempo para eliminar algunos cuellos de botella. De ahí surgió mi publicación anterior Recomendaciones de rendimiento de la base de datos (por Azure PSQL).

Aquí están las 10 consultas de mayor duración de la semana pasada:

Si necesitas las consultas completas, házmelo saber. También sería interesante saber por qué esto afecta el uso del almacenamiento.

Probablemente la primera consulta completa con seguridad; esa es grande, con una duración de 1:00 y 14 ejecuciones.

Hola. La primera consulta se activará/ejecutará a través de DirectoryItem.refresh_period (supongo).

Así que, aquí está la consulta real:

Resumen
WITH x AS (SELECT
			u.id user_id,
			SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = 2 THEN 1 ELSE 0 END) likes_received,
			SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = 1 THEN 1 ELSE 0 END) likes_given,
			COALESCE((SELECT COUNT(topic_id) FROM topic_views AS v WHERE v.user_id = u.id AND v.viewed_at > '2019-10-28 23:52:24.911261'), 0) topics_entered,
			COALESCE((SELECT COUNT(id) FROM user_visits AS uv WHERE uv.user_id = u.id AND uv.visited_at > '2019-10-28 23:52:24.911261'), 0) days_visited,
			COALESCE((SELECT SUM(posts_read) FROM user_visits AS uv2 WHERE uv2.user_id = u.id AND uv2.visited_at > '2019-10-28 23:52:24.911261'), 0) posts_read,
			SUM(CASE WHEN t2.id IS NOT NULL AND ua.action_type = 4 THEN 1 ELSE 0 END) topic_count,
			SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = 5 THEN 1 ELSE 0 END) post_count
			FROM users AS u
			LEFT OUTER JOIN user_actions AS ua ON ua.user_id = u.id AND COALESCE(ua.created_at, '2019-10-28 23:52:24.911261') > '2019-10-28 23:52:24.911261'
			LEFT OUTER JOIN posts AS p ON ua.target_post_id = p.id AND p.deleted_at IS NULL AND p.post_type = 1 AND NOT p.hidden
			LEFT OUTER JOIN topics AS t ON p.topic_id = t.id AND t.archetype = 'regular' AND t.deleted_at IS NULL AND t.visible
			LEFT OUTER JOIN topics AS t2 ON t2.id = ua.target_topic_id AND t2.archetype = 'regular' AND t2.deleted_at IS NULL AND t2.visible
			LEFT OUTER JOIN categories AS c ON t.category_id = c.id
			WHERE u.active
			AND u.silenced_till IS NULL
			AND u.id > 0
			GROUP BY u.id)
	UPDATE directory_items di SET
		 likes_received = x.likes_received,
		 likes_given = x.likes_given,
		 topics_entered = x.topics_entered,
		 days_visited = x.days_visited,
		 posts_read = x.posts_read,
		 topic_count = x.topic_count,
		 post_count = x.post_count
	FROM x
	WHERE
	x.user_id = di.user_id AND
	di.period_type = 5 AND (
	di.likes_received <> x.likes_received OR
	di.likes_given <> x.likes_given OR
	di.topics_entered <> x.topics_entered OR
	di.days_visited <> x.days_visited OR
	di.posts_read <> x.posts_read OR
	di.topic_count <> x.topic_count OR
	di.post_count <> x.post_count )

¿Puedo proporcionar algo de contexto para que puedas evaluarlo mejor:
Tenemos ~430k usuarios, 1.6mió de temas (sin eliminar) con 8.4mió de publicaciones (sin eliminar) en 241 categorías y 12mió de user_actions.

Pero aún no entiendo por qué las consultas lentas deberían causar esta alta cantidad de operaciones de LECTURA en el almacenamiento (/uploads). ¿Puede que me esté perdiendo algo?

Esto no suena correcto. Estoy confundido; si estás en Azure, ¿cómo estás almacenando los archivos? ¿Es una configuración de contenedor único? ¿Cómo están configuradas las cargas?

La actualización del directorio es muy lenta. Si no puedes permitirte el costo de actualizarlo, podrías deshabilitar el directorio https://meta.discourse.org/u. Tenemos planes muy concretos para agregar la búsqueda de usuarios a la búsqueda de página completa, de modo que puedas prescindir del directorio.

Perdona por la confusión. Intentaré explicar cómo hemos configurado Discourse.

En primer lugar, no se trata de una configuración de un solo contenedor. Lo hemos dividido para utilizar los servicios propios de Azure para Redis, PostgreSQL y almacenamiento.

Hay 3 máquinas virtuales (VM) ejecutando Discourse + nginx. Un Azure File Share separado se monta mediante SMBv3 en estas 3 VMs y este punto de montaje se adjunta a los contenedores de Discourse como un volumen.
Es allí donde se almacenarán /public/uploads, /tmp/javascript-cache y /tmp/stylesheet-cache.

Además, hacemos uso de Azure Cache for Redis y Azure Database for PostgreSQL.

Los discos de las VM, el almacenamiento y la base de datos están separados entre sí. Por lo tanto, la carga de la BD no debería afectar al rendimiento del almacenamiento o de las VM, y podemos aprovechar las ventajas de estos servicios (como las estadísticas de la base de datos en la instancia de PostgreSQL que mencionaste anteriormente y las recomendaciones de rendimiento).

Esta configuración también nos permite monitorear cada servicio/parte por separado y hemos observado que nuestro Azure File Share, donde se encuentran los uploads, recibe una cantidad muy alta de transacciones (como puedes ver en mi primer mensaje). Estas transacciones son mayoritariamente operaciones de LECTURA.
Dado que este almacenamiento (File Share) solo es utilizado por el propio Discourse, hemos intentado averiguar qué proceso/tarea es responsable de estos eventos que ocurren 1-2 veces al día durante varios minutos hasta varias horas.

Además de estos enormes conteos de transacciones, esta configuración funciona bastante bien, excepto por algunas consultas lentas que afectan al rendimiento solo en unos pocos casos (por ejemplo, una pequeña cantidad de páginas de resumen de actividad de usuarios pueden tardar hasta 15 segundos en cargarse).

Espero haber explicado por qué me preguntaba cómo el rendimiento de la BD podría tener un efecto en los conteos de transacciones de archivos estáticos.

Saludos cordiales y gracias por sus esfuerzos hasta ahora,
Sascha

P.D.
Estamos utilizando una imagen de Docker personalizada en nuestra configuración y entiendo perfectamente que no pueden/no ofrecerán soporte para soluciones personalizadas.
Lo único que nos gustaría saber es qué proceso/tarea/ajuste podría causar estos conteos de transacciones de almacenamiento que en parte ralentizan toda la configuración y qué podemos hacer para evitarlo.

Creo que tu mejor opción en cuanto a rendimiento es cambiar el almacenamiento de subida a S3 o a un motor de almacenamiento compatible con S3 + CDN. Usar un recurso compartido SMB para las subidas es algo que nunca hemos probado; mi suposición es que estamos verificando el tamaño de las subidas diariamente, lo cual es extremadamente rápido en local y muy lento en SMB.

Gracias por la aclaración y por tus consejos. Efectivamente, SMB puede ser muy lento cuando se trata de acceder a muchos archivos. La mayoría de las veces no hay diferencia, ya que los archivos de acceso frecuente se almacenan en caché por nginx (aplicamos con frecuencia los cambios realizados en la configuración de ejemplo de nginx para Discourse). Pero cuando se trata de esos accesos puntuales, el rendimiento cae.

Llevamos algún tiempo buscando otras soluciones de almacenamiento. Usar un almacenamiento externo compatible con S3 podría podría romper partes de nuestro concepto de seguridad. Cada instancia/servicio involucrado (base de datos, máquina virtual, almacenamiento, …) está vinculado a una red privada y es inaccesible desde internet público. Todo el tráfico público es gestionado por un Azure Application Gateway.

Desafortunadamente, Azure Blob Storage no es compatible con S3, pero quizás deberíamos invertir algo de tiempo en aprovecharlo.

Las posibles soluciones actuales son el plugin de almacenamiento de blobs de Discourse o usar blobfuse directamente dentro del contenedor.

De todos modos, gracias por tu tiempo y ayuda. ¿Hay alguna razón por la que se verifique el tamaño de la carga diariamente y existe alguna forma de desactivarlo?

Saludos cordiales

Esto probablemente proviene de aquí:

Supongo que puedes desactivar la carga de imágenes con enlaces externos o crear un parche de mono en un plugin que desactive esto:

O simplemente asigna du en tu contenedor para que sea una operación nula modificando la configuración de tu contenedor.

Muchas gracias, esto ayudará mucho. du será muy costoso al usar un recurso compartido SMB; al menos estamos alojando ~800k archivos (38 GB) en este recurso compartido de archivos.

Ya hemos desactivado pulling_hotlinked_images debido a posibles problemas legales/de derechos de autor.

Creo que es un poco demasiado invasivo crear un alias para du, aunque es una buena idea parchearlo mediante un plugin. ¿Podríamos simplemente aplicar un git-patch al construir la imagen con algo como:

def self.used(path)
    output = Discourse::Utils.execute_command('df', '-Pk', path)
    size_line = output.split("\n")[1]
    size_line.split(/\s+/)[2].to_i * 1024
end

Dado que du puede ser más confiable/preciso, creo que df debería satisfacer nuestras necesidades y no debería romper ninguna otra funcionalidad.