Uploads et ecritures atomiques
Ecritures atomiques (hidden stores)
Par defaut, un upload ecrit dans le fichier de destination, comme
HiddenStores off dans ProFTPD : un lecteur peut y voir un fichier partiel
tant que le transfert dure.
Avec enabled = true, l’upload ecrit dans un fichier en cours de transfert
du meme repertoire, puis le renomme en destination a la fin. La destination
n’apparait, ou ne change, qu’une fois le fichier complet.
[server.hidden_stores]
enabled = true
prefix = ".in."
extension = "."
Toutes les cles : Reference [server.hidden_stores].
Le fichier en cours de transfert s’appelle <prefix><nom>.<jeton><extension> :
.in.rapport.csv.3f9a1c04b7e25d68. avec les defauts. Le jeton, 16 caracteres
hexadecimaux, est propre a chaque upload. Un motif .in.* les reconnait tous.
enabled | Pendant le transfert | Upload qui echoue |
|---|---|---|
false | le fichier grossit sous son vrai nom | le fichier partiel reste sous son vrai nom |
true | le fichier en cours de transfert grossit a cote ; la destination est intacte | le fichier en cours de transfert est supprime ; la destination est intacte |
Les fichiers en cours de transfert se listent et se lisent comme les autres.
Ils concernent les backends local et sftp (proxy) ; un objet S3
n’apparait qu’une fois son upload termine, sans fichier a cote.
Par backend, par montage
La meme table se pose a trois niveaux. Chaque cle se resout a part : le
montage l’emporte sur le backend, qui l’emporte sur [server.hidden_stores],
qui l’emporte sur les defauts. SFTP et REST resolvent la meme valeur.
[server.hidden_stores] # tout le serveur
enabled = true
[[backends]]
name = "disque"
type = "local"
root = "/srv/sftp"
hidden_stores = { enabled = false } # ce backend ecrit en place
[[roles]]
name = "depot"
[[roles.mounts]]
backend = "disque"
mount_path = "/depot"
home_dir = "/depot"
hidden_stores = { enabled = true } # sauf ce montage
acl = [{ path = "/", rights = ["write", "list"], recursive = true }]
Restes d’un transfert interrompu
Un processus tue en plein upload laisse son fichier en cours de transfert.
Le serveur le supprime plus tard, quand un upload passe dans le meme
repertoire, une fois le fichier plus vieux que grace_secs.
[uploads.stale_partials]
grace_secs = 900
age_check = true
Toutes les cles : Reference [uploads.stale_partials].
La meme table se pose par backend (stale_partials = { grace_secs = 1800 }
dans son [[backends]]), cle par cle au-dessus de [uploads.stale_partials].
Sur un backend S3, elle regle le menage des uploads multipart abandonnes
(voir S3).
Un reste n’est supprime que si son nom a la forme exacte que le serveur
ecrit, si personne ne tient son verrou, et si son age depasse grace_secs
sur l’horloge du stockage. Au moindre doute, il reste. Chaque suppression
laisse une ligne INFO.
- Les instances qui partagent un stockage ont le meme
uploads.idle_timeout_secs - le seuil se calcule sur celui de l’instance qui balaie.
Deux uploads vers la meme destination
Le premier upload a s’ouvrir garde la destination jusqu’a sa fin. Un second, du meme compte ou d’un autre, par la meme porte ou une autre, est refuse des son ouverture, avant d’envoyer un octet.
Un upload bloque (client suspendu par Ctrl+Z) est repris par une relance du
meme compte sur la meme instance, passe
uploads.takeover_idle_secs sans donnees.
Entre plusieurs instances, un verrou pose sur le stockage, a cote de la
destination, porte la meme regle. Il est rafraichi toutes les 20 s ; celui
d’un processus disparu est repris passe 2 x idle_timeout_secs + 60 s
(120 s par defaut). cross_instance_reservation et lock_prefix :
Reference [[backends]].
Ce qu’est le verrou sur chaque type : Local,
S3, Proxy SFTP.
Les noms qui commencent par .craftfilegate-upload ou par le lock_prefix
appartiennent au serveur : on les liste et on les lit, on n’y ecrit pas.
Avec une seule instance, cross_instance_reservation = false suffit.
Plafonner la taille d’un fichier
max_file_mb, sur un montage, plafonne la taille de chaque fichier envoye
(1 Mo = 1 048 576 octets). Ce n’est pas un compteur d’espace.
[[roles.mounts]]
backend = "disque"
home_dir = "/{username}"
max_file_mb = 500
| Valeur | Effet |
|---|---|
| absente | aucun plafond |
N | chaque fichier envoye fait au plus N Mo, juge avant le stockage (en REST, sur Content-Length avant le corps) ; ce qu’un envoi qui depasse avait ecrit est retire |
0 | aucun envoi sous ce montage, lecture seule de fait |
Ce que vous verrez
| Quand | Ligne |
|---|---|
| demarrage et rechargement, par backend | INFO stale in-flight files: ..., champs backend, grace_secs, grace_from, age_check, idle_timeout_secs |
| un reste supprime | INFO removed a stale in-flight file: ..., champs backend, path, age_secs, clock, lock |
| verrou d’un processus disparu repris | INFO took over a stale upload reservation: ... (local), took over the marker of an upload ... (S3), took over the lock file of an upload ... (proxy) |
| ecart d’horloge du stockage | metrique craftfilegate_stale_partials_clock_skew_seconds{backend} |