Mon système de fichiers, le zFS

Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

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()
Avatar de l’utilisateur
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

Message par augur1 »

tu sais moi j'y connais rien alors j'admire juste ^^
+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 ! ;)
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

+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 ! ;)
ç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 ! :D


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 !
Avatar de l’utilisateur
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

Message par augur1 »


ç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 ! :D
Yeahhh ^^
... et donc : 1 fibre pour du FC 16Gb/s + 1 GbE pour les metadonnées ? :)
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

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 :D
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

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 :lol: :lol: :lol:
y a sûrement moyen de gagner en performances, mais ça n'ira jamais bien haut
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

ça me prend la tête :pt1cable: :pt1cable:

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 !
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

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
    );
ça marche relativement bien :D

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
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

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 :whistle:
Avatar de l’utilisateur
poulpito
Messages : 12402
Inscription : ven. 12 janv. 2018 17:44
Localisation : Grenoble

Mon système de fichiers, le zFS

Message par poulpito »

mdr
perf inside
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

j'ai encore augmenté les performances :D
Avatar de l’utilisateur
poulpito
Messages : 12402
Inscription : ven. 12 janv. 2018 17:44
Localisation : Grenoble

Mon système de fichiers, le zFS

Message par poulpito »

tu as changé de cpu ? :D
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

tu as changé de cpu ? :D
non j'ai amélioré l'algorithme d'écriture pour regrouper des opérations, ça améliore un poil mais ça reste vraiment lent

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
Avatar de l’utilisateur
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

Message par augur1 »

Ou comme en zfs : t'ecris tout en memoire et t'envoie en sequentiel sur le hdd ;)
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

Ou comme en zfs : t'ecris tout en memoire et t'envoie en sequentiel sur le hdd ;)
Comme beaucoup en fait ;)

Y a un commande fsync d'ailleurs dans les fs qui sert à leur dire de synchroniser les écritures.
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

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 :D

Vive le wifi :o
Avatar de l’utilisateur
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

Message par dsebire »

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 ;)
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

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 ;)
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és :)

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 ^^
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

j'ai amélioré les performances, j'ai 3,50 Mo/s d'écriture via fibre :)
Avatar de l’utilisateur
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

Message par dsebire »

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
Avatar de l’utilisateur
biour
Messages : 24388
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par biour »

+1
je le voi en jeux, +de 100ms de ping en wifi, a peine plus de 50 en LAN
Image
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

c'est vraiment dla merde le wifi :o
Avatar de l’utilisateur
c0bw3b
Messages : 5521
Inscription : ven. 12 janv. 2018 17:44
Localisation : Lyon

Mon système de fichiers, le zFS

Message par c0bw3b »

Bah c'est de la radio, donc par nature la qualité du support fluctue bcp, et la qualité du transfert suit la tendance :)
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

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 :)
Avatar de l’utilisateur
Zedoune
Messages : 15343
Inscription : ven. 12 janv. 2018 17:44

Mon système de fichiers, le zFS

Message par Zedoune »

Je vais faire un article dans BSD Magazine (numéro de novembre) sur une introduction à Fuse si ça vous intéresse :)
Répondre