Problème de mise à jour de BLOB lors du stockage de documents sur S3

Bonjour à tous,

Nous rencontrons un comportement étrange lors de la mise en place du stockage de documents sur une base de données S3. Voici une description détaillée du problème :

Contexte :

  • Nous avons configuré l’accès au S3 en nous basant sur le code d’exemple du module Minio.

  • Le fichier est bien déposé sur le S3.

  • Dans l’interface de paramétrage de l’objet métier, nous voyons bien le document ajouté dans l’onglet “Documents”.

  • Nous pouvons également télécharger le document sans problème à partir de cette liste, et les logs montrent un appel correct au S3.

Problème :

Lors de l’upload d’un document, l’IHM renvoie une erreur :

blob update failed

Les logs système affichent les erreurs suivantes :

2026-03-20 11:16:58,486|SIMPLICITE|ERROR||http://XXX/oliv|/oliv|ECOREDB001|system|com.simplicite.util.engine.GrantDirect|updateBlob||Error SQL query: update m_document set dbd_content=? where dbd_path = 'NomvDocuments3/nomvDocFichier/0/19/Bannière 2.png'
    org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.

2026-03-20 11:16:58,487|SIMPLICITE|ERROR||http://XXX/oliv|/oliv|ERROR|system|com.simplicite.util.tools.DocTool|writeFile||Event: Write error: NomvDocuments3/nomvDocFichier/0/19/Bannière 2.png
    java.io.IOException: BLOB update failed (-1)

2026-03-20 11:16:58,487|SIMPLICITE|ERROR||http://XXX/oliv|/oliv|ECORED0001|a.zoopy|com.simplicite.util.tools.DocTool|upload||Erreur Upload error
    java.io.IOException: BLOB update failed (-1)

Observations supplémentaires :

  • La table globale des documents Simplicité est bien mise à jour.

  • La suppression d’un document dans la liste envoie bien un appel de suppression au S3.

  • Si nous supprimons manuellement un document dans le S3, le téléchargement depuis Simplicité renvoie un fichier vide (0 octet), car Simplicité ne détecte pas la suppression.

Hypothèse :

Il semble que seul le lien entre l’objet métier et le document déposé pose problème lors de la mise à jour. Tout le reste fonctionne correctement.

Questions :

  • Avez-vous déjà rencontré ce type de problème ?

  • Y a-t-il des configurations spécifiques à vérifier pour le stockage des documents sur S3 ?

  • Des suggestions pour résoudre ce problème de mise à jour de BLOB ?

Merci d’avance pour votre aide !

Pour bien cerner le problème, on parle bien d’une modification d’un document préalablement créé (et bien présent dans votre bucket S3) ?

Sur ce point:

  • Si nous supprimons manuellement un document dans le S3, le téléchargement depuis Simplicité renvoie un fichier vide (0 octet), car Simplicité ne détecte pas la suppression.

C’est totalement normal, Simplicité pilote le stockage, pas l’inverse, supprimer un document directement dans un bucket ne peut donc pas être notifié à Simplicité. Nous allons regarder comment mieux indiquer le cas où le document référencé par Simplicité n’est plus physiquement là.

Bonjour, la suppression dans le S3 invisible dans simplicité n’est pour l’instant pas un problème, je voulais juste expliquer les test que nous avons fait. Nous parlons d’un document qui n’a jamais été créer dans simplicité. L’attribut document est vide et lorsque nous y déposons un document nous avons une erreur.

Je ne constate pas de problème (ni message UI ni erreur dans les logs) à la création d’un document sur un bucket Minio:


Je sélectionne un document puis je clique sur Save, celui-ci est bien dans le bucket

Le document téléchargé via le bouton ad hoc est correct.

Et si je modifie le document, celui-ci est bien aussi mis à jour coté bucket

N’y aurait il pas des spécificités sur votre S3 ou votre réseau, ex: limites de taille/type de fichier uploadable dans le bucket, timeout, etc.

Ou alors il y a des différences vs l’exemple GitHub que j’utilise dans la manière dont vous avez implémenté vos platform hooks (sans votre code je ne peux pas savoir).

PS: Merci de systématiquement préciser la version de Simplicité (le plus simple étant de nous fournir le résultat du /health)que vous utilisez lorsque vous sollicitez notre support

Bonjour,

Voici mon /health

[Platform]
Status=OK
Version=6.3.5
BuiltOn=2026-02-25 23:18
Git=6.3
Encoding=UTF-8
EndpointIP=
EndpointURL=
TimeZone=Europe/Paris
SystemDate=2026-03-24 16:14:48

[Application]
ApplicationVersion=1.0.0
ContextPath=
ContextURL=
ActiveSessions=0
TotalUsers=4
EnabledUsers=2
LastLoginDate=2026-03-23 14:43:07

[Server]
ServerInfo=Apache Tomcat/9.0.108
ServerType=WEB
ServerDevMode=true
ServerCompiler=true
ServerActiveSessions=0
ServerSessionTimeout=30
CronStarted=true

[OS]
Name=Linux
Architecture=amd64
Version=5.14.0-570.60.1.el9_6.x86_64
SystemEncoding=UTF-8

[Disk]
DiskFree=6484
DiskUsable=6484
DiskTotal=12224

[JavaVM]
Version=21.0.9
Vendor=Red Hat, Inc.
VMName=OpenJDK 64-Bit Server VM
VMVersion=21.0.9+10-LTS
ScriptEngine=rhino
ScriptEngineVersion=Rhino 1.7.13 2020 09 02
HeapFree=808594
HeapSize=1482752
HeapMaxSize=1482752
TotalFreeSize=808594

