int ra_state::unit_test()

in turbonfs/src/readahead.cpp [704:904]


int ra_state::unit_test()
{
    ra_state ras{128 * 1024, 4 * 1024};
    int64_t next_ra;
    int64_t next_read;
    int64_t complete_ra;

    AZLogInfo("Unit testing ra_state, start");

    // 1st read.
    next_read = 0*_MiB;
    ras.on_application_read(next_read, 1*_MiB);

    // Only 1 read complete, cannot confirm sequential pattern till 3 reads.
    assert(ras.get_next_ra(4*_MiB) == 0);

    next_read += 1*_MiB;
    ras.on_application_read(next_read, 1*_MiB);

    // Only 2 reads complete, cannot confirm sequential pattern till 3 reads.
    assert(ras.get_next_ra(4*_MiB) == 0);

    next_read += 1*_MiB;
    ras.on_application_read(next_read, 1*_MiB);

    /*
     * Ok 3 reads complete, all were sequential, so now we should get a
     * readahead recommendation.
     */
    next_ra = 3*_MiB;
    assert(ras.get_next_ra(4*_MiB) == next_ra);

    /*
     * Since we have 128MB ra window, next 31 (+1 above) get_next_ra() calls
     * will recommend readahead.
     */
    for (int i = 0; i < 31; i++) {
        next_ra += 4*_MiB;
        /*
         * We don't pass the length parameter to get_next_ra(), it should
         * use the default ra size set in the constructor. We set that to
         * 4MiB.
         */
        assert(ras.get_next_ra() == next_ra);
    }

    // No more readahead reads after full ra window is issued.
    assert(ras.get_next_ra(4*_MiB) == 0);

    /*
     * Complete one readahead.
     * We don't pass the length parameter to on_readahead_complete(), it should
     * use the default ra size set in the constructor. We set that to 4MiB.
     */
    complete_ra = 3*_MiB;
    ras.on_readahead_complete(complete_ra);

    // One more readahead should be allowed.
    next_ra += 4*_MiB;
    assert(ras.get_next_ra(4*_MiB) == next_ra);

    // Not any more.
    assert(ras.get_next_ra(4*_MiB) == 0);

    // Complete all readahead reads.
    for (int i = 0; i < 32; i++) {
        complete_ra += 4*_MiB;
        ras.on_readahead_complete(complete_ra, 4*_MiB);
    }

    // Now it should recommend next readahead.
    next_ra += 4*_MiB;
    assert(ras.get_next_ra(4*_MiB) == next_ra);

    // Complete that one too.
    complete_ra = next_ra;
    ras.on_readahead_complete(complete_ra, 4*_MiB);

    /*
     * Now issue next read at 100MB offset.
     * This will cause access density to drop since now we have a gap of
     * 97MiB and we have just read 4MiB till now.
     */
    ras.on_application_read(100*_MiB, 1*_MiB);
    assert(ras.get_next_ra(4*_MiB) == 0);

    /*
     * Read the entire gap.
     * This will fill the gap and get the access density back to 100%, so
     * now it should recommend readahead.
     */
    for (int i = 0; i < 97; i++) {
        next_read += 1*_MiB;
        ras.on_application_read(next_read, 1*_MiB);
    }

    /*
     * Readahead recommended should be after the last byte read or the last
     * readahead byte, whichever is larger. In this case next readahead is
     * larger.
     */
    next_ra += 4*_MiB;
    assert(next_ra > 101*_MiB);
    assert(ras.get_next_ra(4*_MiB) == next_ra);

    /*
     * Read from a new section.
     * This should reset the pattern detector and it should not recommend a
     * readahead, till it again confirms a sequential pattern.
     */
    next_read = 2*_GiB;
    ras.on_application_read(next_read, 1*_MiB);
    assert(ras.get_next_ra(4*_MiB) == 0);

    // 2nd read in the new section.
    next_read += 1*_MiB;
    ras.on_application_read(next_read, 1*_MiB);
    assert(ras.get_next_ra(4*_MiB) == 0);

    // 3rd read in the new section.
    next_read += 1*_MiB;
    ras.on_application_read(next_read, 1*_MiB);

    next_ra = next_read + 1*_MiB;
    assert(ras.get_next_ra(4*_MiB) == next_ra);

    /*
     * Read from the next section. We will only do random reads so pattern
     * detector should not see a seq pattern and must not recommend readahead.
     */
    next_read = 4*_GiB;
    ras.on_application_read(next_read, 1*_MiB);
    assert(ras.get_next_ra(4*_MiB) == 0);

    for (int i = 0; i < 1000; i++) {
        next_read = random_number(0, 1*_TiB);
        ras.on_application_read(next_read, 1*_MiB);
        assert(ras.get_next_ra(4*_MiB) == 0);

        next_read = random_number(1*_TiB, 2*_TiB);
        ras.on_application_read(next_read, 1*_MiB);
        assert(ras.get_next_ra(4*_MiB) == 0);
    }

    /*
     * Jump to a new section.
     * Here we will only do sequential reads. After 3 sequential reads, we
     * should detect the pattern and after that we should recommend readahead.
     */
    next_read = 10*_GiB;
    ras.on_application_read(next_read, 1*_MiB);
    assert(ras.get_next_ra(4*_MiB) == 0);

    next_read += 1*_MiB;
    ras.on_application_read(next_read, 1*_MiB);
    assert(ras.get_next_ra(4*_MiB) == 0);

    next_read += 1*_MiB;
    ras.on_application_read(next_read, 1*_MiB);

    next_ra = next_read+1*_MiB;
    assert(ras.get_next_ra(4*_MiB) == next_ra);

    for (int i = 0; i < 2000; i++) {
        next_read += 1*_MiB;
        ras.on_application_read(next_read, 1*_MiB);

        next_ra += 4*_MiB;
        assert(ras.get_next_ra(4*_MiB) == next_ra);

        complete_ra = next_ra;
        ras.on_readahead_complete(complete_ra, 4*_MiB);
    }

    // Stress run.
    for (int i = 0; i < 10'000'000; i++) {
        next_read = random_number(0, 1*_TiB);
        ras.on_application_read(next_read, 1*_MiB);
        assert(!ras.is_sequential());
        assert(ras.get_next_ra(4*_MiB) == 0);

        next_read = random_number(1*_TiB, 2*_TiB);
        ras.on_application_read(next_read, 1*_MiB);
        assert(!ras.is_sequential());
        assert(ras.get_next_ra(4*_MiB) == 0);

        next_read = random_number(2*_TiB, 3*_TiB);
        ras.on_application_read(next_read, 1*_MiB);
        assert(!ras.is_sequential());
        assert(ras.get_next_ra(4*_MiB) == 0);

        next_read = random_number(3*_TiB, 4*_TiB);
        ras.on_application_read(next_read, 1*_MiB);
        assert(!ras.is_sequential());
        assert(ras.get_next_ra(4*_MiB) == 0);
    }

    AZLogInfo("Unit testing ra_state, done!");

    return 0;
}