Mon système de fichiers, le zFS
Mon système de fichiers, le zFS
d'ailleurs, chiffrer les données serait vraiment très simple, suffit d'ajouter une fonction chiffrer() dechiffrer() sur les 2 fonctions que j'ai écris au-dessus, autour de compress() et decompress()
- augur1
- Messages : 13167
- Inscription : ven. 12 janv. 2018 17:44
- Localisation : où tout est neuf et tout est sauvage
- Contact :
Mon système de fichiers, le zFS
+1tu sais moi j'y connais rien alors j'admire juste ^^
... et pis si tu prévois une possibilité d'être en block ET de pouvoir partager les fichiers entre plusieurs machine, ce serait top !

Mon système de fichiers, le zFS
ça marche en réseau, suffit que chaque machine utilise la même base de données en backend ^^+1
... et pis si tu prévois une possibilité d'être en block ET de pouvoir partager les fichiers entre plusieurs machine, ce serait top !
c'est ça l'intérêt du zFS !

edit : j'ai pas mal amélioré les performances, mais là je bute sur un bug qui fait que quand on demande un morceau de fichier plus petit que la taille d'un bloc ça plante, ça devrait être facile à corriger mais là ça me prend la tête. Pour les performances, elles restent assez faibles, surtout pour les gros fichiers. Le but n'étant pas les performances, j'essaie quand mêmeque ça reste à des niveaux raisonnables. On verra bien ^^ Après tout, je fais ça pour le fun aussi !
- augur1
- Messages : 13167
- Inscription : ven. 12 janv. 2018 17:44
- Localisation : où tout est neuf et tout est sauvage
- Contact :
Mon système de fichiers, le zFS
Yeahhh ^^
ça marche en réseau, suffit que chaque machine utilise la même base de données en backend ^^
c'est ça l'intérêt du zFS !
... et donc : 1 fibre pour du FC 16Gb/s + 1 GbE pour les metadonnées ?

Mon système de fichiers, le zFS
tout passe en même temps, les données et métadonnées sont sur la même machine
mais ne t'attends pas à des grosses performances.
Si tu veux un truc hyper performant, j'ai trouvé qqn qui a écrit un système de fichiers ramdisk réseau qui cumule la ram de plusieurs machines
mais ne t'attends pas à des grosses performances.
Si tu veux un truc hyper performant, j'ai trouvé qqn qui a écrit un système de fichiers ramdisk réseau qui cumule la ram de plusieurs machines

Mon système de fichiers, le zFS
Yeahhh ^^
... et donc : 1 fibre pour du FC 16Gb/s + 1 GbE pour les metadonnées ?
avec mon i3 et un ssd je fais du 7 Mo/s en écriture sur un mongodb en local



y a sûrement moyen de gagner en performances, mais ça n'ira jamais bien haut
Mon système de fichiers, le zFS
ça me prend la tête
j'ai choisi d'augmenter la taille des blocks (32768 octets) pour augmenter les performances (on écrit des plus gros morceaux, ça fait moins de transactions base de données).
Cependant, plein de programmes utilisent une taille de 4096 octets pour l'écriture ou la lecture. Le problème, c'est que l'algo est le suivant :
- si écriture < taille du block
- lecture du block, décompression du bloc, concaténation du morceau à écrire à ce qui existe déjà, compression, mise à jour base de données
c'est trèèèèèèèèèèèèèès lent, c'est du gaspillage !


