Page 2 sur 2

Mon système de fichiers, le zFS

Publié : lun. 5 oct. 2015 14:17
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()

Mon système de fichiers, le zFS

Publié : lun. 5 oct. 2015 18:39
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 ! ;)

Mon système de fichiers, le zFS

Publié : lun. 5 oct. 2015 18:52
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 !

Mon système de fichiers, le zFS

Publié : lun. 5 oct. 2015 19:11
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 ? :)

Mon système de fichiers, le zFS

Publié : lun. 5 oct. 2015 19:18
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

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 10:00
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

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 10:47
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 !

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 14:34
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

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 16:09
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:

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 18:26
par poulpito
mdr
perf inside

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 18:28
par Zedoune
j'ai encore augmenté les performances :D

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 18:33
par poulpito
tu as changé de cpu ? :D

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 18:38
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

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 18:42
par augur1
Ou comme en zfs : t'ecris tout en memoire et t'envoie en sequentiel sur le hdd ;)

Mon système de fichiers, le zFS

Publié : mar. 6 oct. 2015 20:32
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.

Mon système de fichiers, le zFS

Publié : mer. 7 oct. 2015 10:38
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

Mon système de fichiers, le zFS

Publié : mer. 7 oct. 2015 10:45
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 ;)

Mon système de fichiers, le zFS

Publié : mer. 7 oct. 2015 10:48
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 ^^

Mon système de fichiers, le zFS

Publié : mer. 7 oct. 2015 12:53
par Zedoune
j'ai amélioré les performances, j'ai 3,50 Mo/s d'écriture via fibre :)

Mon système de fichiers, le zFS

Publié : mer. 7 oct. 2015 14:30
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

Mon système de fichiers, le zFS

Publié : mer. 7 oct. 2015 15:33
par biour
+1
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

Publié : mer. 7 oct. 2015 16:02
par Zedoune
c'est vraiment dla merde le wifi :o

Mon système de fichiers, le zFS

Publié : mer. 7 oct. 2015 17:08
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 :)

Mon système de fichiers, le zFS

Publié : mer. 7 oct. 2015 22:43
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 :)

Mon système de fichiers, le zFS

Publié : lun. 26 oct. 2015 15:56
par Zedoune
Je vais faire un article dans BSD Magazine (numéro de novembre) sur une introduction à Fuse si ça vous intéresse :)