DBIx-BatchChunker

 view release on metacpan or  search on metacpan

t/dbic.t  view on Meta::CPAN

            if ($ls->chunk_size < $min_size) {
                $min_size = $ls->chunk_size;
                $chunk_size_changes++;
            }

            $max_end  = max($max_end, $ls->end);
            $min_time = $ls->prev_runtime if $ls->prev_runtime && $ls->prev_runtime < $min_time;

            note explain $ls if $BATCHCHUNK_TEST_DEBUG;
        },

        min_chunk_percent => 0,
        debug             => 0,
    );

    # Calculate
    ok($batch_chunker->calculate_ranges, 'calculate_ranges ok');
    ok(defined $batch_chunker->min_id,   'min_id ok');
    ok(defined $batch_chunker->max_id,   'max_id ok');

    my $range = $batch_chunker->max_id - $batch_chunker->min_id + 1;
    my $multiplier_range = ceil($range / $CHUNK_SIZE);
    my $right_calls      = $range - $CHUNK_SIZE + 1;

    $batch_chunker->execute;
    cmp_ok($calls,    '>',  $multiplier_range,      'Greater coderef calls than normal');
    cmp_ok($calls,    '==', $right_calls,           'Right coderef calls');
    cmp_ok($max_end,  '==', $batch_chunker->max_id, 'Final chunk ends at max_id');
    cmp_ok($min_time, '>',  0.05,                   'Always exceeded target time');
    cmp_ok($min_size, '==', 1,                      'Right chunk size');
};

subtest 'Chunk resizing + runtime targeting (slow/different count_rs)' => sub {
    my ($max_end, $max_range) = (0, 0);
    my $min_size = $CHUNK_SIZE;

    my $count_rs = $schema->resultset('Track')->search({ position => { -in => [1, 2] } });
    my $batch_chunker = DBIx::BatchChunker->new(
        chunk_size  => $CHUNK_SIZE,
        target_time => 0.10,

        count_rs    => $count_rs,
        rs          => $track_rs,
        coderef     => sub {
            my ($bc, $rs) = @_;
            isa_ok($rs, ['DBIx::Class::ResultSet'], '$rs');

            my $ls    = $bc->loop_state;
            my $range = $ls->end - $ls->start + 1;

            $max_end   = max($max_end, $ls->end);
            $max_range = max($max_range, $range);

            note explain $ls if $BATCHCHUNK_TEST_DEBUG;
        },

        min_chunk_percent => 0.01,
        verbose           => 0,
    );

    # DBI callbacks here are the only good way to interfere with COUNT statements
    my $dbh = $batch_chunker->dbic_storage->dbh;
    $dbh->{Callbacks} = {
        ChildCallbacks => {
            execute => sub {
                my ($sth, $start, $end) = @_;
                if ($sth->{Statement} =~ /COUNT/) {
                    my $ls    = $batch_chunker->loop_state || return;
                    my $range = $ls->end - $ls->start + 1;

                    # maximum of $CHUNK_SIZE-1 IDs here
                    sleep min(0.20, 0.08 / ($CHUNK_SIZE - 1) * $range);

                    # We want to test the count_rs / COUNT timing, but not the main runtime targeting.
                    # So, put back any chunk_size changes back to their original value.
                    $ls->chunk_size($CHUNK_SIZE);

                    note explain $ls if $BATCHCHUNK_TEST_DEBUG;
                }
                return;    # DBI callback cannot return anything
            },
        }
    };

    # Calculate
    ok($batch_chunker->calculate_ranges, 'calculate_ranges ok');
    ok(defined $batch_chunker->min_id,   'min_id ok');
    ok(defined $batch_chunker->max_id,   'max_id ok');

    $batch_chunker->execute;
    cmp_ok($max_end,   '==', $batch_chunker->max_id, 'Final chunk ends at max_id');
    cmp_ok($max_range, '==', $CHUNK_SIZE - 1,        'Right max ID range');

    # Remove the callback completely
    delete $dbh->{Callbacks}{ChildCallbacks}{execute};
    delete $dbh->{Callbacks}{ChildCallbacks};
    delete $dbh->{Callbacks};
};

subtest 'Retry testing' => sub {
    my ($calls, $max_end) = (0, 0);

    # Constructor
    my $batch_chunker = DBIx::BatchChunker->new(
        chunk_size => $CHUNK_SIZE,

        rs          => $track_rs,
        coderef     => sub {
            my ($bc, $rs) = @_;
            isa_ok($rs, ['DBIx::Class::ResultSet'], '$rs');
            $calls++;

            my $ls = $bc->loop_state;
            $max_end = max($max_end, $ls->end);

            note explain $ls if $BATCHCHUNK_TEST_DEBUG;
            die "Don't wanna process right now" if $calls % 3;  # fail 2/3rds of the calls
        },

        dbic_retry_opts   => {},  # non-DBIC "defaults"
        min_chunk_percent => 0,



( run in 3.439 seconds using v1.01-cache-2.11-cpan-5a3173703d6 )