j'ai choisi d'augmenter la taille des blocks (32768 octets) pour augmenter les performances (on écrit des plus gros morceaux, ça fait moins de transactions base de données).
Cependant, plein de programmes utilisent une taille de 4096 octets pour l'écriture ou la lecture. Le problème, c'est que l'algo est le suivant :
- si écriture < taille du block
- lecture du block, décompression du bloc, concaténation du morceau à écrire à ce qui existe déjà, compression, mise à jour base de données
c'est trèèèèèèèèèèèèèès lent, c'est du gaspillage !
Mon système de fichiers, le zFS
Code : Tout sélectionner
#!/usr/bin/env perl
use strict;
use warnings;
use Fuse;
use File::Basename;
use MongoDB;
use MongoDB::OID;
use Data::Dumper;
use Time::HiRes;
use POSIX qw(ENOENT EISDIR EINVAL floor ceil);
use File::stat;
use File::Stat::Bits;
use threads;
use threads::shared;
use Compress::Snappy;
use constant DEBUG => 1;
use constant DEBUGFUSE => 0;
use constant BLKSIZE => 32768;
my $host = shift;
my $client = MongoDB::MongoClient->new(
host => $host,
auto_reconnect => 1,
auto_connect => 1,
timeout => 10000,
);
my $db = $client->get_database('fs');
my $fs = $db->get_collection('files');
my %TYPES = (
"S_IFDIR" => S_IFDIR,
"S_IFREG" => S_IFREG,
"S_IFLNK" => S_IFLNK
);
my ($debut,$fin);
sub get_dir_id {
my $path = shift;
$path =~ s|/$||;
my $id = 0;
print "get_dir_id : $path\n" if DEBUG;
if (length($path) > 1) {
my $node = $fs->find({ "path" => $path })->limit(1)->fields({ _id => 1})->next;
$id = $node->{_id};
}
return $id;
}
sub e_chmod {
my $path = shift;
my $mode = shift;
print "e_chmod : $path $mode\n" if DEBUG;
$fs->update({ "path" => $path }, {'$set', =>{'mode' => $mode}});
return 0;
}
sub e_chown {
my $path = shift;
my $uid = shift;
my $gid = shift;
print "e_chown : $path $uid $gid\n" if DEBUG;
$fs->update({ "path" => $path }, {'$set', =>{'uid' => $uid}});
$fs->update({ "path" => $path }, {'$set', =>{'gid' => $gid}});
return 0;
}
sub e_utime {
print "e_utime\n" if DEBUG;
my $path = shift;
my $atime = shift;
my $ctime = shift;
$fs->update({ "path" => $path }, {'$set' =>{'atime' => "$atime"}});
$fs->update({ "path" => $path }, {'$set' =>{'ctime' => "$ctime"}});
return 0;
}
sub e_unlink {
my $path = shift;
print "e_unlink : $path\n" if DEBUG;
my $file = $fs->find_one({ "path" => $path});
$fs->remove({ file_parent => $file->{_id}});
$fs->remove( { "path" => $path });
return 0;
}
sub e_rename {
my ($old, $new) = @_;
print "e_rename : $old $new \n" if DEBUG;
my ($name,$chemin) = fileparse $new;
my $parent = get_dir_id $chemin;
my $file = $fs->find_one({ "path" => $new});
if(exists($file->{path})) {
e_unlink $new;
}
$fs->update({ "path" => $old }, {'$set' =>{'name' => $name}});
$fs->update({ "path" => $old }, {'$set' =>{'parent' => $parent}});
$fs->update({ "path" => $old }, {'$set' =>{'path' => $new}});
return 0;
}
sub e_rmdir {
my $path = shift;
my $parent = get_dir_id $path;
my $check = $fs->find({ "parent" => $parent })->limit(1)->count();
if($check == 0) { # le dossier est vide
$fs->remove( { "path" => $path });
return 0;
} else {
return -1;
}
}
sub e_create {
my $path = shift;
my $mask = shift;
print "e_create : $path \n" if DEBUG;
my $mode = 0b111111111;
$mode &= $mask;
# dossier parent et nom
my ($name,$chemin) = fileparse $path;
my $time = time();
my $parent = get_dir_id $chemin;
$fs->insert(
{
path => $path,
parent => $parent,
name => $name,
data => undef,
mode => $mode,
uid => 0,
gid => 0,
rdev => 0,
atime => "$time",
ctime => "$time",
mtime => "$time",
nlink => 1,
type => "S_IFREG"
});
return 0;
}
sub e_symlink {
my $dest = shift;
my $path = shift;
print "e_symlink -> $path $dest\n" if DEBUG;
# dossier parent et nom
my ($name,$chemin) = fileparse $path;
my $time = time();
my $parent = get_dir_id $chemin;
$fs->insert(
{
path => $path,
parent => $parent,
dest => $dest,
name => $name,
mode => 0777,
uid => 0,
gid => 0,
rdev => 0,
atime => "$time",
ctime => "$time",
mtime => "$time",
nlink => 1,
type => "S_IFLNK"
});
return 0;
}
sub e_mkdir {
my $path = shift;
my $mode = shift;
print "e_mkdir -> $path\n" if DEBUG;
# dossier parent et nom
my ($name,$chemin) = fileparse $path;
my $time = time();
my $parent = get_dir_id $chemin;
$fs->insert(
{
path => $path,
parent => $parent,
name => $name,
size => 0,
mode => $mode,
uid => 0,
gid => 0,
rdev => 0,
atime => "$time",
ctime => "$time",
mtime => "$time",
nlink => 1,
type => "S_IFDIR"
});
return 0;
}
sub e_read {
my ($path,$size,$offset,$fh) = @_;
my $block_start = floor($offset / BLKSIZE);
my $block_count = ceil($size / BLKSIZE);
my $output = undef;
print "e_read -> $path : $size : $offset : $block_start : $block_count \n" if DEBUG;
my $file = $fs->find_one(
{ "path" => $path},
{ blocks => { '$slice' => [$block_start, $block_count] }});
if(exists($file->{blocks})) {
my $blocks = $fs->find( { _id => { '$in' => $file->{blocks} }})->sort([block_number => 1]);
while(my $data = $blocks->next) {
$output .= decompress($data->{data});
}
$output = substr $output, $offset % BLKSIZE, $size;
}
return $output;
}
sub e_write {
# chemin complet
my $path = shift;
# contenu
my $buffer = shift;
# offset
my $offset = shift;
# taille buffer
my $size = length($buffer);
# result à enregistrer
my $data;
print "e_write => $path : $size $offset \n" if DEBUG;
my $file = $fs->find_one({ "path" => $path });
if( $offset == 0 && exists($file->{blocks}) ) {
print "e_write => suppression des blocks \n" if DEBUG;
foreach my $id (@{$file->{blocks}}) {
$fs->remove( { _id => $id });
}
$fs->update( { path => $path }, {'$set' => { blocks => []}});
$fs->update( { path => $path }, {'$set' => { size => 0}});
}
if($size >= BLKSIZE) {
for(my $i=0; $i< $size / BLKSIZE; $i++) {
my $block = $i + $offset / BLKSIZE;
print "block : $block\n";
my $morceau = substr $buffer, $i * BLKSIZE, BLKSIZE;
my $obj = MongoDB::BSON::Binary->new(data => compress($morceau));
my $block_id = $fs->insert( { data => $obj, file_parent => $file->{_id}, block_number => $block });
$fs->update( { path => $path }, {'$push' => { blocks => $block_id}});
$fs->update( { path => $path }, {'$inc' => { size => length($morceau)}});
}
}
# TODO : gérer une fin de bloc qui va sur un nouveau bloc
if($size find_one( { file_parent => $file->{_id}, block_number => $block });
my $obj = MongoDB::BSON::Binary->new(data => compress(decompress($partial->{data}).$buffer));
$fs->update( { _id => $partial->{_id} }, {'$set' => { data => $obj}});
}
else { # nouveau bloc
print "nouveau block $block\n";
my $obj = MongoDB::BSON::Binary->new(data => compress($buffer));
my $block_id = $fs->insert( { data => $obj, file_parent => $file->{_id}, block_number => $block });
$fs->update( { path => $path }, {'$push' => { blocks => $block_id}});
}
}
$fs->update( { path => $path }, {'$inc' => { size => $size}});
return length($buffer);
}
sub e_getattr {
my $node = shift;
print "e_getattr : $node\n" if DEBUG;
my $type;
my $size = 0;
my ($uid,$gid);
my $mode;
my ($atime, $ctime, $mtime);
if($node eq "/") {
$type = S_IFDIR;
($uid,$gid) = (0,0);
$mode = 0777;
$mode |= $type;
$atime = $ctime = $mtime = time()-1000;
} else {
my $name = $fs->find( { path => $node } )->next;
return -ENOENT() unless exists($name->{name});
if (exists($name->{size})) {
$size = $name->{size};
} else {
$size = 0;
}
$type = $TYPES{$name->{type}};
$uid = $name->{uid};
$gid = $name->{gid};
$mode = $name->{mode};
$mode |= $type;
$mtime = $name->{mtime};
$ctime = $name->{ctime};
$atime = $name->{atime};
}
my ($dev,$ino,$rdev, $blocks, $nlink, $blksize) = (0,0,0,0,1,BLKSIZE);
return ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks);
}
sub e_statfs {
print "e_statfs\n";
return 255, 2, 1, BLKSIZE, BLKSIZE, 1;
}
sub e_open {
print "e_open\n";
return 0;
}
sub e_readdir {
my $path = shift;
print "e_readdir => $path \n" if DEBUG;
my $parent = get_dir_id $path;
my @result;
my $files = $fs->find( { parent => $parent } )->fields({ _id => 0 });
my ($dev,$ino,$rdev, $blocks, $nlink, $blksize,$size) = (0,0,0,1,1,1024,0);
while(my $name = $files->next) {
my $type;
my ($uid,$gid);
my $mode;
my ($atime, $ctime, $mtime);
$size = $name->{size};
$type = $TYPES{$name->{type}};
$uid = $name->{uid};
$gid = $name->{gid};
$mode = $name->{mode};
$mode |= $type;
$mtime = $name->{mtime};
$ctime = $name->{ctime};
$atime = $name->{atime};
push(@result, [0,$name->{name}, [$dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks]]);
}
push(@result, 0);
return @result;
}
sub e_truncate {
print "e_truncate \n" if DEBUG;
my $path = shift;
my $offset = shift;
my $file = $fs->find_one({ "path" => $path});
my $value;
if($offset < $file->{"size"}) {
my $data = $file->{"data"};
$value = substr $data, 0, $offset;
$fs->update({ "path" => $path }, {'$set' =>{'data' => $data}});
$fs->update({ "path" => $path }, {'$set' =>{'size' => length($data)}});
}
return 0;
}
sub e_readlink {
my $path = shift;
print "e_readlink : $path\n" if DEBUG;
my $file = $fs->find({ "path" => $path })->fields({ dest => 1})->next;
return $file->{dest};
}
my ($mountpoint) = "";
$mountpoint = shift(@ARGV) if @ARGV;
Fuse::main(
mountpoint=>$mountpoint,
mountopts => "big_writes",
getattr =>"main::e_getattr",
getdir =>"main::e_getdir",
open =>"main::e_open",
statfs =>"main::e_statfs",
read =>"main::e_read",
write =>"main::e_write",
create =>"main::e_create",
utime =>"main::e_utime",
unlink =>"main::e_unlink",
mkdir =>"main::e_mkdir",
rmdir =>"main::e_rmdir",
truncate =>"main::e_truncate",
symlink =>"main::e_symlink",
readlink =>"main::e_readlink",
readdir =>"main::e_readdir",
rename =>"main::e_rename",
chown =>"main::e_chown",
chmod =>"main::e_chmod",
threaded => 0,
debug => DEBUGFUSE
);

