CPAN-Testers-Backend/lib/CPAN/Testers/Backend/Migrate/MetabaseCache.pm
package CPAN::Testers::Backend::Migrate::MetabaseCache;
our $VERSION = '0.005';
# ABSTRACT: Migrate old metabase cache to new test report format
#pod =head1 SYNOPSIS
#pod
#pod beam run <container> <service> [--force | -f]
#pod
#pod =head1 DESCRIPTION
#pod
#pod This task migrates the reports in the C<metabase.metabase> table to the
#pod C<cpanstats.test_report> table. This will enable us to drop the C<metabase>
#pod database altogether.
#pod
#pod =cut
use CPAN::Testers::Backend::Base 'Runnable';
with 'Beam::Runnable';
use Getopt::Long qw( GetOptionsFromArray );
use Data::FlexSerializer;
use JSON::MaybeXS qw( encode_json );
use CPAN::Testers::Report;
use CPAN::Testers::Fact::TestSummary;
use CPAN::Testers::Fact::LegacyReport;
#pod =attr metabase_dbh
#pod
#pod The L<DBI> object connected to the C<metabase> database.
#pod
#pod =cut
has metabase_dbh => (
is => 'ro',
isa => InstanceOf['DBI::db'],
required => 1,
);
#pod =attr schema
#pod
#pod The L<CPAN::Testers::Schema> to write reports to.
#pod
#pod =cut
has schema => (
is => 'ro',
isa => InstanceOf['CPAN::Testers::Schema'],
required => 1,
);
sub run( $self, @args ) {
GetOptionsFromArray(
\@args, \my %opt,
'force|f',
);
if ( $opt{force} && !@args ) {
$LOG->info( '--force and no IDs specified: Re-processing all cache entries' );
my $sth = $self->find_entries;
$self->process_sth( $sth );
}
elsif ( @args ) {
$LOG->info( 'Re-processing ' . @args . ' cache entries from command-line' );
my $sth = $self->find_entries( @args );
$self->process_sth( $sth );
}
else {
$LOG->info( 'Processing all unprocessed cache entries' );
my $sth = $self->find_unprocessed_entries;
while ( my $count = $self->process_sth( $sth ) ) {
$sth = $self->find_unprocessed_entries;
}
}
return 0;
}
#pod =method process_sth
#pod
#pod Process the given statement handle full of reports. Returns the number
#pod of reports processed
#pod
#pod =cut
sub process_sth( $self, $sth ) {
my $rs = $self->schema->resultset( 'TestReport' );
my $count = 0;
while ( my $row = $sth->fetchrow_hashref ) {
my $fact = $self->parse_metabase_report( $row );
$rs->insert_metabase_fact( $fact );
$count++;
}
$LOG->info( 'Processed ' . $count . ' entries' );
return $count;
}
#pod =method find_unprocessed_entries
#pod
#pod $sth = $self->find_unprocessed_entries;
#pod
#pod Returns a L<DBI> statement handle on to a list of C<metabase.metabase>
#pod row hashrefs for reports that are not in the main test report table
#pod (managed by L<CPAN::Testers::Schema::Result::TestReport>).
#pod
#pod =cut
sub find_unprocessed_entries( $self ) {
my @ids;
my $i = 0;
my $page = 10000;
my $current_page = $self->metabase_dbh->selectcol_arrayref(
'SELECT guid FROM metabase LIMIT ' . $page . ' OFFSET ' . $i
);
while ( @$current_page > 0 && @ids < $page ) {
my %found = map {; $_ => 1 } $self->schema->resultset( 'TestReport' )->search( {
id => {
-in => $current_page,
}
} )->get_column( 'id' )->all;
push @ids, grep !$found{ $_ }, @$current_page;
$i += 1000;
$current_page = $self->metabase_dbh->selectcol_arrayref(
'SELECT guid FROM metabase LIMIT ' . $page . ' OFFSET ' . $i
);
}
die "No unprocessed reports" unless @ids;
$LOG->info( 'Found ' . (scalar @ids) . ' entries to process' );
return $self->find_entries( @ids );
}
#pod =method find_entries
#pod
#pod $sth = $self->find_entries;
#pod $sth = $self->find_entries( @ids );
#pod
#pod Find all the cache entries to be processed by this module, optionally
#pod limited only to the IDs passed-in. Returns a list of row hashrefs.
#pod
#pod =cut
sub find_entries( $self, @ids ) {
my ( $where, @values );
if ( @ids ) {
$where = " WHERE guid IN (" . join( ', ', ( '?' ) x @ids ) . ")";
@values = @ids;
}
my $sth = $self->metabase_dbh->prepare(
"SELECT * FROM metabase" . $where
);
$sth->execute( @values );
return $sth;
}
#pod =method parse_metabase_report
#pod
#pod This sub undoes the processing that CPAN Testers expects before it is
#pod put in the database so we can ensure that the report was submitted
#pod correctly.
#pod
#pod This code is stolen from CPAN::Testers::Data::Generator sub load_fact
#pod
#pod =cut
my $zipper = Data::FlexSerializer->new(
detect_compression => 1,
detect_sereal => 1,
detect_json => 1,
);
sub parse_metabase_report( $self, $row ) {
if ( $row->{fact} ) {
return $zipper->deserialize( $row->{fact} );
}
my $data = $zipper->deserialize( $row->{report} );
my $struct = {
metadata => {
core => {
$data->{'CPAN::Testers::Fact::TestSummary'}{metadata}{core}->%*,
guid => $row->{guid},
type => 'CPAN-Testers-Report',
},
},
content => encode_json( [
$data->{'CPAN::Testers::Fact::LegacyReport'},
$data->{'CPAN::Testers::Fact::TestSummary'},
] ),
};
#; use Data::Dumper;
#; warn Dumper $struct;
my $fact = CPAN::Testers::Report->from_struct( $struct );
return $fact;
}
1;
__END__
=pod
=head1 NAME
CPAN::Testers::Backend::Migrate::MetabaseCache - Migrate old metabase cache to new test report format
=head1 VERSION
version 0.005
=head1 SYNOPSIS
beam run <container> <service> [--force | -f]
=head1 DESCRIPTION
This task migrates the reports in the C<metabase.metabase> table to the
C<cpanstats.test_report> table. This will enable us to drop the C<metabase>
database altogether.
=head1 ATTRIBUTES
=head2 metabase_dbh
The L<DBI> object connected to the C<metabase> database.
=head2 schema
The L<CPAN::Testers::Schema> to write reports to.
=head1 METHODS
=head2 process_sth
Process the given statement handle full of reports. Returns the number
of reports processed
=head2 find_unprocessed_entries
$sth = $self->find_unprocessed_entries;
Returns a L<DBI> statement handle on to a list of C<metabase.metabase>
row hashrefs for reports that are not in the main test report table
(managed by L<CPAN::Testers::Schema::Result::TestReport>).
=head2 find_entries
$sth = $self->find_entries;
$sth = $self->find_entries( @ids );
Find all the cache entries to be processed by this module, optionally
limited only to the IDs passed-in. Returns a list of row hashrefs.
=head2 parse_metabase_report
This sub undoes the processing that CPAN Testers expects before it is
put in the database so we can ensure that the report was submitted
correctly.
This code is stolen from CPAN::Testers::Data::Generator sub load_fact
=head1 AUTHOR
Doug Bell <preaction@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2017 by Doug Bell.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut