185 lines
4.5 KiB
Perl
Executable file
185 lines
4.5 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
#
|
|
# bin/upgrading/migrate-mogilefs.pl
|
|
#
|
|
# Move files out of a MogileFS cluster and into the current BlobStore
|
|
# primary storage.
|
|
#
|
|
# Authors:
|
|
# Mark Smith <mark@dreamwidth.org>
|
|
#
|
|
# Copyright (c) 2017 by Dreamwidth Studios, LLC.
|
|
#
|
|
# This program is free software; you may redistribute it and/or modify it under
|
|
# the same terms as Perl itself. For a copy of the license, please reference
|
|
# 'perldoc perlartistic' or 'perldoc perlgpl'.
|
|
#
|
|
|
|
use v5.10;
|
|
use strict;
|
|
BEGIN { require "$ENV{LJHOME}/cgi-bin/ljlib.pl"; }
|
|
|
|
use Carp qw/ croak /;
|
|
use DBI;
|
|
use Getopt::Long;
|
|
use MogileFS::Client;
|
|
|
|
use DW::BlobStore;
|
|
|
|
use constant BLOCK_SIZE => 1_000;
|
|
|
|
my ( $startfid, $endfid, $max_workers, $conf );
|
|
GetOptions(
|
|
'start-fid=i' => \$startfid,
|
|
'end-fid=i' => \$endfid,
|
|
'num-workers=i' => \$max_workers,
|
|
'mogilefs-config=s' => \$conf,
|
|
);
|
|
$startfid ||= 0;
|
|
$endfid ||= 1_000_000_000;
|
|
$max_workers ||= 10;
|
|
|
|
die "Must provide valid --mogilefs-config=FILEPATH argument.\n"
|
|
unless $conf && -e $conf;
|
|
|
|
my $mogc = get_mogilefs_client();
|
|
|
|
my $queue = [];
|
|
my $cur_workers = 0;
|
|
my $num_workers = 0;
|
|
my $enqueued = 0;
|
|
while ( $startfid <= $endfid ) {
|
|
my $files = get_files( $startfid, $startfid + ( BLOCK_SIZE - 1 ) );
|
|
$startfid += BLOCK_SIZE;
|
|
next unless $files;
|
|
|
|
foreach my $fid ( keys %$files ) {
|
|
$enqueued++;
|
|
push @$queue, $files->{$fid};
|
|
next if scalar @$queue < BLOCK_SIZE;
|
|
|
|
$0 = sprintf( 'migrate-mogilefs: enqueued %d, workers %d', $enqueued, $num_workers );
|
|
|
|
if ( $cur_workers >= $max_workers ) {
|
|
wait;
|
|
$cur_workers--;
|
|
}
|
|
|
|
make_worker($queue);
|
|
|
|
$num_workers++;
|
|
$cur_workers++;
|
|
$queue = [];
|
|
}
|
|
}
|
|
|
|
if ( $queue && @$queue ) {
|
|
make_worker($queue);
|
|
$cur_workers++;
|
|
}
|
|
|
|
while ( $cur_workers > 0 ) {
|
|
wait;
|
|
$cur_workers--;
|
|
}
|
|
say "All done.";
|
|
|
|
sub make_worker {
|
|
my $queue = $_[0];
|
|
|
|
if ( my $pid = fork ) {
|
|
return;
|
|
}
|
|
|
|
my $pos = 0;
|
|
my $llen = scalar @$queue;
|
|
foreach my $file (@$queue) {
|
|
$pos++;
|
|
$0 = sprintf( 'migrate-mogilefs [%d/%d] = %0.2f%%', $pos, $llen, 100 * ( $pos / $llen ) );
|
|
|
|
# Quick check to make sure this file isn't already in BlobStore
|
|
if ( DW::BlobStore->exists( $file->{class} => $file->{key} ) ) {
|
|
say "$file->{fid}: OK EXISTS";
|
|
next;
|
|
}
|
|
|
|
my $data = $mogc->get_file_data( $file->{key} );
|
|
unless ($data) {
|
|
say "$file->{fid}: ERR NODATA";
|
|
next;
|
|
}
|
|
|
|
my $size = length $$data;
|
|
unless ( $size == $file->{size} ) {
|
|
say "$file->{fid}: ERR WRONGSIZE $size $file->{size}";
|
|
next;
|
|
}
|
|
|
|
# It's a file and the length is write, let's store it to BlobStore with the
|
|
# right data...
|
|
my $rv = DW::BlobStore->store( $file->{class} => $file->{key}, $data );
|
|
if ($rv) {
|
|
say "$file->{fid}: OK STORE";
|
|
}
|
|
else {
|
|
say "$file->{fid}: ERR STORE";
|
|
}
|
|
}
|
|
|
|
exit;
|
|
}
|
|
|
|
sub get_files {
|
|
my ( $start_fid, $end_fid ) = @_;
|
|
|
|
my $dbh = get_mogilefs_dbh();
|
|
|
|
my $rows = $dbh->selectall_arrayref(
|
|
q{SELECT f.fid, f.dkey, f.length, d.namespace, c.classname
|
|
FROM file f, domain d, class c
|
|
WHERE f.fid >= ? AND f.fid <= ? AND
|
|
d.dmid = f.dmid AND c.dmid = f.dmid AND c.classid = f.classid
|
|
},
|
|
undef, $start_fid, $end_fid,
|
|
);
|
|
return undef unless $rows && scalar @$rows;
|
|
|
|
my $out = {};
|
|
foreach my $row (@$rows) {
|
|
$out->{ $row->[0] } = {
|
|
fid => $row->[0],
|
|
key => $row->[1],
|
|
size => $row->[2],
|
|
domain => $row->[3],
|
|
class => $row->[4],
|
|
};
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
sub get_mogilefs_dbh {
|
|
my %config;
|
|
open FILE, "<$conf" or die;
|
|
foreach my $line (<FILE>) {
|
|
my ( $key, $val ) = ( $1, $2 )
|
|
if $line =~ /^db_(\w+)\s*=\s*(.+?)$/;
|
|
next unless $key && $val;
|
|
|
|
$config{$key} = $val;
|
|
}
|
|
close FILE;
|
|
|
|
my $dbh = DBI->connect( $config{dsn}, $config{user}, $config{pass} )
|
|
or die "Failed to connect to MogileFS.\n";
|
|
return $dbh;
|
|
}
|
|
|
|
sub get_mogilefs_client {
|
|
my $mogclient = MogileFS::Client->new(
|
|
domain => $LJ::MOGILEFS_CONFIG{domain},
|
|
root => $LJ::MOGILEFS_CONFIG{root},
|
|
hosts => $LJ::MOGILEFS_CONFIG{hosts},
|
|
timeout => $LJ::MOGILEFS_CONFIG{timeout},
|
|
) or die "Failed to create MogileFS::Client!\n";
|
|
return $mogclient;
|
|
}
|