reste à gérer une écriture qui empiète à moitié sur un bloc et qui en écrit sur un autre, pour le moment aç fait de la merde mais avec des programmes "normaux" c'est pas censé arriver (souvent on utilise des puissances de 2 en taille de buffer et d'offset.
la fonction truncate marche pas vraiment non plus
et j'ai plein de vérifications à rajouter par-ci par-là pour avoir des erreurs qui correspondent bien à ce qui arrive
Mon système de fichiers, le zFS
je faisais un transfert pour essai, ça plafonnait à 200 ko/s
j'ai ajouté un index et ça a décollé à 12 Mo/s avec perl qui bouffe 100% du cpu
j'ai ajouté un index et ça a décollé à 12 Mo/s avec perl qui bouffe 100% du cpu

Mon système de fichiers, le zFS
mdr
perf inside
perf inside
Mon système de fichiers, le zFS
j'ai encore augmenté les performances 

Mon système de fichiers, le zFS
tu as changé de cpu ?

Mon système de fichiers, le zFS
non j'ai amélioré l'algorithme d'écriture pour regrouper des opérations, ça améliore un poil mais ça reste vraiment lenttu as changé de cpu ?![]()
je pense que le seul moyen de régler efficacement le problème de performance c'est de travailler de manière asynchrone. On fait tellement de petites opérations que ça en devient lent car on fait des micro ajouts (de 4k) et ça fait travailler la bdd pour rien
- augur1
- Messages : 13167
- Inscription : ven. 12 janv. 2018 17:44
- Localisation : où tout est neuf et tout est sauvage
- Contact :
Mon système de fichiers, le zFS
Ou comme en zfs : t'ecris tout en memoire et t'envoie en sequentiel sur le hdd 