[Cache]
ObjectCache=55
ObjectCacheMax=10000
ObjectCacheRatio=0
ProcessCache=0
ProcessCacheMax=10000
ProcessCacheRatio=0
APIGrantCache=0
APIGrantCacheMax=1000
APIGrantRatio=0

[Database]
Vendor=3
VendorName=postgresql
ProductName=PostgreSQL
ProductVersion=16.9
DriverName=PostgreSQL JDBC Driver
DriverVersion=42.7.10
DBDate=2026-03-24 16:14:48
DBDateOffset=0
DBPatchLevel=6;P03;51af22b13838925e31430a969bd336ba;5
UsingBLOBs=true

[Healthcheck]
Date=2026-03-24 16:14:59
ElapsedTime=11283

et mon code :

package com.simplicite.commons.ConnexionS3;

import java.util.*;
import java.io.File;
import java.nio.file.Files;
import java.io.InputStream;
import java.io.ByteArrayInputStream;

import org.json.JSONObject;

import com.simplicite.util.;
import com.simplicite.bpm.;
import com.simplicite.util.exceptions.;
import com.simplicite.util.tools.;

/**

Platform Hooks
*/
public class PlatformHooksArtesca extends com.simplicite.util.engine.PlatformHooksInterface {

private File getStorageFile(String path) {
AppLog.warning(“Activation hook artesca”);
// Attention pour l’instant n’envoi que les documents des objets du module TestNommageVanilla
return path.startsWith(“Nomv”) ? new File(path.replace(“/”, “~”).replace(" “,”_")) : null;
}

private CloudStorageTool getStorageTool() throws Exception {
//AppLog.info(Grant.getSystemAdmin().getJSONObjectParameter(“ARTESCA_CONFIG”).toString(), null);
return new CloudStorageTool(Grant.getSystemAdmin().getJSONObjectParameter(“ARTESCA_CONFIG”));
}

@Override
public InputStream readDocument(String path) throws Exception {
File f = getStorageFile(path);

 if (f == null)
     return null;
 AppLog.warning("Lecture S3", null);
 try (CloudStorageTool cst = getStorageTool()) {
     JSONObject sf = cst.get(f.getName(), true);
     return new ByteArrayInputStream((byte[])sf.get("content"));
 } catch (Exception e) {
     AppLog.error(null, e, null);
     return null;
 }

}

@Override
public boolean writeDocument(String path, Object data) throws Exception {
File f = getStorageFile(path);
AppLog.warning(“Demande écriture S3”, null);
if (f == null){
AppLog.warning(“Document non S3”, null);
return false;
}
try (CloudStorageTool cst = getStorageTool()) {
AppLog.warning(“Ecriture S3 en cours”, null);
cst.put(new JSONObject().put(“name”, f.getName()).put(“mime”, Files.probeContentType(f.toPath())).put(“content”, data));
AppLog.warning("SUCCES Ecriture S3 ", null);
return true;
} catch (Exception e) {
AppLog.error(null, e, null);
return false;
}
}

@Override
public boolean deleteDocument(String path) throws Exception {
File f = getStorageFile(path);
if (f == null)
return false;
AppLog.warning(“Suppression document S3”, null);

 try (CloudStorageTool cst = getStorageTool()) {
     cst.delete(f.getName());
     return true;
 } catch (Exception e) {
     AppLog.error(null, e, null);
     return false;
 }

}

}

Sur le code ça me semble correspondre à ce que je teste = +/- le code du module d’exemple sur GitHub que j’utilise pour mes tests…

Je ne vois donc pas trop pourquoi ça ne marcherait pas chez vous alors que ça marche chez moi, si ce n’est des potentielles différences au niveau de la configuration du service S3 et/ou du réseau (cf. ma réponse précédente).

A noter que j’utilise pour mes tests une 6.3 à jour (= révision 6.3.6 du 12 mars 2026) mais il n’y a pas de différences au niveau du CloudStorageTool

Ce qui m’étonne le plus c’est cette erreur : Error SQL query: update m_document set dbd_content=? where dbd_path = ‘NomvDocuments3/nomvDocFichier/0/19/CpjAAPThese.java’
org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.

Est-ce que cloudstorageTool s’attend à une certaine réponse du S3 pour finir d’enregistrer le document ?

Est-ce que le nom du fichier indiqué dans l’erreur (CpjAAPThese.java) est le nom du fichier que vous essayez d’enregistrer ?

Avez vous plus d’une classe PlatformHooks* ?

Il me faudrait l’ensemble des logs correspondant au save de l’objet pour mieux comprendre ce qu’il se passe exactement

Le fichier java est bien l’objet à stocker.

J’ai bien un deuxième plateformehooks qui overide la méthode parseAuth uniquement.

Comment puis-je trouver le log du save ?

OK nous avons détecté une anomalie lorsqu’il y a plusieurs platform hooks (d’où ma question sur ce point), on va corriger ça dans la prochaine révision 6.3.7

En attendant vous pouvez soit fusionner temporairement ces 2 classes en une seule, soit (je n’ai pas testé mais ça doit marcher) implementer temporairement les 2 hooks writeDocument/deleteDocument avec un simplereturn true dans la classe du platform hook qui ne les contient pas.

This topic was automatically closed 60 minutes after the last reply. New replies are no longer allowed.