Development #94087
Réduire la consommation d'espace en base de données pour wcs
0%
Description
Aujourd'hui wcs représente plus de 650GB en bases de données, contre 162GB pour l'ensemble des autres applications.
Sur beaucoup de bases, la majorité de l'espace disque utilisé l'est par les evolutions et la colonne parts.
Cette colonne contient des objets python sérialisés en pickle. Je ne peux donc pas aisément les analyser en PostgreSQL.
Si l'ensemble des données contenues dans ces blobs est réellement utile, serait-il possible de compresser à la volée (avec zstd par exemple) ces colonnes ? PostgreSQL (14) permettrait de compresser de manière transparente en LZ4 en remplacement du "pglz" historique, mais je pense qu'on peut se permettre une compression plus forte côté python avec ZSTD (paquet python3-zstandard, disponible dans debian stable), le prix en CPU sera faible par rapport aux gains en stockage et en bande passante.
History
Updated by Frédéric Péters about 2 months ago
Cette colonne contient des objets python sérialisés en pickle. Je ne peux donc pas aisément les analyser en PostgreSQL.
Si tu me pointes un tenant / une démarche particulière, je peux regarder; c'est possible qu'il y ait des appels webservice qui retournent beaucoup et que ça se trouve enregistré. (mais normalement ça ne devrait alors pas se faire si souvent, vs les webservices qui demandent un statut, qui vont stocker peu mais souvent).
Updated by Frédéric Péters about 2 months ago
- Status changed from Nouveau to Information nécessaire
- Assignee set to Pierre Ducroquet
Updated by Pierre Ducroquet about 2 months ago
- Assignee changed from Pierre Ducroquet to Frédéric Péters
Updated by Frédéric Péters about 2 months ago
- Status changed from Information nécessaire to En cours
Updated by Frédéric Péters about 2 months ago
- Assignee changed from Frédéric Péters to Pierre Ducroquet
Updated by Pierre Ducroquet 23 days ago
- Assignee changed from Pierre Ducroquet to Frédéric Péters
J'ai modifié le script comme suit pour le rendre plus efficace et pouvoir le lancer sur toute la prod, je veux bien un avis avant de le faire tourner. Au lieu de regarder les 1000 premières demandes, je cible immédiatement les demandes avec une evolution dont le parts fait plus de 1MB.
from quixote import get_publisher import re import itertools import wcs.sql from wcs.sql_criterias import Null, NotEqual, Contains from wcs.formdef import FormDef from wcs.carddef import CardDef from wcs.workflows import ContentSnapshotPart legacy_re = re.compile(r'(bo|)(\d+)(_raw|_display|_structured|)$') uuid_re = re.compile(r'(bo|)[0-9a-f]{8}-') conn, cur = wcs.sql.get_connection_and_cursor() for formdef in itertools.chain(FormDef.select(ignore_errors=True), CardDef.select(ignore_errors=True)): print('==', get_publisher().tenant.hostname, formdef) # make sure there are some 'big' evolutions available cur.execute("SELECT distinct formdata_id FROM %s WHERE length(parts) > 1024*1024" % (wcs.sql.get_formdef_table_name(formdef) + '_evolutions')) ids = [x[0] for x in cur.fetchall()] if len(ids) == 0: continue criterias = [NotEqual('status', 'draft'), Null('anonymised'), Contains('id', ids)] for i, formdata in enumerate(formdef.data_class().select_iterator(criterias, itersize=200)): try: formdata.evolution content_snapshot_part = formdata.evolution[0].parts[0] if not isinstance(content_snapshot_part, ContentSnapshotPart): continue except (TypeError, IndexError): continue new_data_field_ids = [x for x in content_snapshot_part.new_data.keys() if not legacy_re.match(x) and not uuid_re.match(x)] if new_data_field_ids: print(get_publisher().tenant.hostname, formdata, new_data_field_ids) for field_id in new_data_field_ids: del content_snapshot_part.new_data[field_id] formdata._store_all_evolution = True formdata.store()
Par ailleurs, en analysant un autre cas, j'ai remarqué que des pièces jointes peuvent être stockées dans des objets wcs.wf.jump.WorkflowTriggeredEvolutionPart. C'est le cas sur l'évolution 351698, demande 1-66394 de demarches.toodego.com. L'évolution en question pèse 7MB à cause d'une photo en pièce jointe dans cet objet, est-ce normal que le fichier soit stocké en base au lieu du disque ?
Updated by Frédéric Péters 23 days ago
- Assignee changed from Frédéric Péters to Pierre Ducroquet
J'ai modifié le script comme suit pour le rendre plus efficace et pouvoir le lancer sur toute la prod, je veux bien un avis avant de le faire tourner. Au lieu de regarder les 1000 premières demandes, je cible immédiatement les demandes avec une evolution dont le parts fait plus de 1MB.
En pratique j'avais donc déjà lancé mon code et l'heuristique "si aucun cas dans les 1000 premiers" était une bonne optimisation. Ici je viens de lancer une variante du code proposé sur demarches.toodego.com et ça m'a semblé beaucoup plus long, mais ça a détecté quelques formdatas (23-5602, 85-33655 → 85-33687) qui n'avaient pas été traités (mais pour les 85 c'est plutôt parce qu'ils sont arrivés après le traitement qu'à cause de l'heuristique).
Quoiqu'il en soit, si tu veux, ça doit marcher de lancer ça :
sudo -u wcs wcs-manage runscript --all-tenants ~fred/check_too_much_initial_data_2.py
(assez proche de ta proposition, mais tu peux tout aussi bien lancer ton script).
L'évolution en question pèse 7MB à cause d'une photo en pièce jointe dans cet objet, est-ce normal que le fichier soit stocké en base au lieu du disque ?
En pratique ça n'est pas un fichier qui est stocké dans le WorkflowTriggeredEvolutionPart mais le contenu JSON de la requête appelante. Qui dans le cas que tu pointes est la sérialisation complète d'une demande, ce qui reprend donc les fichiers en base64. La demande en question date de novembre 2023, il faudrait voir au niveau du projet si le déroulé a été modifié depuis.
Updated by Frédéric Péters 23 days ago
La demande en question date de novembre 2023, il faudrait voir au niveau du projet si le déroulé a été modifié depuis.
Ça me semble toujours possible, et inutile, j'ai créé #95234.