Mon système de fichiers, le zFS
Comme beaucoup en faitOu comme en zfs : t'ecris tout en memoire et t'envoie en sequentiel sur le hdd

Y a un commande fsync d'ailleurs dans les fs qui sert à leur dire de synchroniser les écritures.
Mon système de fichiers, le zFS
C'est marrant tiens, entre mon laptop pro et mon laptop perso qui est en wifi en local, j'avais ~~ 1 mo/s
Entre le laptop pro (client) et de la fibre SFR et mon serveur online, j'ai 3 Mo/s en écriture
Vive le wifi
Entre le laptop pro (client) et de la fibre SFR et mon serveur online, j'ai 3 Mo/s en écriture

Vive le wifi

- dsebire
- Messages : 13158
- Inscription : ven. 12 janv. 2018 17:44
- Localisation : Loiret - entre la ville et les champs
Mon système de fichiers, le zFS
vu que tu passe ton temps a faire des accès, a chaque fois, tu prend la latence du ping.
sur du Wifi, c'est beaucoup plus élevé qu'une fibre internet
sur du Wifi, c'est beaucoup plus élevé qu'une fibre internet

Mon système de fichiers, le zFS
normalement le ping en local d'un wifi il est quand même inférieur à 5 ms, mais je pense plutôt à une congestion à cause du nombre de paquets envoyésvu que tu passe ton temps a faire des accès, a chaque fois, tu prend la latence du ping.
sur du Wifi, c'est beaucoup plus élevé qu'une fibre internet

