Note of MogileFS #09 - Inside MogileFS architecture (2)
ã¯ããã«
ååã®ã¨ã³ããª(d:id:ZIGOROu:20061018:1161155472)ã®ç¶ãã§ãã
ååã¯ä¸»ã«daemonã¨ãã¦ã®èµ·åããclientããã®ãªã¯ã¨ã¹ãåä¿¡ãã³ãã³ãã®ãã¼ã¹è¾ºãã¾ã§ã ã£ããã§ã
å®éã«çºè¡ãããcreate_openã³ãã³ããªã©ãè¦ã¦è¡ããã¨æãã¾ãã
MogileFS::Worker::Query->cmd_create_open
ããã®ã½ã¼ã¹ã¯ç¸å½é·ãã§ããæ°åå ¥ãã¦èªã¾ãªãã¨è¿·åã«ãªãã¾ãã
sub cmd_create_open { my MogileFS::Worker::Query $self = shift; my $args = shift; # has to be filled out for some plugins $args->{dmid} = $self->check_domain($args) or return $self->err_line('domain_not_found'); # first, pass this to a hook to do any manipulations needed MogileFS::run_global_hook('cmd_create_open', $args);
ãã¡ã¤ã³ã®ãã§ãã¯ã¯ã¹ãããã
é¢ç½ãã®ã¯MogileFS::run_global_hookã§ãããã
ãã¡ãã¨hookãæãããä»æ§ã®ããã§ããååã®ã¨ã³ããªã§ã触ãã¾ãããMogileFSã£ã¦packageã®å®ä½ã¯mogilefsdã®ä¸ã«ããã¾ãã
MogileFS::run_global_hook
sub register_global_hook { $hooks{$_[0]} = $_[1]; return 1; } sub unregister_global_hook { delete $hooks{$_[0]}; return 1; } sub run_global_hook { my $hookname = shift; my $ref = $hooks{$hookname}; return $ref->(@_) if defined $ref; return undef; }
ã¨è¨ã訳ã§ãããã®é¢æ°ã%hooksã¨è¨ãããã·ã¥ã¸ã®ã¢ã¯ã»ãµã§ããã
å®ä½ã¯ãã£ã¨å¥ã®å ´æã«ããã¨ã
serverã®trunkã§ackã§æ¤ç´¢ã
$ ack --perl register_global_hook mogilefsd 783:sub register_global_hook { 788:sub unregister_global_hook { lib/MogileFS/Plugin/FilePaths.pm 23: MogileFS::register_global_hook( 'cmd_create_open', sub { 30: MogileFS::register_global_hook( 'cmd_create_close', sub { 41: MogileFS::register_global_hook( 'file_stored', sub { 74: MogileFS::register_global_hook( 'cmd_get_paths', \&_path_to_key ); 75: MogileFS::register_global_hook( 'cmd_delete', \&_path_to_key ); 140: MogileFS::unregister_global_hook( 'cmd_create_open' ); 141: MogileFS::unregister_global_hook( 'cmd_create_close' ); 142: MogileFS::unregister_global_hook( 'file_stored' );
ã¨è¨ã訳ã§MogileFS::Plugin::FilePathsã§è¨å®ããã¦ãã¿ããã§ããã
ä½ãåã³ack使ã£ã¦æ¤ç´¢ãã¦ã¿ãã¨ã
$ ack --perl "Plugin" lib/MogileFS/Config.pm 169: my $rv = eval "use MogileFS::Plugin::$plugin; MogileFS::Plugin::$plugin->load; 1;"; lib/MogileFS/Plugin/FilePaths.pm 8:package MogileFS::Plugin::FilePaths; 53: my $parentnodeid = MogileFS::Plugin::FilePaths::vivify_path( $args->{dmid}, $path ); 57: my $oldfid = MogileFS::Plugin::FilePaths::get_file_mapping( $args->{dmid}, $parentnodeid, $filename ); 65: my $nodeid = MogileFS::Plugin::FilePaths::set_file_mapping( $args->{dmid}, $parentnodeid, $filename, $args->{fid} ); 94: my $nodeid = MogileFS::Plugin::FilePaths::load_path( $dmid, $path ); 101: my @files = MogileFS::Plugin::FilePaths::list_directory( $nodeid ); 256: my $parentnodeid = MogileFS::Plugin::FilePaths::load_path( $args->{dmid}, $path ); 260: my $fid = MogileFS::Plugin::FilePaths::get_file_mapping( $args->{dmid}, $parentnodeid, $filename );
ã¨è¨ã訳ã§MogileFS::Configã§å¼ã³åºãã¦ã¾ããã
å¾ã£ã¦Pluginã§è¨å®ããã¦ããhookã¯å
¨ã¦è¨å®ããã¦ã¾ãã*1
ã¡ãªã¿ã«loadã¡ã½ããã§è¨å®ã§ãã¾ãããèªåã§å¥½ããªhookãè¨è¿°ããäºãåºæ¥ãã§ãããã
MogileFS::Worker::Query->cmd_create_open (2)
ã§ã¾ãcmd_create_openã®ç¶ãã
# validate parameters my $dmid = $args->{dmid}; my $key = $args->{key} || ""; my $multi = $args->{multi_dest} ? 1 : 0; my $fid = ($args->{fid} + 0) || undef; # we want it to be undef if they didn't give one # and we want to force it numeric... # get DB handle my $dbh = Mgd::get_dbh() or return $self->err_line("nodb");
ããã§éè¦ãªã®ã¯multi_destãªãã·ã§ã³ããããï¼
create_openã ã¨å¼·å¶çã«trueã§ãã
# figure out what classid this file is for my $class = $args->{class} || ""; my $classid = 0; if (length($class)) { # TODO: cache this $classid = $dbh->selectrow_array("SELECT classid FROM class ". "WHERE dmid=? AND classname=?", undef, $dmid, $class) or return $self->err_line("unreg_class"); }
classã®åå¨ãã§ãã¯ã§ããã
# if we haven't heard from the monitoring job yet, we need to chill a bit # to prevent a race where we tell a user that we can't create a file when # in fact we've just not heard from the monitor while (! $self->monitor_has_run) { $self->read_from_parent; $self->still_alive; sleep 1; }
monitorã®workerã®çåãã§ãã¯ã*2
ãããããéè¦ã ã¨æãããã
# find a device to put this file on that has 100Mb free. my (@dests, @hosts); my $devs = Mgd::get_device_summary(); while (scalar(@dests) < ($multi ? 3 : 1)) { my $devid = Mgd::find_deviceid( random => 1, must_be_writeable => 1, weight_by_free => 1, not_on_hosts => \@hosts, ); last unless defined $devid; push @dests, $devid; push @hosts, $devs->{$devid}->{hostid}; } return $self->err_line("no_devices") unless @dests;
trackerã®æ°åã³multiãtrueãã©ããã§ããããwhileã«ã¼ãã®æ¡ä»¶ãç°ãªãã¾ãã
$devidãåããéã¯lastã§æããªãäºãããã©ã³ãã ã§deviceãåã£ã¦ããã£ã¦äºã§ããã
ããã«$devidã@destsã«è¿½å ãã¦ããäºãããã«ã¼ãã®æ¡ä»¶ã«é¢ä¿ãã¦ãã¾ãã
$multiãfalseãªå ´åæåã®ã«ã¼ããå®è¡ãããå¾ã«whileã®statementã§falseã«ãªãã®ã§å¦çãæãã¾ããã
ãã£ã¦@dests, @hostsã¯è¦ç´ æ°1ã®é
åã
$multiãtrueã®å ´åã¯ãããã¤ã¹ãæ大3ã¤åãåºãã¾ã§ã©ã³ãã ã§æ½åºãã¾ãã
my $explicit_fid_used = $fid ? 1 : 0; # setup the new mapping. we store the devices that we picked for # this file in here, knowing that they might not be used. create_close # is responsible for actually mapping in file_on. NOTE: fid is being # passed in, it's either some number they gave us, or it's going to be # undef which translates into NULL which means to automatically create # one. that should be fine. my $ins_tempfile = sub { $dbh->do("INSERT INTO tempfile SET ". " fid=?, dmid=?, dkey=?, classid=?, createtime=UNIX_TIMESTAMP(), devids=?", undef, $fid, $dmid, $key, $classid, join(',', @dests)); return undef if $dbh->err; unless (defined $fid) { # if they did not give us a fid, then we want to grab the one that was # theoretically automatically generated $fid = $dbh->{mysql_insertid}; # FIXME: mysql-ism } return undef unless defined $fid && $fid > 0; return 1; }; return undef unless $ins_tempfile->();
ãã³ãã©ãªãã¡ã¤ã«ã¨è¨ãç©ãDBä¸ã«ã»ãããã¾ãã*3
my $fid_in_use = sub { my $exists = $dbh->selectrow_array("SELECT COUNT(*) FROM file WHERE fid=?", undef, $fid); die if $dbh->err; return $exists ? 1 : 0; }; # if the fid is in use, do something while ($fid_in_use->($fid)) { return $self->err_line("fid_in_use") if $explicit_fid_used; # mysql could have been restarted with an empty tempfile table, causing # innodb to reuse a fid number. so we need to seed the tempfile table... # get the highest fid from the filetable and insert a dummy row $fid = $dbh->selectrow_array("SELECT MAX(fid) FROM file"); $ins_tempfile->(); # then do a normal auto-increment $fid = undef; return undef unless $ins_tempfile->(); }
fidã被ããªãããã«ãã³ãã©ãªãã¡ã¤ã«ãä½ãã£ã¦ã¨ãã§ããã
# make sure directories exist for client to be able to PUT into foreach my $devid (@dests) { my $path = Mgd::make_path($devid, $fid); Mgd::vivify_directories($path); }
ãããåãéè¦ï¼
Mgdãååã®ã¨ã³ããªã§è¿°ã¹ãéããmogilefsdã«å«ã¾ãã¾ãã
Mgd::make_path
sub make_path { return Mgd::make_full_url(@_); }
ã¯ãã次ã
Mgd::make_full_url
sub make_full_url { # set use_get_port to be true to specify to use the get port my ($devid, $fid, $use_get_port) = @_; # get some information we'll need my $devs = Mgd::get_device_summary(); my $dev = $devs->{$devid} or return undef; my $path = Mgd::make_http_path($devid, $fid) or return undef; my $host = Mgd::hostid_ip($dev->{hostid}) or return undef; my $port = $use_get_port ? Mgd::hostid_http_get_port($dev->{hostid}) : undef; $port ||= Mgd::hostid_http_port($dev->{hostid}) or return undef; return "http://$host:$port$path"; }
ç´°ããäºã¯æãã«ãã¦ãçå±ä¸é
ç½®ãããWebDAVãªã½ã¼ã¹ã®å ´æã
ã¶ã£ã¡ããURLãè¿ã£ã¦ãã¾ãã
ãã®loopã§ã¯ããã«Mgd::vivify_directoriesã£ã¦ã¡ã½ãããå¼ã³åºãã¦ã¾ãã
Mgd::vivify_directories()
é·ããã©ãã³ã¡ã³ããåãããããã®ã§ãèªã¿ãããã
sub vivify_directories { my $path = shift; # $path is something like: # http://10.0.0.26:7500/dev2/0/000/148/0000148056.fid # three directories we'll want to make: # http://10.0.0.26:7500/dev2/0 # http://10.0.0.26:7500/dev2/0/000 # http://10.0.0.26:7500/dev2/0/000/148
ã¤ã¾ãå帰çã«æå®ããããã¤ã¹ã«ãã£ã¬ã¯ããªä½ã£ã¦ãããã£ã¦äºã§ããã
foreach my $path (@need) { $depth++; next if $dir_made{$path}; my $sock = IO::Socket::INET->new(PeerAddr => $peer, Timeout => 1) or next; print $sock "MKCOL $path HTTP/1.0\r\n". "Content-Length: 0\r\n\r\n"; my $ans = <$sock>; $dir_made{$path} = [$depth, $now]; }
ã¡ãªã¿ã«MKCOLã£ã¦ã¡ã½ããã§ä½ã模æ§ã*4
ã¨è¨ã訳ã§å
ã®foreachã¯1ã3åã®@destsã«å¯¾ãã¦deviceã®IDã
WebDAVã®URLãçªã£è¾¼ãã§ããã¦ã
# original single path support return $self->ok_line({ fid => $fid, devid => $dests[0], path => Mgd::make_path($dests[0], $fid), }) unless $multi;
æå¾ã«ã¬ã¹ãã³ã¹ãclientã«è¿ãã¾ãã*5
ä½ãããã¯åä¸æå®ãããå ´åãmulti_destã0ã ã¨ãã®ããã«ãªãã¾ãã*6
# multiple path support my $ct = 0; my $res = {}; foreach my $devid (@dests) { $ct++; $res->{"devid_$ct"} = $devid; $res->{"path_$ct"} = Mgd::make_path($devid, $fid); } $res->{fid} = $fid; $res->{dev_count} = $ct; return $self->ok_line($res); }
ããã¾ã§ã§è¨ãã°create_openã³ãã³ãã¯ãããããã¡ã¤ã«ãçªã£è¾¼ããã£ã¦è¨ã宣è¨ã¨ããã®ä¸æºåã§å®ã¯storage nodeã«ã¯ãã£ã¬ã¯ããªãä½æããã®ã¿ã§ãä»ã«ã¯ä½ããã£ã¦ããªãããã§ããã
ããã§ã¯multiãtrueã§å¤ãã¦3ã¤ã®ããã¤ã¹ãã©ã³ãã ã§æ½åºããã¦åºã¦ãã¾ããã©ããã®éã«deviceã«ã¯strageã®hostãå½ç¶å«ã¾ãã¾ãã®ã§ãmogadmã§è¨å®ããhostã®docroot/device以ä¸ã«ãã£ã¬ã¯ããªãåºæ¥ã¦ããäºã«ãªãã¾ãã
MogileFS::Client->new_file
create_openã³ãã³ãå¾ã§ãã
my $dests = []; # [ [devid,path], [devid,path], ... ] # determine old vs. new format to populate destinations unless (exists $res->{dev_count}) { push @$dests, [ $res->{devid}, $res->{path} ]; } else { for my $i (1..$res->{dev_count}) { push @$dests, [ $res->{"devid_$i"}, $res->{"path_$i"} ]; } }
multiãtrueã®æã«query workerã§ã¯è¤æ°ã®deviceãæããé¸æã§ãã¦ããã¯ãã§ãã®ã«ã¦ã³ã¿ãã¬ã¹ãã³ã¹ã«å«ã¾ããã®ã§ãæããelse, forã¨ç¶ãã¾ãã
ãããã«ããä¿åå ã$destsã«ç»é²ãã¾ãã
my $main_dest = shift @$dests; my ($main_devid, $main_path) = ($main_dest->[0], $main_dest->[1]); # create a MogileFS::NewHTTPFile object, based off of IO::File unless ($main_path =~ m!^http://!) { Carp::croak("This version of MogileFS::Client no longer supports non-http storage URLs.\n"); } return IO::WrapTie::wraptie('MogileFS::NewHTTPFile', mg => $self, fid => $res->{fid}, path => $main_path, devid => $main_devid, backup_dests => $dests, class => $class, key => $key, content_length => $bytes+0, );
ä¿åå
ã®ãã¡æ½åºããå
é ã代表ã¨ãã¦MogileFS::NewHTTPFileã¢ã¸ã¥ã¼ã«ãIO::WrapTieã§tieãã¦IOãã³ãã«ãè¿ãã¾ãã
ã¤ã¾ãIOãã³ãã«ã¨ãã¦ä½¿ãããã ãã©ãä¸ã§ã©ããªãã¸ãã¯ä½¿ã£ã¦ããã¯MogileFS::NewHTTPFileã«ä¾åã£ã¦è¨³ã§ãã
ç¶ãã¯ã¾ã次åï¼
*1:ãããè¦è½ã¨ãã¦ããã§è¨æ£ãã¾ããã
*2:å¤åæ»ãã§ãã復帰ãããã¨æããããæªç¢ºèª
*3:ãã¡ã¤ã«ã®å®ä½ã§ã¯ãªãã¦ãããããã¡ã¤ã«çªã£è¾¼ããã£ã¦å®£è¨ã¿ãããªããã ã¨æãããã
*4:ã¾ãããhttpã®ä¸è¬çãªã¡ã½ããï¼
*5:ok_lineã¡ã½ãã
*6:ã¡ãªã¿ã«create_openã³ãã³ãã¯å¼·å¶çã«true