Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

enabledPendant le transfertUpload qui echoue
falsele fichier grossit sous son vrai nomle fichier partiel reste sous son vrai nom
truele fichier en cours de transfert grossit a cote ; la destination est intactele 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
ValeurEffet
absenteaucun plafond
Nchaque 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
0aucun envoi sous ce montage, lecture seule de fait

Ce que vous verrez

QuandLigne
demarrage et rechargement, par backendINFO stale in-flight files: ..., champs backend, grace_secs, grace_from, age_check, idle_timeout_secs
un reste supprimeINFO removed a stale in-flight file: ..., champs backend, path, age_secs, clock, lock
verrou d’un processus disparu reprisINFO 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 stockagemetrique craftfilegate_stale_partials_clock_skew_seconds{backend}