je suis en train de faire un benchmark avec iozone, je sais pas trop comment on lit et si on peut faire un graphique avec ^^
Mon système de fichiers, le zFS
j'ai amélioré les performances, j'ai 3,50 Mo/s d'écriture via fibre 

- dsebire
- Messages : 13158
- Inscription : ven. 12 janv. 2018 17:44
- Localisation : Loiret - entre la ville et les champs
Mon système de fichiers, le zFS
le wifi chez moi, c'est très variable.
si chargé (pas nécessairement en débit mais accès) ça peut monter a 100ms
si chargé (pas nécessairement en débit mais accès) ça peut monter a 100ms
Mon système de fichiers, le zFS
+1
je le voi en jeux, +de 100ms de ping en wifi, a peine plus de 50 en LAN
je le voi en jeux, +de 100ms de ping en wifi, a peine plus de 50 en LAN

Mon système de fichiers, le zFS
c'est vraiment dla merde le wifi 

Mon système de fichiers, le zFS
Bah c'est de la radio, donc par nature la qualité du support fluctue bcp, et la qualité du transfert suit la tendance 

Mon système de fichiers, le zFS
Et hop, le FS gère le multithreading. Ca veut dire que l'on peut copier un fichier tout en visualisant des fichiers. Avant c'était bloquant 

Mon système de fichiers, le zFS
Je vais faire un article dans BSD Magazine (numéro de novembre) sur une introduction à Fuse si ça vous intéresse 
